aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-06-19 17:03:28 +0200
committerSven Gothel <[email protected]>2014-06-19 17:03:28 +0200
commitd9a584844a60542519d813b5dc1a62428f14a0ae (patch)
tree942c10a5ebcd0aab65e9d6facb59778468f39d3b /LibOVR/Src
Add OculusSDK 0.3.2 Linux Source Code w/o Samples, docs or binaries (libs or tools)
Diffstat (limited to 'LibOVR/Src')
-rw-r--r--LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp73
-rw-r--r--LibOVR/Src/CAPI/CAPI_DistortionRenderer.h118
-rw-r--r--LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp675
-rw-r--r--LibOVR/Src/CAPI/CAPI_FrameTimeManager.h264
-rw-r--r--LibOVR/Src/CAPI/CAPI_GlobalState.cpp142
-rw-r--r--LibOVR/Src/CAPI/CAPI_GlobalState.h84
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp143
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDRenderState.h93
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDState.cpp804
-rw-r--r--LibOVR/Src/CAPI/CAPI_HMDState.h347
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp784
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h178
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h326
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp530
-rw-r--r--LibOVR/Src/CAPI/GL/CAPI_GL_Util.h537
-rw-r--r--LibOVR/Src/Kernel/OVR_Alg.cpp57
-rw-r--r--LibOVR/Src/Kernel/OVR_Alg.h1060
-rw-r--r--LibOVR/Src/Kernel/OVR_Allocator.cpp95
-rw-r--r--LibOVR/Src/Kernel/OVR_Allocator.h347
-rw-r--r--LibOVR/Src/Kernel/OVR_Array.h833
-rw-r--r--LibOVR/Src/Kernel/OVR_Atomic.cpp162
-rw-r--r--LibOVR/Src/Kernel/OVR_Atomic.h890
-rw-r--r--LibOVR/Src/Kernel/OVR_Color.h66
-rw-r--r--LibOVR/Src/Kernel/OVR_ContainerAllocator.h267
-rw-r--r--LibOVR/Src/Kernel/OVR_Deque.h296
-rw-r--r--LibOVR/Src/Kernel/OVR_File.cpp582
-rw-r--r--LibOVR/Src/Kernel/OVR_File.h529
-rw-r--r--LibOVR/Src/Kernel/OVR_FileFILE.cpp595
-rw-r--r--LibOVR/Src/Kernel/OVR_Hash.h1302
-rw-r--r--LibOVR/Src/Kernel/OVR_KeyCodes.h251
-rw-r--r--LibOVR/Src/Kernel/OVR_List.h336
-rw-r--r--LibOVR/Src/Kernel/OVR_Lockless.cpp231
-rw-r--r--LibOVR/Src/Kernel/OVR_Lockless.h107
-rw-r--r--LibOVR/Src/Kernel/OVR_Log.cpp184
-rw-r--r--LibOVR/Src/Kernel/OVR_Log.h204
-rw-r--r--LibOVR/Src/Kernel/OVR_Math.cpp91
-rw-r--r--LibOVR/Src/Kernel/OVR_Math.h2526
-rw-r--r--LibOVR/Src/Kernel/OVR_RefCount.cpp111
-rw-r--r--LibOVR/Src/Kernel/OVR_RefCount.h564
-rw-r--r--LibOVR/Src/Kernel/OVR_Std.cpp1036
-rw-r--r--LibOVR/Src/Kernel/OVR_Std.h514
-rw-r--r--LibOVR/Src/Kernel/OVR_String.cpp768
-rw-r--r--LibOVR/Src/Kernel/OVR_String.h657
-rw-r--r--LibOVR/Src/Kernel/OVR_StringHash.h100
-rw-r--r--LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp53
-rw-r--r--LibOVR/Src/Kernel/OVR_String_PathUtil.cpp211
-rw-r--r--LibOVR/Src/Kernel/OVR_SysFile.cpp138
-rw-r--r--LibOVR/Src/Kernel/OVR_SysFile.h104
-rw-r--r--LibOVR/Src/Kernel/OVR_System.cpp81
-rw-r--r--LibOVR/Src/Kernel/OVR_System.h78
-rw-r--r--LibOVR/Src/Kernel/OVR_Threads.h407
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp787
-rw-r--r--LibOVR/Src/Kernel/OVR_Timer.cpp287
-rw-r--r--LibOVR/Src/Kernel/OVR_Timer.h88
-rw-r--r--LibOVR/Src/Kernel/OVR_Types.h474
-rw-r--r--LibOVR/Src/Kernel/OVR_UTF8Util.cpp556
-rw-r--r--LibOVR/Src/Kernel/OVR_UTF8Util.h99
-rw-r--r--LibOVR/Src/OVR_CAPI.cpp925
-rw-r--r--LibOVR/Src/OVR_CAPI.h790
-rw-r--r--LibOVR/Src/OVR_CAPI_GL.h73
-rw-r--r--LibOVR/Src/OVR_Common_HMDDevice.cpp384
-rw-r--r--LibOVR/Src/OVR_Device.h1135
-rw-r--r--LibOVR/Src/OVR_DeviceConstants.h142
-rw-r--r--LibOVR/Src/OVR_DeviceHandle.cpp185
-rw-r--r--LibOVR/Src/OVR_DeviceHandle.h108
-rw-r--r--LibOVR/Src/OVR_DeviceImpl.cpp794
-rw-r--r--LibOVR/Src/OVR_DeviceImpl.h428
-rw-r--r--LibOVR/Src/OVR_DeviceMessages.h273
-rw-r--r--LibOVR/Src/OVR_HIDDevice.h154
-rw-r--r--LibOVR/Src/OVR_HIDDeviceBase.h51
-rw-r--r--LibOVR/Src/OVR_HIDDeviceImpl.h201
-rw-r--r--LibOVR/Src/OVR_JSON.cpp1185
-rw-r--r--LibOVR/Src/OVR_JSON.h164
-rw-r--r--LibOVR/Src/OVR_LatencyTestImpl.cpp773
-rw-r--r--LibOVR/Src/OVR_LatencyTestImpl.h144
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.cpp331
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.h122
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.cpp819
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.h135
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.cpp291
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.h154
-rw-r--r--LibOVR/Src/OVR_Linux_SensorDevice.cpp57
-rw-r--r--LibOVR/Src/OVR_Profile.cpp1517
-rw-r--r--LibOVR/Src/OVR_Profile.h203
-rw-r--r--LibOVR/Src/OVR_Recording.cpp38
-rw-r--r--LibOVR/Src/OVR_Recording.h83
-rw-r--r--LibOVR/Src/OVR_Sensor2Impl.cpp1124
-rw-r--r--LibOVR/Src/OVR_Sensor2Impl.h153
-rw-r--r--LibOVR/Src/OVR_Sensor2ImplUtil.h676
-rw-r--r--LibOVR/Src/OVR_SensorCalibration.cpp354
-rw-r--r--LibOVR/Src/OVR_SensorCalibration.h82
-rw-r--r--LibOVR/Src/OVR_SensorFilter.cpp99
-rw-r--r--LibOVR/Src/OVR_SensorFilter.h307
-rw-r--r--LibOVR/Src/OVR_SensorFusion.cpp904
-rw-r--r--LibOVR/Src/OVR_SensorFusion.h582
-rw-r--r--LibOVR/Src/OVR_SensorFusionDebug.h82
-rw-r--r--LibOVR/Src/OVR_SensorImpl.cpp1165
-rw-r--r--LibOVR/Src/OVR_SensorImpl.h316
-rw-r--r--LibOVR/Src/OVR_SensorImpl_Common.cpp245
-rw-r--r--LibOVR/Src/OVR_SensorImpl_Common.h150
-rw-r--r--LibOVR/Src/OVR_SensorTimeFilter.cpp385
-rw-r--r--LibOVR/Src/OVR_SensorTimeFilter.h226
-rw-r--r--LibOVR/Src/OVR_Stereo.cpp1805
-rw-r--r--LibOVR/Src/OVR_Stereo.h460
-rw-r--r--LibOVR/Src/OVR_ThreadCommandQueue.cpp380
-rw-r--r--LibOVR/Src/OVR_ThreadCommandQueue.h319
-rw-r--r--LibOVR/Src/Util/Util_ImageWindow.cpp511
-rw-r--r--LibOVR/Src/Util/Util_ImageWindow.h200
-rw-r--r--LibOVR/Src/Util/Util_Interface.cpp34
-rw-r--r--LibOVR/Src/Util/Util_Interface.h37
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest.cpp570
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest.h173
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest2.cpp191
-rw-r--r--LibOVR/Src/Util/Util_LatencyTest2.h238
-rw-r--r--LibOVR/Src/Util/Util_Render_Stereo.cpp1472
-rw-r--r--LibOVR/Src/Util/Util_Render_Stereo.h498
116 files changed, 48619 insertions, 0 deletions
diff --git a/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp
new file mode 100644
index 0000000..613d8fb
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp
@@ -0,0 +1,73 @@
+/************************************************************************************
+
+Filename : CAPI_DistortionRenderer.cpp
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+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 "CAPI_DistortionRenderer.h"
+
+#if defined (OVR_OS_WIN32)
+
+// TBD: Move to separate config file that handles back-ends.
+#define OVR_D3D_VERSION 11
+#include "D3D1X/CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#define OVR_D3D_VERSION 10
+#include "D3D1X/CAPI_D3D1X_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#define OVR_D3D_VERSION 9
+#include "D3D1X/CAPI_D3D9_DistortionRenderer.h"
+#undef OVR_D3D_VERSION
+
+#endif
+
+#include "GL/CAPI_GL_DistortionRenderer.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** DistortionRenderer
+
+// TBD: Move to separate config file that handles back-ends.
+
+DistortionRenderer::CreateFunc DistortionRenderer::APICreateRegistry[ovrRenderAPI_Count] =
+{
+ 0, // None
+ &GL::DistortionRenderer::Create,
+ 0, // Android_GLES
+#if defined (OVR_OS_WIN32)
+ &D3D9::DistortionRenderer::Create,
+ &D3D10::DistortionRenderer::Create,
+ &D3D11::DistortionRenderer::Create
+#else
+ 0,
+ 0,
+ 0
+#endif
+};
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h
new file mode 100644
index 0000000..a256bc6
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h
@@ -0,0 +1,118 @@
+/************************************************************************************
+
+Filename : CAPI_DistortionRenderer.h
+Content : Abstract interface for platform-specific rendering of distortion
+Created : February 2, 2014
+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.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_DistortionRenderer_h
+#define OVR_CAPI_DistortionRenderer_h
+
+#include "CAPI_HMDRenderState.h"
+#include "CAPI_FrameTimeManager.h"
+
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** CAPI::DistortionRenderer
+
+// DistortionRenderer implements rendering of distortion and other overlay elements
+// in platform-independent way.
+// Platform-specific renderer back ends for CAPI are derived from this class.
+
+class DistortionRenderer : public RefCountBase<DistortionRenderer>
+{
+ // Quiet assignment compiler warning.
+ void operator = (const DistortionRenderer&) { }
+public:
+
+ DistortionRenderer(ovrRenderAPIType api, ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : RenderAPI(api), HMD(hmd), TimeManager(timeManager), RState(renderState)
+ { }
+ virtual ~DistortionRenderer()
+ { }
+
+
+ // Configures the Renderer based on externally passed API settings. Must be
+ // called before use.
+ // Under D3D, apiConfig includes D3D Device pointer, back buffer and other
+ // needed structures.
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned distortionCaps) = 0;
+
+ // Submits one eye texture for rendering. This is in the separate method to
+ // allow "submit as you render" scenarios on horizontal screens where one
+ // eye can be scanned out before the other.
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture) = 0;
+
+ // Finish the frame, optionally swapping buffers.
+ // Many implementations may actually apply the distortion here.
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor,
+ unsigned char* latencyTester2DrawColor) = 0;
+
+ // Stores the current graphics pipeline state so it can be restored later.
+ void SaveGraphicsState() { if (!(RState.EnabledHmdCaps & ovrHmdCap_NoRestore)) GfxState->Save(); }
+
+ // Restores the saved graphics pipeline state.
+ void RestoreGraphicsState() { if (!(RState.EnabledHmdCaps & ovrHmdCap_NoRestore)) GfxState->Restore(); }
+
+ // *** Creation Factory logic
+
+ ovrRenderAPIType GetRenderAPI() const { return RenderAPI; }
+
+ // Creation function for this interface, registered for API.
+ typedef DistortionRenderer* (*CreateFunc)(ovrHmd hmd,
+ FrameTimeManager &timeManager,
+ const HMDRenderState& renderState);
+
+ static CreateFunc APICreateRegistry[ovrRenderAPI_Count];
+
+protected:
+
+ class GraphicsState : public RefCountBase<GraphicsState>
+ {
+ public:
+ GraphicsState() : IsValid(false) {}
+ virtual ~GraphicsState() {}
+ virtual void Save() = 0;
+ virtual void Restore() = 0;
+
+ protected:
+ bool IsValid;
+ };
+
+ const ovrRenderAPIType RenderAPI;
+ const ovrHmd HMD;
+ FrameTimeManager& TimeManager;
+ const HMDRenderState& RState;
+ Ptr<GraphicsState> GfxState;
+};
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_DistortionRenderer_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
new file mode 100644
index 0000000..3e776c3
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp
@@ -0,0 +1,675 @@
+/************************************************************************************
+
+Filename : CAPI_FrameTimeManager.cpp
+Content : Manage frame timing and pose prediction for rendering
+Created : November 30, 2013
+Authors : Volga Aksoy, 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 "CAPI_FrameTimeManager.h"
+
+
+namespace OVR { namespace CAPI {
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameLatencyTracker
+
+
+FrameLatencyTracker::FrameLatencyTracker()
+{
+ Reset();
+}
+
+void FrameLatencyTracker::Reset()
+{
+ TrackerEnabled = true;
+ WaitMode = SampleWait_Zeroes;
+ FrameIndex = 0;
+ MatchCount = 0;
+ RenderLatencySeconds = 0.0;
+ TimewarpLatencySeconds = 0.0;
+
+ FrameDeltas.Clear();
+}
+
+
+unsigned char FrameLatencyTracker::GetNextDrawColor()
+{
+ if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes) ||
+ (FrameIndex >= FramesTracked))
+ {
+ return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(0);
+ }
+
+ OVR_ASSERT(FrameIndex < FramesTracked);
+ return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1);
+}
+
+
+void FrameLatencyTracker::SaveDrawColor(unsigned char drawColor, double endFrameTime,
+ double renderIMUTime, double timewarpIMUTime )
+{
+ if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes))
+ return;
+
+ if (FrameIndex < FramesTracked)
+ {
+ OVR_ASSERT(Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1) == drawColor);
+ OVR_UNUSED(drawColor);
+
+ // saves {color, endFrame time}
+ FrameEndTimes[FrameIndex].ReadbackIndex = FrameIndex + 1;
+ FrameEndTimes[FrameIndex].TimeSeconds = endFrameTime;
+ FrameEndTimes[FrameIndex].RenderIMUTimeSeconds = renderIMUTime;
+ FrameEndTimes[FrameIndex].TimewarpIMUTimeSeconds= timewarpIMUTime;
+ FrameEndTimes[FrameIndex].MatchedRecord = false;
+ FrameIndex++;
+ }
+ else
+ {
+ // If the request was outstanding for too long, switch to zero mode to restart.
+ if (endFrameTime > (FrameEndTimes[FrameIndex-1].TimeSeconds + 0.15))
+ {
+ if (MatchCount == 0)
+ {
+ // If nothing was matched, we have no latency reading.
+ RenderLatencySeconds = 0.0;
+ TimewarpLatencySeconds = 0.0;
+ }
+
+ WaitMode = SampleWait_Zeroes;
+ MatchCount = 0;
+ FrameIndex = 0;
+ }
+ }
+}
+
+
+void FrameLatencyTracker::MatchRecord(const Util::FrameTimeRecordSet &r)
+{
+ if (!TrackerEnabled)
+ return;
+
+ if (WaitMode == SampleWait_Zeroes)
+ {
+ // Do we have all zeros?
+ if (r.IsAllZeroes())
+ {
+ OVR_ASSERT(FrameIndex == 0);
+ WaitMode = SampleWait_Match;
+ MatchCount = 0;
+ }
+ return;
+ }
+
+ // We are in Match Mode. Wait until all colors are matched or timeout,
+ // at which point we go back to zeros.
+
+ for (int i = 0; i < FrameIndex; i++)
+ {
+ int recordIndex = 0;
+ int consecutiveMatch = 0;
+
+ OVR_ASSERT(FrameEndTimes[i].ReadbackIndex != 0);
+
+ if (r.FindReadbackIndex(&recordIndex, FrameEndTimes[i].ReadbackIndex))
+ {
+ // Advance forward to see that we have several more matches.
+ int ri = recordIndex + 1;
+ int j = i + 1;
+
+ consecutiveMatch++;
+
+ for (; (j < FrameIndex) && (ri < Util::FrameTimeRecordSet::RecordCount); j++, ri++)
+ {
+ if (r[ri].ReadbackIndex != FrameEndTimes[j].ReadbackIndex)
+ break;
+ consecutiveMatch++;
+ }
+
+ // Match at least 2 items in the row, to avoid accidentally matching color.
+ if (consecutiveMatch > 1)
+ {
+ // Record latency values for all but last samples. Keep last 2 samples
+ // for the future to simplify matching.
+ for (int q = 0; q < consecutiveMatch; q++)
+ {
+ const Util::FrameTimeRecord &scanoutFrame = r[recordIndex+q];
+ FrameTimeRecordEx &renderFrame = FrameEndTimes[i+q];
+
+ if (!renderFrame.MatchedRecord)
+ {
+ double deltaSeconds = scanoutFrame.TimeSeconds - renderFrame.TimeSeconds;
+ if (deltaSeconds > 0.0)
+ {
+ FrameDeltas.AddTimeDelta(deltaSeconds);
+ LatencyRecordTime = scanoutFrame.TimeSeconds;
+ RenderLatencySeconds = scanoutFrame.TimeSeconds - renderFrame.RenderIMUTimeSeconds;
+ TimewarpLatencySeconds = (renderFrame.TimewarpIMUTimeSeconds == 0.0) ? 0.0 :
+ (scanoutFrame.TimeSeconds - renderFrame.TimewarpIMUTimeSeconds);
+ }
+
+ renderFrame.MatchedRecord = true;
+ MatchCount++;
+ }
+ }
+
+ // Exit for.
+ break;
+ }
+ }
+ } // for ( i => FrameIndex )
+
+
+ // If we matched all frames, start over.
+ if (MatchCount == FramesTracked)
+ {
+ WaitMode = SampleWait_Zeroes;
+ MatchCount = 0;
+ FrameIndex = 0;
+ }
+}
+
+
+void FrameLatencyTracker::GetLatencyTimings(float latencies[3])
+{
+ if (ovr_GetTimeInSeconds() > (LatencyRecordTime + 2.0))
+ {
+ latencies[0] = 0.0f;
+ latencies[1] = 0.0f;
+ latencies[2] = 0.0f;
+ }
+ else
+ {
+ latencies[0] = (float)RenderLatencySeconds;
+ latencies[1] = (float)TimewarpLatencySeconds;
+ latencies[2] = (float)FrameDeltas.GetMedianTimeDelta();
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+
+FrameTimeManager::FrameTimeManager(bool vsyncEnabled)
+ : VsyncEnabled(vsyncEnabled), DynamicPrediction(true), SdkRender(false),
+ FrameTiming()
+{
+ RenderIMUTimeSeconds = 0.0;
+ TimewarpIMUTimeSeconds = 0.0;
+
+ // HACK: SyncToScanoutDelay observed close to 1 frame in video cards.
+ // Overwritten by dynamic latency measurement on DK2.
+ VSyncToScanoutDelay = 0.013f;
+ NoVSyncToScanoutDelay = 0.004f;
+}
+
+void FrameTimeManager::Init(HmdRenderInfo& renderInfo)
+{
+ // Set up prediction distances.
+ // With-Vsync timings.
+ RenderInfo = renderInfo;
+
+ ScreenSwitchingDelay = RenderInfo.Shutter.PixelSettleTime * 0.5f +
+ RenderInfo.Shutter.PixelPersistence * 0.5f;
+}
+
+void FrameTimeManager::ResetFrameTiming(unsigned frameIndex,
+ bool dynamicPrediction,
+ bool sdkRender)
+{
+ DynamicPrediction = dynamicPrediction;
+ SdkRender = sdkRender;
+
+ FrameTimeDeltas.Clear();
+ DistortionRenderTimes.Clear();
+ ScreenLatencyTracker.Reset();
+
+ FrameTiming.FrameIndex = frameIndex;
+ FrameTiming.NextFrameTime = 0.0;
+ FrameTiming.ThisFrameTime = 0.0;
+ FrameTiming.Inputs.FrameDelta = calcFrameDelta();
+ FrameTiming.Inputs.ScreenDelay = calcScreenDelay();
+ FrameTiming.Inputs.TimewarpWaitDelta = 0.0f;
+
+ LocklessTiming.SetState(FrameTiming);
+}
+
+
+double FrameTimeManager::calcFrameDelta() const
+{
+ // Timing difference between frame is tracked by FrameTimeDeltas, or
+ // is a hard-coded value of 1/FrameRate.
+ double frameDelta;
+
+ if (!VsyncEnabled)
+ {
+ frameDelta = 0.0;
+ }
+ else if (FrameTimeDeltas.GetCount() > 3)
+ {
+ frameDelta = FrameTimeDeltas.GetMedianTimeDelta();
+ if (frameDelta > (RenderInfo.Shutter.VsyncToNextVsync + 0.001))
+ frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
+ }
+ else
+ {
+ frameDelta = RenderInfo.Shutter.VsyncToNextVsync;
+ }
+
+ return frameDelta;
+}
+
+
+double FrameTimeManager::calcScreenDelay() const
+{
+ double screenDelay = ScreenSwitchingDelay;
+ double measuredVSyncToScanout;
+
+ // Use real-time DK2 latency tester HW for prediction if its is working.
+ // Do sanity check under 60 ms
+ if (!VsyncEnabled)
+ {
+ screenDelay += NoVSyncToScanoutDelay;
+ }
+ else if ( DynamicPrediction &&
+ (ScreenLatencyTracker.FrameDeltas.GetCount() > 3) &&
+ (measuredVSyncToScanout = ScreenLatencyTracker.FrameDeltas.GetMedianTimeDelta(),
+ (measuredVSyncToScanout > 0.0001) && (measuredVSyncToScanout < 0.06)) )
+ {
+ screenDelay += measuredVSyncToScanout;
+ }
+ else
+ {
+ screenDelay += VSyncToScanoutDelay;
+ }
+
+ return screenDelay;
+}
+
+
+double FrameTimeManager::calcTimewarpWaitDelta() const
+{
+ // If timewarp timing hasn't been calculated, we should wait.
+ if (!VsyncEnabled)
+ return 0.0;
+
+ if (SdkRender)
+ {
+ if (NeedDistortionTimeMeasurement())
+ return 0.0;
+ return -(DistortionRenderTimes.GetMedianTimeDelta() + 0.002);
+ }
+
+ // Just a hard-coded "high" value for game-drawn code.
+ // TBD: Just return 0 and let users calculate this themselves?
+ return -0.003;
+}
+
+
+
+void FrameTimeManager::Timing::InitTimingFromInputs(const FrameTimeManager::TimingInputs& inputs,
+ HmdShutterTypeEnum shutterType,
+ double thisFrameTime, unsigned int frameIndex)
+{
+ // ThisFrameTime comes from the end of last frame, unless it it changed.
+ double nextFrameBase;
+ double frameDelta = inputs.FrameDelta;
+
+ FrameIndex = frameIndex;
+
+ ThisFrameTime = thisFrameTime;
+ NextFrameTime = ThisFrameTime + frameDelta;
+ nextFrameBase = NextFrameTime + inputs.ScreenDelay;
+ MidpointTime = nextFrameBase + frameDelta * 0.5;
+ TimewarpPointTime = (inputs.TimewarpWaitDelta == 0.0) ?
+ 0.0 : (NextFrameTime + inputs.TimewarpWaitDelta);
+
+ // Calculate absolute points in time when eye rendering or corresponding time-warp
+ // screen edges will become visible.
+ // This only matters with VSync.
+ switch(shutterType)
+ {
+ case HmdShutter_RollingTopToBottom:
+ EyeRenderTimes[0] = MidpointTime;
+ EyeRenderTimes[1] = MidpointTime;
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ break;
+ case HmdShutter_RollingLeftToRight:
+ EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.25;
+ EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.75;
+
+ /*
+ // TBD: MA: It is probably better if mesh sets it up per-eye.
+ // Would apply if screen is 0 -> 1 for each eye mesh
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = MidpointTime;
+ TimeWarpStartEndTimes[1][0] = MidpointTime;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ */
+
+ // Mesh is set up to vary from Edge of scree 0 -> 1 across both eyes
+ TimeWarpStartEndTimes[0][0] = nextFrameBase;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+
+ break;
+ case HmdShutter_RollingRightToLeft:
+
+ EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.75;
+ EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.25;
+
+ // This is *Correct* with Tom's distortion mesh organization.
+ TimeWarpStartEndTimes[0][0] = nextFrameBase ;
+ TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta;
+ TimeWarpStartEndTimes[1][0] = nextFrameBase ;
+ TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta;
+ break;
+ case HmdShutter_Global:
+ // TBD
+ EyeRenderTimes[0] = MidpointTime;
+ EyeRenderTimes[1] = MidpointTime;
+ TimeWarpStartEndTimes[0][0] = MidpointTime;
+ TimeWarpStartEndTimes[0][1] = MidpointTime;
+ TimeWarpStartEndTimes[1][0] = MidpointTime;
+ TimeWarpStartEndTimes[1][1] = MidpointTime;
+ break;
+ default:
+ break;
+ }
+}
+
+
+double FrameTimeManager::BeginFrame(unsigned frameIndex)
+{
+ RenderIMUTimeSeconds = 0.0;
+ TimewarpIMUTimeSeconds = 0.0;
+
+ // ThisFrameTime comes from the end of last frame, unless it it changed.
+ double thisFrameTime = (FrameTiming.NextFrameTime != 0.0) ?
+ FrameTiming.NextFrameTime : ovr_GetTimeInSeconds();
+
+ // We are starting to process a new frame...
+ FrameTiming.InitTimingFromInputs(FrameTiming.Inputs, RenderInfo.Shutter.Type,
+ thisFrameTime, frameIndex);
+
+ return FrameTiming.ThisFrameTime;
+}
+
+
+void FrameTimeManager::EndFrame()
+{
+ // Record timing since last frame; must be called after Present & sync.
+ FrameTiming.NextFrameTime = ovr_GetTimeInSeconds();
+ if (FrameTiming.ThisFrameTime > 0.0)
+ {
+ FrameTimeDeltas.AddTimeDelta(FrameTiming.NextFrameTime - FrameTiming.ThisFrameTime);
+ FrameTiming.Inputs.FrameDelta = calcFrameDelta();
+ }
+
+ // Write to Lock-less
+ LocklessTiming.SetState(FrameTiming);
+}
+
+
+
+// Thread-safe function to query timing for a future frame
+
+FrameTimeManager::Timing FrameTimeManager::GetFrameTiming(unsigned frameIndex)
+{
+ Timing frameTiming = LocklessTiming.GetState();
+
+ if (frameTiming.ThisFrameTime != 0.0)
+ {
+ // If timing hasn't been initialized, starting based on "now" is the best guess.
+ frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
+ ovr_GetTimeInSeconds(), frameIndex);
+ }
+
+ else if (frameIndex > frameTiming.FrameIndex)
+ {
+ unsigned frameDelta = frameIndex - frameTiming.FrameIndex;
+ double thisFrameTime = frameTiming.NextFrameTime +
+ double(frameDelta-1) * frameTiming.Inputs.FrameDelta;
+ // Don't run away too far into the future beyond rendering.
+ OVR_ASSERT(frameDelta < 6);
+
+ frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type,
+ thisFrameTime, frameIndex);
+ }
+
+ return frameTiming;
+}
+
+
+double FrameTimeManager::GetEyePredictionTime(ovrEyeType eye)
+{
+ if (VsyncEnabled)
+ {
+ return FrameTiming.EyeRenderTimes[eye];
+ }
+
+ // No VSync: Best guess for the near future
+ return ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
+}
+
+Transformf FrameTimeManager::GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye)
+{
+ double eyeRenderTime = GetEyePredictionTime(eye);
+ ovrSensorState eyeState = ovrHmd_GetSensorState(hmd, eyeRenderTime);
+
+// EyeRenderPoses[eye] = eyeState.Predicted.Pose;
+
+ // Record view pose sampling time for Latency reporting.
+ if (RenderIMUTimeSeconds == 0.0)
+ RenderIMUTimeSeconds = eyeState.Recorded.TimeInSeconds;
+
+ return eyeState.Predicted.Pose;
+}
+
+
+void FrameTimeManager::GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2])
+{
+ if (VsyncEnabled)
+ {
+ timewarpStartEnd[0] = FrameTiming.TimeWarpStartEndTimes[eye][0];
+ timewarpStartEnd[1] = FrameTiming.TimeWarpStartEndTimes[eye][1];
+ return;
+ }
+
+ // Free-running, so this will be displayed immediately.
+ // Unfortunately we have no idea which bit of the screen is actually going to be displayed.
+ // TODO: guess which bit of the screen is being displayed!
+ // (e.g. use DONOTWAIT on present and see when the return isn't WASSTILLWAITING?)
+
+ // We have no idea where scan-out is currently, so we can't usefully warp the screen spatially.
+ timewarpStartEnd[0] = ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay;
+ timewarpStartEnd[1] = timewarpStartEnd[0];
+}
+
+
+void FrameTimeManager::GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eyeId,
+ ovrPosef renderPose, ovrMatrix4f twmOut[2])
+{
+ if (!hmd)
+ {
+ return;
+ }
+
+ double timewarpStartEnd[2] = { 0.0, 0.0 };
+ GetTimewarpPredictions(eyeId, timewarpStartEnd);
+
+ ovrSensorState startState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[0]);
+ ovrSensorState endState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[1]);
+
+ if (TimewarpIMUTimeSeconds == 0.0)
+ TimewarpIMUTimeSeconds = startState.Recorded.TimeInSeconds;
+
+ Quatf quatFromStart = startState.Predicted.Pose.Orientation;
+ Quatf quatFromEnd = endState.Predicted.Pose.Orientation;
+ Quatf quatFromEye = renderPose.Orientation; //EyeRenderPoses[eyeId].Orientation;
+ quatFromEye.Invert();
+
+ Quatf timewarpStartQuat = quatFromEye * quatFromStart;
+ Quatf timewarpEndQuat = quatFromEye * quatFromEnd;
+
+ Matrix4f timewarpStart(timewarpStartQuat);
+ Matrix4f timewarpEnd(timewarpEndQuat);
+
+
+ // The real-world orientations have: X=right, Y=up, Z=backwards.
+ // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards.
+ // So we need to perform a similarity transform on this delta matrix.
+ // The verbose code would look like this:
+ /*
+ Matrix4f matBasisChange;
+ matBasisChange.SetIdentity();
+ matBasisChange.M[0][0] = 1.0f;
+ matBasisChange.M[1][1] = -1.0f;
+ matBasisChange.M[2][2] = -1.0f;
+ Matrix4f matBasisChangeInv = matBasisChange.Inverted();
+ matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange;
+ */
+ // ...but of course all the above is a constant transform and much more easily done.
+ // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column,
+ // and of course most of the flips cancel:
+ // +++ +-- +--
+ // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++
+ // +++ +-- -++
+ timewarpStart.M[0][1] = -timewarpStart.M[0][1];
+ timewarpStart.M[0][2] = -timewarpStart.M[0][2];
+ timewarpStart.M[1][0] = -timewarpStart.M[1][0];
+ timewarpStart.M[2][0] = -timewarpStart.M[2][0];
+
+ timewarpEnd .M[0][1] = -timewarpEnd .M[0][1];
+ timewarpEnd .M[0][2] = -timewarpEnd .M[0][2];
+ timewarpEnd .M[1][0] = -timewarpEnd .M[1][0];
+ timewarpEnd .M[2][0] = -timewarpEnd .M[2][0];
+
+ twmOut[0] = timewarpStart;
+ twmOut[1] = timewarpEnd;
+}
+
+
+// Used by renderer to determine if it should time distortion rendering.
+bool FrameTimeManager::NeedDistortionTimeMeasurement() const
+{
+ if (!VsyncEnabled)
+ return false;
+ return DistortionRenderTimes.GetCount() < 10;
+}
+
+
+void FrameTimeManager::AddDistortionTimeMeasurement(double distortionTimeSeconds)
+{
+ DistortionRenderTimes.AddTimeDelta(distortionTimeSeconds);
+
+ // If timewarp timing changes based on this sample, update it.
+ double newTimewarpWaitDelta = calcTimewarpWaitDelta();
+ if (newTimewarpWaitDelta != FrameTiming.Inputs.TimewarpWaitDelta)
+ {
+ FrameTiming.Inputs.TimewarpWaitDelta = newTimewarpWaitDelta;
+ LocklessTiming.SetState(FrameTiming);
+ }
+}
+
+
+void FrameTimeManager::UpdateFrameLatencyTrackingAfterEndFrame(
+ unsigned char frameLatencyTestColor,
+ const Util::FrameTimeRecordSet& rs)
+{
+ // FrameTiming.NextFrameTime in this context (after EndFrame) is the end frame time.
+ ScreenLatencyTracker.SaveDrawColor(frameLatencyTestColor,
+ FrameTiming.NextFrameTime,
+ RenderIMUTimeSeconds,
+ TimewarpIMUTimeSeconds);
+
+ ScreenLatencyTracker.MatchRecord(rs);
+
+ // If screen delay changed, update timing.
+ double newScreenDelay = calcScreenDelay();
+ if (newScreenDelay != FrameTiming.Inputs.ScreenDelay)
+ {
+ FrameTiming.Inputs.ScreenDelay = newScreenDelay;
+ LocklessTiming.SetState(FrameTiming);
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** TimeDeltaCollector
+
+void TimeDeltaCollector::AddTimeDelta(double timeSeconds)
+{
+ // avoid adding invalid timing values
+ if(timeSeconds < 0.0f)
+ return;
+
+ if (Count == Capacity)
+ {
+ for(int i=0; i< Count-1; i++)
+ TimeBufferSeconds[i] = TimeBufferSeconds[i+1];
+ Count--;
+ }
+ TimeBufferSeconds[Count++] = timeSeconds;
+}
+
+double TimeDeltaCollector::GetMedianTimeDelta() const
+{
+ double SortedList[Capacity];
+ bool used[Capacity];
+
+ memset(used, 0, sizeof(used));
+ SortedList[0] = 0.0; // In case Count was 0...
+
+ // Probably the slowest way to find median...
+ for (int i=0; i<Count; i++)
+ {
+ double smallestDelta = 1000000.0;
+ int index = 0;
+
+ for (int j = 0; j < Count; j++)
+ {
+ if (!used[j])
+ {
+ if (TimeBufferSeconds[j] < smallestDelta)
+ {
+ smallestDelta = TimeBufferSeconds[j];
+ index = j;
+ }
+ }
+ }
+
+ // Mark as used
+ used[index] = true;
+ SortedList[i] = smallestDelta;
+ }
+
+ return SortedList[Count/2];
+}
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h
new file mode 100644
index 0000000..4693697
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_FrameTimeManager.h
@@ -0,0 +1,264 @@
+/************************************************************************************
+
+Filename : CAPI_FrameTimeManager.h
+Content : Manage frame timing and pose prediction for rendering
+Created : November 30, 2013
+Authors : Volga Aksoy, 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.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_FrameTimeManager_h
+#define OVR_CAPI_FrameTimeManager_h
+
+#include "../OVR_CAPI.h"
+#include "../Kernel/OVR_Timer.h"
+#include "../Kernel/OVR_Math.h"
+#include "../Util/Util_Render_Stereo.h"
+#include "../Util/Util_LatencyTest2.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+
+// Helper class to collect median times between frames, so that we know
+// how long to wait.
+struct TimeDeltaCollector
+{
+ TimeDeltaCollector() : Count(0) { }
+
+ void AddTimeDelta(double timeSeconds);
+ void Clear() { Count = 0; }
+
+ double GetMedianTimeDelta() const;
+
+ double GetCount() const { return Count; }
+
+ enum { Capacity = 12 };
+private:
+ int Count;
+ double TimeBufferSeconds[Capacity];
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameLatencyTracker
+
+// FrameLatencyTracker tracks frame Present to display Scan-out timing, as reported by
+// the DK2 internal latency tester pixel read-back. The computed value is used in
+// FrameTimeManager for prediction. View Render and TimeWarp to scan-out latencies are
+// also reported for debugging.
+//
+// The class operates by generating color values from GetNextDrawColor() that must
+// be rendered on the back end and then looking for matching values in FrameTimeRecordSet
+// structure as reported by HW.
+
+class FrameLatencyTracker
+{
+public:
+
+ enum { FramesTracked = Util::LT2_IncrementCount-1 };
+
+ FrameLatencyTracker();
+
+ // DrawColor == 0 is special in that it doesn't need saving of timestamp
+ unsigned char GetNextDrawColor();
+
+ void SaveDrawColor(unsigned char drawColor, double endFrameTime,
+ double renderIMUTime, double timewarpIMUTime );
+
+ void MatchRecord(const Util::FrameTimeRecordSet &r);
+
+ void GetLatencyTimings(float latencies[3]);
+
+ void Reset();
+
+public:
+
+ struct FrameTimeRecordEx : public Util::FrameTimeRecord
+ {
+ bool MatchedRecord;
+ double RenderIMUTimeSeconds;
+ double TimewarpIMUTimeSeconds;
+ };
+
+ // True if rendering read-back is enabled.
+ bool TrackerEnabled;
+
+ enum SampleWaitType {
+ SampleWait_Zeroes, // We are waiting for a record with all zeros.
+ SampleWait_Match // We are issuing & matching colors.
+ };
+
+ SampleWaitType WaitMode;
+ int MatchCount;
+ // Records of frame timings that we are trying to measure.
+ FrameTimeRecordEx FrameEndTimes[FramesTracked];
+ int FrameIndex;
+ // Median filter for (ScanoutTimeSeconds - PostPresent frame time)
+ TimeDeltaCollector FrameDeltas;
+ // Latency reporting results
+ double RenderLatencySeconds;
+ double TimewarpLatencySeconds;
+ double LatencyRecordTime;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** FrameTimeManager
+
+// FrameTimeManager keeps track of rendered frame timing and handles predictions for
+// orientations and time-warp.
+
+class FrameTimeManager
+{
+public:
+ FrameTimeManager(bool vsyncEnabled = true);
+
+ // Data that affects frame timing computation.
+ struct TimingInputs
+ {
+ // Hard-coded value or dynamic as reported by FrameTimeDeltas.GetMedianTimeDelta().
+ double FrameDelta;
+ // Screen delay from present to scan-out, as potentially reported by ScreenLatencyTracker.
+ double ScreenDelay;
+ // Negative value of how many seconds before EndFrame we start timewarp. 0.0 if not used.
+ double TimewarpWaitDelta;
+
+ TimingInputs()
+ : FrameDelta(0), ScreenDelay(0), TimewarpWaitDelta(0)
+ { }
+ };
+
+ // Timing values for a specific frame.
+ struct Timing
+ {
+ TimingInputs Inputs;
+
+ // Index of a frame that started at ThisFrameTime.
+ unsigned int FrameIndex;
+ // Predicted absolute times for when this frame will show up on screen.
+ // Generally, all values will be >= NextFrameTime, since that's the time we expect next
+ // vsync to succeed.
+ double ThisFrameTime;
+ double TimewarpPointTime;
+ double NextFrameTime;
+ double MidpointTime;
+ double EyeRenderTimes[2];
+ double TimeWarpStartEndTimes[2][2];
+
+ Timing()
+ {
+ memset(this, 0, sizeof(Timing));
+ }
+
+ void InitTimingFromInputs(const TimingInputs& inputs, HmdShutterTypeEnum shutterType,
+ double thisFrameTime, unsigned int frameIndex);
+ };
+
+
+ // Called on startup to provided data on HMD timing.
+ void Init(HmdRenderInfo& renderInfo);
+
+ // Called with each new ConfigureRendering.
+ void ResetFrameTiming(unsigned frameIndex,
+ bool dynamicPrediction, bool sdkRender);
+
+ void SetVsync(bool enabled) { VsyncEnabled = enabled; }
+
+ // BeginFrame returns time of the call
+ // TBD: Should this be a predicted time value instead ?
+ double BeginFrame(unsigned frameIndex);
+ void EndFrame();
+
+ // Thread-safe function to query timing for a future frame
+ Timing GetFrameTiming(unsigned frameIndex);
+
+ double GetEyePredictionTime(ovrEyeType eye);
+ Transformf GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye);
+
+ void GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]);
+ void GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]);
+
+ // Used by renderer to determine if it should time distortion rendering.
+ bool NeedDistortionTimeMeasurement() const;
+ void AddDistortionTimeMeasurement(double distortionTimeSeconds);
+
+
+ // DK2 Lateny test interface
+
+ // Get next draw color for DK2 latency tester
+ unsigned char GetFrameLatencyTestDrawColor()
+ { return ScreenLatencyTracker.GetNextDrawColor(); }
+
+ // Must be called after EndFrame() to update latency tester timings.
+ // Must pass color reported by NextFrameColor for this frame.
+ void UpdateFrameLatencyTrackingAfterEndFrame(unsigned char frameLatencyTestColor,
+ const Util::FrameTimeRecordSet& rs);
+
+ void GetLatencyTimings(float latencies[3])
+ { return ScreenLatencyTracker.GetLatencyTimings(latencies); }
+
+
+ const Timing& GetFrameTiming() const { return FrameTiming; }
+
+private:
+
+ double calcFrameDelta() const;
+ double calcScreenDelay() const;
+ double calcTimewarpWaitDelta() const;
+
+
+ HmdRenderInfo RenderInfo;
+ // Timings are collected through a median filter, to avoid outliers.
+ TimeDeltaCollector FrameTimeDeltas;
+ TimeDeltaCollector DistortionRenderTimes;
+ FrameLatencyTracker ScreenLatencyTracker;
+
+ // Timing changes if we have no Vsync (all prediction is reduced to fixed interval).
+ bool VsyncEnabled;
+ // Set if we are rendering via the SDK, so DistortionRenderTimes is valid.
+ bool DynamicPrediction;
+ // Set if SDk is doing teh rendering.
+ bool SdkRender;
+
+ // Total frame delay due to VsyncToFirstScanline, persistence and settle time.
+ // Computed from RenderInfor.Shutter.
+ double VSyncToScanoutDelay;
+ double NoVSyncToScanoutDelay;
+ double ScreenSwitchingDelay;
+
+ // Current (or last) frame timing info. Used as a source for LocklessTiming.
+ Timing FrameTiming;
+ // TBD: Don't we need NextFrame here as well?
+ LocklessUpdater<Timing> LocklessTiming;
+
+
+ // IMU Read timings
+ double RenderIMUTimeSeconds;
+ double TimewarpIMUTimeSeconds;
+};
+
+
+}} // namespace OVR::CAPI
+
+#endif // OVR_CAPI_FrameTimeManager_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_GlobalState.cpp b/LibOVR/Src/CAPI/CAPI_GlobalState.cpp
new file mode 100644
index 0000000..2ed1794
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_GlobalState.cpp
@@ -0,0 +1,142 @@
+/************************************************************************************
+
+Filename : CAPI_GlobalState.cpp
+Content : Maintains global state of the CAPI
+Created : January 24, 2014
+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 "CAPI_GlobalState.h"
+
+namespace OVR { namespace CAPI {
+
+
+//-------------------------------------------------------------------------------------
+// Open Questions / Notes
+
+// 2. Detect HMDs.
+// Challenge: If we do everything through polling, it would imply we want all the devices
+// initialized. However, there may be multiple rifts, extra sensors, etc,
+// which shouldn't be allocated.
+//
+
+// How do you reset orientation Quaternion?
+// Can you change IPD?
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVRGlobalState
+
+// Global instance
+GlobalState* GlobalState::pInstance = 0;
+
+
+GlobalState::GlobalState()
+{
+ pManager = *DeviceManager::Create();
+ // Handle the DeviceManager's messages
+ pManager->AddMessageHandler( this );
+ EnumerateDevices();
+
+ // PhoneSensors::Init();
+}
+
+GlobalState::~GlobalState()
+{
+ RemoveHandlerFromDevices();
+ OVR_ASSERT(HMDs.IsEmpty());
+}
+
+int GlobalState::EnumerateDevices()
+{
+ // Need to use separate lock for device enumeration, as pManager->GetHandlerLock()
+ // would produce deadlocks here.
+ Lock::Locker lock(&EnumerationLock);
+
+ EnumeratedDevices.Clear();
+
+ DeviceEnumerator<HMDDevice> e = pManager->EnumerateDevices<HMDDevice>();
+ while(e.IsAvailable())
+ {
+ EnumeratedDevices.PushBack(DeviceHandle(e));
+ e.Next();
+ }
+
+ return (int)EnumeratedDevices.GetSize();
+}
+
+
+HMDDevice* GlobalState::CreateDevice(int index)
+{
+ Lock::Locker lock(&EnumerationLock);
+
+ if (index >= (int)EnumeratedDevices.GetSize())
+ return 0;
+ return EnumeratedDevices[index].CreateDeviceTyped<HMDDevice>();
+}
+
+
+void GlobalState::AddHMD(HMDState* hmd)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ HMDs.PushBack(hmd);
+}
+void GlobalState::RemoveHMD(HMDState* hmd)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ hmd->RemoveNode();
+}
+
+void GlobalState::NotifyHMDs_AddDevice(DeviceType deviceType)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ for(HMDState* hmd = HMDs.GetFirst(); !HMDs.IsNull(hmd); hmd = hmd->pNext)
+ hmd->NotifyAddDevice(deviceType);
+}
+
+void GlobalState::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved)
+ {
+ if (msg.pDevice == pManager)
+ {
+ const MessageDeviceStatus& statusMsg =
+ static_cast<const MessageDeviceStatus&>(msg);
+
+ if (msg.Type == Message_DeviceAdded)
+ {
+ //LogText("OnMessage DeviceAdded.\n");
+
+ // We may have added a sensor/other device; notify any HMDs that might
+ // need it to check for it later.
+ NotifyHMDs_AddDevice(statusMsg.Handle.GetType());
+ }
+ else
+ {
+ //LogText("OnMessage DeviceRemoved.\n");
+ }
+ }
+ }
+}
+
+
+}} // namespace OVR::CAPI
diff --git a/LibOVR/Src/CAPI/CAPI_GlobalState.h b/LibOVR/Src/CAPI/CAPI_GlobalState.h
new file mode 100644
index 0000000..54ab8cc
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_GlobalState.h
@@ -0,0 +1,84 @@
+/************************************************************************************
+
+Filename : CAPI_GlobalState.h
+Content : Maintains global state of the CAPI
+Created : January 24, 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.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_GlobalState_h
+#define OVR_CAPI_GlobalState_h
+
+#include "../OVR_CAPI.h"
+#include "../OVR_Device.h"
+#include "../Kernel/OVR_Timer.h"
+#include "../Kernel/OVR_Math.h"
+
+#include "CAPI_HMDState.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** OVRGlobalState
+
+// Global DeviceManager state - singleton instance of this is created
+// by ovr_Initialize().
+class GlobalState : public MessageHandler, public NewOverrideBase
+{
+public:
+ GlobalState();
+ ~GlobalState();
+
+ static GlobalState *pInstance;
+
+ int EnumerateDevices();
+ HMDDevice* CreateDevice(int index);
+
+ // MessageHandler implementation
+ void OnMessage(const Message& msg);
+
+ // Helpers used to keep track of HMDs and notify them of sensor changes.
+ void AddHMD(HMDState* hmd);
+ void RemoveHMD(HMDState* hmd);
+ void NotifyHMDs_AddDevice(DeviceType deviceType);
+
+ const char* GetLastError()
+ {
+ return 0;
+ }
+
+ DeviceManager* GetManager() { return pManager; }
+
+protected:
+
+ Ptr<DeviceManager> pManager;
+ Lock EnumerationLock;
+ Array<DeviceHandle> EnumeratedDevices;
+
+ // Currently created hmds; protected by Manager lock.
+ List<HMDState> HMDs;
+};
+
+}} // namespace OVR::CAPI
+
+#endif
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp b/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp
new file mode 100644
index 0000000..00bdea2
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp
@@ -0,0 +1,143 @@
+/************************************************************************************
+
+Filename : OVR_CAPI_HMDRenderState.cpp
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+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 "CAPI_HMDRenderState.h"
+
+namespace OVR { namespace CAPI {
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDRenderState
+
+
+HMDRenderState::HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo)
+ : HMD(hmd), HMDInfo(hmdInfo)
+{
+ RenderInfo = GenerateHmdRenderInfoFromHmdInfo( HMDInfo, userProfile );
+
+ Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderInfo, 0);
+ Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderInfo, 0);
+
+ ClearColor[0] = ClearColor[1] = ClearColor[2] = ClearColor[3] =0.0f;
+
+ EnabledHmdCaps = 0;
+}
+
+HMDRenderState::~HMDRenderState()
+{
+}
+
+ovrHmdDesc HMDRenderState::GetDesc()
+{
+ ovrHmdDesc d;
+ memset(&d, 0, sizeof(d));
+
+ d.Type = ovrHmd_Other;
+
+ d.ProductName = HMDInfo.ProductName;
+ d.Manufacturer = HMDInfo.Manufacturer;
+ d.Resolution.w = HMDInfo.ResolutionInPixels.w;
+ d.Resolution.h = HMDInfo.ResolutionInPixels.h;
+ d.WindowsPos.x = HMDInfo.DesktopX;
+ d.WindowsPos.y = HMDInfo.DesktopY;
+ d.DisplayDeviceName = HMDInfo.DisplayDeviceName;
+ d.DisplayId = HMDInfo.DisplayId;
+
+ d.HmdCaps = ovrHmdCap_Present | ovrHmdCap_NoVSync;
+ d.SensorCaps = ovrSensorCap_YawCorrection | ovrSensorCap_Orientation;
+ d.DistortionCaps = ovrDistortionCap_Chromatic | ovrDistortionCap_TimeWarp | ovrDistortionCap_Vignette;
+
+ if (strstr(HMDInfo.ProductName, "DK1"))
+ {
+ d.Type = ovrHmd_DK1;
+ }
+ else if (strstr(HMDInfo.ProductName, "DK2"))
+ {
+ d.Type = ovrHmd_DK2;
+ d.HmdCaps |= ovrHmdCap_LowPersistence |
+ ovrHmdCap_LatencyTest | ovrHmdCap_DynamicPrediction;
+ d.SensorCaps |= ovrSensorCap_Position;
+ }
+
+ DistortionRenderDesc& leftDistortion = Distortion[0];
+ DistortionRenderDesc& rightDistortion = Distortion[1];
+
+ // The suggested FOV (assuming eye rotation)
+ d.DefaultEyeFov[0] = CalculateFovFromHmdInfo(StereoEye_Left, leftDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION);
+ d.DefaultEyeFov[1] = CalculateFovFromHmdInfo(StereoEye_Right, rightDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION);
+
+ // FOV extended across the entire screen
+ d.MaxEyeFov[0] = GetPhysicalScreenFov(StereoEye_Left, leftDistortion);
+ d.MaxEyeFov[1] = GetPhysicalScreenFov(StereoEye_Right, rightDistortion);
+
+ if (HMDInfo.Shutter.Type == HmdShutter_RollingRightToLeft)
+ {
+ d.EyeRenderOrder[0] = ovrEye_Right;
+ d.EyeRenderOrder[1] = ovrEye_Left;
+ }
+ else
+ {
+ d.EyeRenderOrder[0] = ovrEye_Left;
+ d.EyeRenderOrder[1] = ovrEye_Right;
+ }
+
+ return d;
+}
+
+
+ovrSizei HMDRenderState::GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel)
+{
+ OVR_ASSERT((unsigned)eye < 2);
+ StereoEye seye = (eye == ovrEye_Left) ? StereoEye_Left : StereoEye_Right;
+ return CalculateIdealPixelSize(seye, Distortion[eye], fov, pixelsPerDisplayPixel);
+}
+
+ovrEyeRenderDesc HMDRenderState::calcRenderDesc(ovrEyeType eyeType, const ovrFovPort& fov)
+{
+ HmdRenderInfo& hmdri = RenderInfo;
+ StereoEye eye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right;
+ ovrEyeRenderDesc e0;
+
+ e0.Eye = eyeType;
+ e0.Fov = fov;
+ e0.ViewAdjust = CalculateEyeVirtualCameraOffset(hmdri, eye, false);
+ e0.DistortedViewport = GetFramebufferViewport(eye, hmdri);
+ e0.PixelsPerTanAngleAtCenter = Distortion[0].PixelsPerTanAngleAtCenter;
+
+ return e0;
+}
+
+
+void HMDRenderState::setupRenderDesc( ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrFovPort eyeFovIn[2] )
+{
+ eyeRenderDescOut[0] = EyeRenderDesc[0] = calcRenderDesc(ovrEye_Left, eyeFovIn[0]);
+ eyeRenderDescOut[1] = EyeRenderDesc[1] = calcRenderDesc(ovrEye_Right, eyeFovIn[1]);
+}
+
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDRenderState.h b/LibOVR/Src/CAPI/CAPI_HMDRenderState.h
new file mode 100644
index 0000000..a4e8d21
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDRenderState.h
@@ -0,0 +1,93 @@
+/************************************************************************************
+
+Filename : CAPI_HMDRenderState.h
+Content : Combines all of the rendering state associated with the HMD
+Created : February 2, 2014
+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.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_HMDRenderState_h
+#define OVR_CAPI_HMDRenderState_h
+
+#include "../OVR_CAPI.h"
+#include "../Kernel/OVR_Math.h"
+#include "../Util/Util_Render_Stereo.h"
+
+
+namespace OVR { namespace CAPI {
+
+using namespace OVR::Util::Render;
+
+//-------------------------------------------------------------------------------------
+// ***** HMDRenderState
+
+// Combines all of the rendering setup information about one HMD.
+
+class HMDRenderState : public NewOverrideBase
+{
+ // Quiet assignment compiler warning.
+ void operator = (const HMDRenderState&) { }
+public:
+
+ HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo);
+ virtual ~HMDRenderState();
+
+
+ // *** Rendering Setup
+
+ // Delegated access APIs
+ ovrHmdDesc GetDesc();
+ ovrSizei GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel);
+
+ ovrEyeRenderDesc calcRenderDesc(ovrEyeType eyeType, const ovrFovPort& fov);
+
+ void setupRenderDesc(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrFovPort eyeFovIn[2]);
+public:
+
+ // HMDInfo shouldn't change, as its string pointers are passed out.
+ ovrHmd HMD;
+ const OVR::HMDInfo& HMDInfo;
+
+ //const char* pLastError;
+
+ HmdRenderInfo RenderInfo;
+ DistortionRenderDesc Distortion[2];
+ ovrEyeRenderDesc EyeRenderDesc[2];
+
+ // Clear color used for distortion
+ float ClearColor[4];
+
+ // Pose at which last time the eye was rendered, as submitted by EndEyeRender.
+ ovrPosef EyeRenderPoses[2];
+
+ // Capabilities passed to Configure.
+ unsigned EnabledHmdCaps;
+ unsigned DistortionCaps;
+};
+
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_HMDState_h
+
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDState.cpp b/LibOVR/Src/CAPI/CAPI_HMDState.cpp
new file mode 100644
index 0000000..fd98225
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDState.cpp
@@ -0,0 +1,804 @@
+/************************************************************************************
+
+Filename : CAPI_HMDState.cpp
+Content : State associated with a single HMD
+Created : January 24, 2014
+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 "CAPI_HMDState.h"
+#include "CAPI_GlobalState.h"
+#include "../OVR_Profile.h"
+
+namespace OVR { namespace CAPI {
+
+//-------------------------------------------------------------------------------------
+// ***** HMDState
+
+
+HMDState::HMDState(HMDDevice* device)
+ : pHMD(device), HMDInfoW(device), HMDInfo(HMDInfoW.h),
+ EnabledHmdCaps(0), HmdCapsAppliedToSensor(0),
+ SensorStarted(0), SensorCreated(0), SensorCaps(0),
+ AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0),
+ RenderState(getThis(), pHMD->GetProfile(), HMDInfoW.h),
+ LastFrameTimeSeconds(0.0f), LastGetFrameTimeSeconds(0.0),
+ LatencyTestActive(false),
+ LatencyTest2Active(false)
+{
+ pLastError = 0;
+ GlobalState::pInstance->AddHMD(this);
+
+ // Should be in renderer?
+ TimeManager.Init(RenderState.RenderInfo);
+
+ EyeRenderActive[0] = false;
+ EyeRenderActive[1] = false;
+
+ LatencyTestDrawColor[0] = 0;
+ LatencyTestDrawColor[1] = 0;
+ LatencyTestDrawColor[2] = 0;
+
+ OVR_CAPI_VISION_CODE( pPoseTracker = 0; )
+
+ RenderingConfigured = false;
+ BeginFrameCalled = false;
+ BeginFrameThreadId = 0;
+ BeginFrameTimingCalled = false;
+}
+
+HMDState::HMDState(ovrHmdType hmdType)
+ : pHMD(0), HMDInfoW(hmdType), HMDInfo(HMDInfoW.h),
+ EnabledHmdCaps(0),
+ SensorStarted(0), SensorCreated(0), SensorCaps(0),
+ AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0),
+ RenderState(getThis(), 0, HMDInfoW.h), // No profile.
+ LastFrameTimeSeconds(0.0), LastGetFrameTimeSeconds(0.0)
+{
+ // TBD: We should probably be looking up the default profile for the given
+ // device type + user.
+
+ pLastError = 0;
+ GlobalState::pInstance->AddHMD(this);
+
+ // Should be in renderer?
+ TimeManager.Init(RenderState.RenderInfo);
+
+ EyeRenderActive[0] = false;
+ EyeRenderActive[1] = false;
+
+ OVR_CAPI_VISION_CODE( pPoseTracker = 0; )
+
+ RenderingConfigured = false;
+ BeginFrameCalled = false;
+ BeginFrameThreadId = 0;
+ BeginFrameTimingCalled = false;
+}
+
+
+HMDState::~HMDState()
+{
+ OVR_ASSERT(GlobalState::pInstance);
+
+ StopSensor();
+ ConfigureRendering(0,0,0,0);
+
+ OVR_CAPI_VISION_CODE( OVR_ASSERT(pPoseTracker == 0); )
+
+ GlobalState::pInstance->RemoveHMD(this);
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// *** Sensor
+
+bool HMDState::StartSensor(unsigned supportedCaps, unsigned requiredCaps)
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ bool crystalCoveOrBetter = (HMDInfo.HmdType == HmdType_CrystalCoveProto) ||
+ (HMDInfo.HmdType == HmdType_DK2);
+ bool sensorCreatedJustNow = false;
+
+ // TBD: In case of sensor not being immediately available, it would be good to check
+ // yaw config availability to match it with ovrHmdCap_YawCorrection requirement.
+ //
+
+ if (!crystalCoveOrBetter)
+ {
+ if (requiredCaps & ovrSensorCap_Position)
+ {
+ pLastError = "ovrSensorCap_Position not supported on this HMD.";
+ return false;
+ }
+ }
+
+ supportedCaps |= requiredCaps;
+
+ if (pHMD && !pSensor)
+ {
+ // Zero AddSensorCount before creation, in case it fails (or succeeds but then
+ // immediately gets disconnected) followed by another Add notification.
+ AddSensorCount = 0;
+ pSensor = *pHMD->GetSensor();
+ sensorCreatedJustNow= true;
+
+ if (pSensor)
+ {
+ pSensor->SetReportRate(500);
+ SFusion.AttachToSensor(pSensor);
+ applyProfileToSensorFusion();
+ }
+ else
+ {
+ if (requiredCaps & ovrSensorCap_Orientation)
+ {
+ pLastError = "Failed to create sensor.";
+ return false;
+ }
+ }
+ }
+
+
+ if ((requiredCaps & ovrSensorCap_YawCorrection) && !pSensor->IsMagCalibrated())
+ {
+ pLastError = "ovrHmdCap_YawCorrection not available.";
+ if (sensorCreatedJustNow)
+ {
+ SFusion.AttachToSensor(0);
+ SFusion.Reset();
+ pSensor.Clear();
+ }
+ return false;
+ }
+
+ SFusion.SetYawCorrectionEnabled((supportedCaps & ovrSensorCap_YawCorrection) != 0);
+
+ if (pSensor && sensorCreatedJustNow)
+ {
+ LogText("Sensor created.\n");
+ SensorCreated = true;
+ }
+
+ updateDK2FeaturesTiedToSensor(sensorCreatedJustNow);
+
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+
+ if (crystalCoveOrBetter && (supportedCaps & ovrSensorCap_Position))
+ {
+ if (!pPoseTracker)
+ {
+ pPoseTracker = new Vision::PoseTracker(SFusion);
+ if (pPoseTracker)
+ {
+ pPoseTracker->AssociateHMD(pSensor);
+ LogText("Sensor Pose tracker created.\n");
+ }
+ }
+
+ // TBD: How do we verify that position tracking is actually available
+ // i.e. camera is plugged in?
+ }
+ else if (pPoseTracker)
+ {
+ // TBD: Internals not thread safe - must fix!!
+ delete pPoseTracker;
+ pPoseTracker = 0;
+ LogText("Sensor Pose tracker destroyed.\n");
+ }
+
+#endif // OVR_CAPI_VISIONSUPPORT
+
+ SensorCaps = supportedCaps;
+ SensorStarted = true;
+
+ return true;
+}
+
+
+// Stops sensor sampling, shutting down internal resources.
+void HMDState::StopSensor()
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ if (SensorStarted)
+ {
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (pPoseTracker)
+ {
+ // TBD: Internals not thread safe - must fix!!
+ delete pPoseTracker;
+ pPoseTracker = 0;
+ LogText("Sensor Pose tracker destroyed.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+
+ SFusion.AttachToSensor(0);
+ SFusion.Reset();
+ pSensor.Clear();
+ HmdCapsAppliedToSensor = 0;
+ AddSensorCount = 0;
+ SensorCaps = 0;
+ SensorCreated = false;
+ SensorStarted = false;
+
+ LogText("StopSensor succeeded.\n");
+ }
+}
+
+// Resets sensor orientation.
+void HMDState::ResetSensor()
+{
+ SFusion.Reset();
+}
+
+
+// Returns prediction for time.
+ovrSensorState HMDState::PredictedSensorState(double absTime)
+{
+ SensorState ss;
+
+ // We are trying to keep this path lockless unless we are notified of new device
+ // creation while not having a sensor yet. It's ok to check SensorCreated volatile
+ // flag here, since GetSensorStateAtTime() is internally lockless and safe.
+
+ if (SensorCreated || checkCreateSensor())
+ {
+ ss = SFusion.GetSensorStateAtTime(absTime);
+
+ if (!(ss.StatusFlags & ovrStatus_OrientationTracked))
+ {
+ Lock::Locker lockScope(&DevicesLock);
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (pPoseTracker)
+ {
+ // TBD: Internals not thread safe - must fix!!
+ delete pPoseTracker;
+ pPoseTracker = 0;
+ LogText("Sensor Pose tracker destroyed.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+ // Not needed yet; SFusion.AttachToSensor(0);
+ // This seems to reset orientation anyway...
+ pSensor.Clear();
+ SensorCreated = false;
+ HmdCapsAppliedToSensor = 0;
+ }
+ }
+ else
+ {
+ // SensorState() defaults to 0s.
+ // ss.Pose.Orientation = Quatf();
+ // ..
+
+ // John:
+ // We still want valid times so frames will get a delta-time
+ // and allow operation with a joypad when the sensor isn't
+ // connected.
+ ss.Recorded.TimeInSeconds = absTime;
+ ss.Predicted.TimeInSeconds = absTime;
+ }
+
+ ss.StatusFlags |= ovrStatus_HmdConnected;
+ return ss;
+}
+
+
+bool HMDState::checkCreateSensor()
+{
+ if (!(SensorStarted && !SensorCreated && AddSensorCount))
+ return false;
+
+ Lock::Locker lockScope(&DevicesLock);
+
+ // Re-check condition once in the lock, in case the state changed.
+ if (SensorStarted && !SensorCreated && AddSensorCount)
+ {
+ if (pHMD)
+ {
+ AddSensorCount = 0;
+ pSensor = *pHMD->GetSensor();
+ }
+
+ if (pSensor)
+ {
+ pSensor->SetReportRate(500);
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetYawCorrectionEnabled((SensorCaps & ovrSensorCap_YawCorrection) != 0);
+ applyProfileToSensorFusion();
+
+#ifdef OVR_CAPI_VISIONSUPPORT
+ if (SensorCaps & ovrSensorCap_Position)
+ {
+ pPoseTracker = new Vision::PoseTracker(SFusion);
+ if (pPoseTracker)
+ {
+ pPoseTracker->AssociateHMD(pSensor);
+ }
+ LogText("Sensor Pose tracker created.\n");
+ }
+#endif // OVR_CAPI_VISION_CODE
+
+ LogText("Sensor created.\n");
+
+ SensorCreated = true;
+ return true;
+ }
+ }
+
+ return SensorCreated;
+}
+
+bool HMDState::GetSensorDesc(ovrSensorDesc* descOut)
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ if (SensorCreated)
+ {
+ OVR_ASSERT(pSensor);
+ OVR::SensorInfo si;
+ pSensor->GetDeviceInfo(&si);
+ descOut->VendorId = si.VendorId;
+ descOut->ProductId = si.ProductId;
+ OVR_ASSERT(si.SerialNumber.GetSize() <= sizeof(descOut->SerialNumber));
+ OVR_strcpy(descOut->SerialNumber, sizeof(descOut->SerialNumber), si.SerialNumber.ToCStr());
+ return true;
+ }
+ return false;
+}
+
+
+void HMDState::applyProfileToSensorFusion()
+{
+ if (!pHMD)
+ return;
+ Profile* profile = pHMD->GetProfile();
+ if (!profile)
+ {
+ OVR_ASSERT(false);
+ return;
+ }
+ SFusion.SetUserHeadDimensions ( *profile, RenderState.RenderInfo );
+}
+
+void HMDState::updateLowPersistenceMode(bool lowPersistence) const
+{
+ OVR_ASSERT(pSensor);
+ DisplayReport dr;
+
+ if (pSensor.GetPtr())
+ {
+ pSensor->GetDisplayReport(&dr);
+
+ dr.Persistence = (UInt16) (dr.TotalRows * (lowPersistence ? 0.18f : 1.0f));
+ dr.Brightness = lowPersistence ? 255 : 0;
+
+ pSensor->SetDisplayReport(dr);
+ }
+}
+
+void HMDState::updateLatencyTestForHmd(bool latencyTesting)
+{
+ if (pSensor.GetPtr())
+ {
+ DisplayReport dr;
+ pSensor->GetDisplayReport(&dr);
+
+ dr.ReadPixel = latencyTesting;
+
+ pSensor->SetDisplayReport(dr);
+ }
+
+ if (latencyTesting)
+ {
+ LatencyUtil2.SetSensorDevice(pSensor.GetPtr());
+ }
+ else
+ {
+ LatencyUtil2.SetSensorDevice(NULL);
+ }
+}
+
+
+void HMDState::updateDK2FeaturesTiedToSensor(bool sensorCreatedJustNow)
+{
+ Lock::Locker lockScope(&DevicesLock);
+
+ if (!SensorCreated || (HMDInfo.HmdType != HmdType_DK2))
+ return;
+
+ // Only send display reports if state changed or sensor initializing first time.
+ if (sensorCreatedJustNow ||
+ ((HmdCapsAppliedToSensor ^ EnabledHmdCaps) & ovrHmdCap_LowPersistence))
+ {
+ updateLowPersistenceMode((EnabledHmdCaps & ovrHmdCap_LowPersistence) ? true : false);
+ }
+
+ if (sensorCreatedJustNow || ((HmdCapsAppliedToSensor ^ EnabledHmdCaps) & ovrHmdCap_LatencyTest))
+ {
+ updateLatencyTestForHmd((EnabledHmdCaps & ovrHmdCap_LatencyTest) != 0);
+ }
+
+ HmdCapsAppliedToSensor = EnabledHmdCaps & (ovrHmdCap_LowPersistence|ovrHmdCap_LatencyTest);
+}
+
+
+
+void HMDState::SetEnabledHmdCaps(unsigned hmdCaps)
+{
+
+ if (HMDInfo.HmdType == HmdType_DK2)
+ {
+ if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_DynamicPrediction)
+ {
+ // DynamicPrediction change
+ TimeManager.ResetFrameTiming(TimeManager.GetFrameTiming().FrameIndex,
+ (hmdCaps & ovrHmdCap_DynamicPrediction) ? true : false,
+ RenderingConfigured);
+ }
+ }
+
+ if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoVSync)
+ {
+ TimeManager.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true);
+ }
+
+
+ EnabledHmdCaps = hmdCaps & ovrHmdCap_Writable_Mask;
+ RenderState.EnabledHmdCaps = EnabledHmdCaps;
+
+ // Unfortunately, LowPersistance and other flags are tied to sensor.
+ // This flag will apply the state of sensor is created; otherwise this will be delayed
+ // till StartSensor.
+ // Such behavior is less then ideal, but should be resolved with the service model.
+
+ updateDK2FeaturesTiedToSensor(false);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Property Access
+
+// TBD: This all needs to be cleaned up and organized into namespaces.
+
+float HMDState::getFloatValue(const char* propertyName, float defaultVal)
+{
+ if (OVR_strcmp(propertyName, "LensSeparation") == 0)
+ {
+ return HMDInfo.LensSeparationInMeters;
+ }
+ else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ return SFusion.GetCenterPupilDepth();
+ }
+ else if (pHMD)
+ {
+ Profile* p = pHMD->GetProfile();
+ if (p)
+ {
+ return p->GetFloatValue(propertyName, defaultVal);
+ }
+ }
+ return defaultVal;
+}
+
+bool HMDState::setFloatValue(const char* propertyName, float value)
+{
+ if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ SFusion.SetCenterPupilDepth(value);
+ return true;
+ }
+ return false;
+}
+
+
+static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize,
+ float source[], unsigned sourceSize)
+{
+ unsigned count = Alg::Min(destSize, sourceSize);
+ for (unsigned i = 0; i < count; i++)
+ dest[i] = source[i];
+ return count;
+}
+
+
+unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsigned arraySize)
+{
+ if (arraySize)
+ {
+ if (OVR_strcmp(propertyName, "ScreenSize") == 0)
+ {
+ float data[2] = { HMDInfo.ScreenSizeInMeters.w, HMDInfo.ScreenSizeInMeters.h };
+
+ return CopyFloatArrayWithLimit(values, arraySize, data, 2);
+ }
+ else if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
+ {
+ return CopyFloatArrayWithLimit(values, arraySize, RenderState.ClearColor, 4);
+ }
+ else if (OVR_strcmp(propertyName, "DK2Latency") == 0)
+ {
+ if (HMDInfo.HmdType != HmdType_DK2)
+ return 0;
+
+ float data[3];
+ TimeManager.GetLatencyTimings(data);
+
+ return CopyFloatArrayWithLimit(values, arraySize, data, 3);
+ }
+
+ /*
+ else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0)
+ {
+ if (arraySize >= 1)
+ {
+ values[0] = SFusion.GetCenterPupilDepth();
+ return 1;
+ }
+ return 0;
+ } */
+ else if (pHMD)
+ {
+ Profile* p = pHMD->GetProfile();
+
+ // TBD: Not quite right. Should update profile interface, so that
+ // we can return 0 in all conditions if property doesn't exist.
+ if (p)
+ {
+ unsigned count = p->GetFloatValues(propertyName, values, arraySize);
+ return count;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned arraySize)
+{
+ if (!arraySize)
+ return false;
+
+ if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
+ {
+ CopyFloatArrayWithLimit(RenderState.ClearColor, 4, values, arraySize);
+ return true;
+ }
+ return false;
+}
+
+
+const char* HMDState::getString(const char* propertyName, const char* defaultVal)
+{
+ if (pHMD)
+ {
+ // For now, just access the profile.
+ Profile* p = pHMD->GetProfile();
+
+ LastGetStringValue[0] = 0;
+ if (p && p->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue)))
+ {
+ return LastGetStringValue;
+ }
+ }
+
+ return defaultVal;
+}
+
+//-------------------------------------------------------------------------------------
+// *** Latency Test
+
+bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3])
+{
+ bool result = false;
+
+ // Check create.
+ if (pLatencyTester)
+ {
+ if (pLatencyTester->IsConnected())
+ {
+ Color colorToDisplay;
+
+ LatencyUtil.ProcessInputs();
+ result = LatencyUtil.DisplayScreenColor(colorToDisplay);
+ rgbColorOut[0] = colorToDisplay.R;
+ rgbColorOut[1] = colorToDisplay.G;
+ rgbColorOut[2] = colorToDisplay.B;
+ }
+ else
+ {
+ // Disconnect.
+ LatencyUtil.SetDevice(NULL);
+ pLatencyTester = 0;
+ LogText("LATENCY SENSOR disconnected.\n");
+ }
+ }
+ else if (AddLatencyTestCount > 0)
+ {
+ // This might have some unlikely race condition issue which could cause us to miss a device...
+ AddLatencyTestCount = 0;
+
+ pLatencyTester = *GlobalState::pInstance->GetManager()->
+ EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTester)
+ {
+ LatencyUtil.SetDevice(pLatencyTester);
+ LogText("LATENCY TESTER connected\n");
+ }
+ }
+
+ return result;
+}
+
+void HMDState::ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime)
+{
+ // Check create.
+ if (!(EnabledHmdCaps & ovrHmdCap_LatencyTest))
+ return;
+
+ if (pLatencyTesterDisplay && !LatencyUtil2.HasDisplayDevice())
+ {
+ if (!pLatencyTesterDisplay->IsConnected())
+ {
+ LatencyUtil2.SetDisplayDevice(NULL);
+ }
+ }
+ else if (AddLatencyTestDisplayCount > 0)
+ {
+ // This might have some unlikely race condition issue
+ // which could cause us to miss a device...
+ AddLatencyTestDisplayCount = 0;
+
+ pLatencyTesterDisplay = *GlobalState::pInstance->GetManager()->
+ EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTesterDisplay)
+ {
+ LatencyUtil2.SetDisplayDevice(pLatencyTesterDisplay);
+ }
+ }
+
+ if (LatencyUtil2.HasDevice() && pSensor && pSensor->IsConnected())
+ {
+ LatencyUtil2.BeginTest(startTime);
+
+ Color colorToDisplay;
+ LatencyTest2Active = LatencyUtil2.DisplayScreenColor(colorToDisplay);
+ rgbColorOut[0] = colorToDisplay.R;
+ rgbColorOut[1] = colorToDisplay.G;
+ rgbColorOut[2] = colorToDisplay.B;
+ }
+ else
+ {
+ LatencyTest2Active = false;
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// *** Rendering
+
+bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrFovPort eyeFovIn[2],
+ const ovrRenderAPIConfig* apiConfig,
+ unsigned distortionCaps)
+{
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_ConfigureRendering");
+
+ // null -> shut down.
+ if (!apiConfig)
+ {
+ if (pRenderer)
+ pRenderer.Clear();
+ RenderingConfigured = false;
+ return true;
+ }
+
+ if (pRenderer &&
+ (apiConfig->Header.API != pRenderer->GetRenderAPI()))
+ {
+ // Shutdown old renderer.
+ if (pRenderer)
+ pRenderer.Clear();
+ }
+
+
+ // Step 1: do basic setup configuration
+ RenderState.setupRenderDesc(eyeRenderDescOut, eyeFovIn);
+ RenderState.EnabledHmdCaps = EnabledHmdCaps; // This is a copy... Any cleaner way?
+ RenderState.DistortionCaps = distortionCaps;
+
+ TimeManager.ResetFrameTiming(0,
+ (EnabledHmdCaps & ovrHmdCap_DynamicPrediction) ? true : false,
+ true);
+
+ LastFrameTimeSeconds = 0.0f;
+
+ // Set RenderingConfigured early to avoid ASSERTs in renderer initialization.
+ RenderingConfigured = true;
+
+ if (!pRenderer)
+ {
+ pRenderer = *DistortionRenderer::APICreateRegistry
+ [apiConfig->Header.API](this, TimeManager, RenderState);
+ }
+
+ if (!pRenderer ||
+ !pRenderer->Initialize(apiConfig, distortionCaps))
+ {
+ RenderingConfigured = false;
+ return false;
+ }
+
+ return true;
+}
+
+
+
+ovrPosef HMDState::BeginEyeRender(ovrEyeType eye)
+{
+ // Debug checks.
+ checkBeginFrameScope("ovrHmd_BeginEyeRender");
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_BeginEyeRender");
+
+ // Unknown eyeId provided in ovrHmd_BeginEyeRender
+ OVR_ASSERT_LOG(eye == ovrEye_Left || eye == ovrEye_Right,
+ ("ovrHmd_BeginEyeRender eyeId out of range."));
+ OVR_ASSERT_LOG(EyeRenderActive[eye] == false,
+ ("Multiple calls to ovrHmd_BeginEyeRender for the same eye."));
+
+ EyeRenderActive[eye] = true;
+
+ // Only process latency tester for drawing the left eye (assumes left eye is drawn first)
+ if (pRenderer && eye == 0)
+ {
+ LatencyTestActive = ProcessLatencyTest(LatencyTestDrawColor);
+ }
+
+ return ovrHmd_GetEyePose(this, eye);
+}
+
+
+void HMDState::EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture)
+{
+ // Debug checks.
+ checkBeginFrameScope("ovrHmd_EndEyeRender");
+ ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_EndEyeRender");
+
+ if (!EyeRenderActive[eye])
+ {
+ OVR_ASSERT_LOG(false,
+ ("ovrHmd_EndEyeRender called without ovrHmd_BeginEyeRender."));
+ return;
+ }
+
+ RenderState.EyeRenderPoses[eye] = renderPose;
+
+ if (pRenderer)
+ pRenderer->SubmitEye(eye, eyeTexture);
+
+ EyeRenderActive[eye] = false;
+}
+
+}} // namespace OVR::CAPI
+
diff --git a/LibOVR/Src/CAPI/CAPI_HMDState.h b/LibOVR/Src/CAPI/CAPI_HMDState.h
new file mode 100644
index 0000000..0a1466f
--- /dev/null
+++ b/LibOVR/Src/CAPI/CAPI_HMDState.h
@@ -0,0 +1,347 @@
+/************************************************************************************
+
+Filename : CAPI_HMDState.h
+Content : State associated with a single HMD
+Created : January 24, 2014
+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.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_HMDState_h
+#define OVR_CAPI_HMDState_h
+
+#include "../Kernel/OVR_Math.h"
+#include "../Kernel/OVR_List.h"
+#include "../Kernel/OVR_Log.h"
+#include "../OVR_CAPI.h"
+#include "../OVR_SensorFusion.h"
+#include "../Util/Util_LatencyTest.h"
+#include "../Util/Util_LatencyTest2.h"
+
+#include "CAPI_FrameTimeManager.h"
+#include "CAPI_HMDRenderState.h"
+#include "CAPI_DistortionRenderer.h"
+
+// Define OVR_CAPI_VISIONSUPPORT to compile in vision support
+#ifdef OVR_CAPI_VISIONSUPPORT
+ #define OVR_CAPI_VISION_CODE(c) c
+ #include "../Vision/Vision_PoseTracker.h"
+#else
+ #define OVR_CAPI_VISION_CODE(c)
+#endif
+
+
+struct ovrHmdStruct { };
+
+namespace OVR { namespace CAPI {
+
+using namespace OVR::Util::Render;
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadChecker
+
+// This helper class is used to verify that the API is used according to supported
+// thread safety constraints (is not re-entrant for this and related functions).
+class ThreadChecker
+{
+public:
+
+#ifndef OVR_BUILD_DEBUG
+
+ // In release build, thread checks are disabled.
+ ThreadChecker() { }
+ void Begin(const char* functionName) { OVR_UNUSED1(functionName); }
+ void End() { }
+
+ // Add thread-re-entrancy check for function scope
+ struct Scope
+ {
+ Scope(ThreadChecker*, const char *) { }
+ ~Scope() { }
+ };
+
+
+#else // OVR_BUILD_DEBUG
+ ThreadChecker() : pFunctionName(0), FirstThread(0)
+ { }
+
+ void Begin(const char* functionName)
+ {
+ if (!pFunctionName)
+ {
+ pFunctionName = functionName;
+ FirstThread = GetCurrentThreadId();
+ }
+ else
+ {
+ // pFunctionName may be not null here if function is called internally on the same thread.
+ OVR_ASSERT_LOG((FirstThread == GetCurrentThreadId()),
+ ("%s (threadId=%p) called at the same times as %s (threadId=%p)\n",
+ functionName, GetCurrentThreadId(), pFunctionName, FirstThread) );
+ }
+ }
+ void End()
+ {
+ pFunctionName = 0;
+ FirstThread = 0;
+ }
+
+ // Add thread-re-entrancy check for function scope.
+ struct Scope
+ {
+ Scope(ThreadChecker* threadChecker, const char *functionName) : pChecker(threadChecker)
+ { pChecker->Begin(functionName); }
+ ~Scope()
+ { pChecker->End(); }
+ private:
+ ThreadChecker* pChecker;
+ };
+
+private:
+ // If not 0, contains the name of the function that first entered the scope.
+ const char * pFunctionName;
+ ThreadId FirstThread;
+
+#endif // OVR_BUILD_DEBUG
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDState
+
+// Describes a single HMD.
+class HMDState : public ListNode<HMDState>,
+ public ovrHmdStruct, public NewOverrideBase
+{
+public:
+
+ HMDState(HMDDevice* device);
+ HMDState(ovrHmdType hmdType);
+ virtual ~HMDState();
+
+
+ // *** Sensor Setup
+
+ bool StartSensor(unsigned supportedCaps, unsigned requiredCaps);
+ void StopSensor();
+ void ResetSensor();
+ ovrSensorState PredictedSensorState(double absTime);
+ bool GetSensorDesc(ovrSensorDesc* descOut);
+
+ // Changes HMD Caps.
+ // Capability bits that are not directly or logically tied to one system (such as sensor)
+ // are grouped here. ovrHmdCap_VSync, for example, affects rendering and timing.
+ void SetEnabledHmdCaps(unsigned caps);
+
+
+ bool ProcessLatencyTest(unsigned char rgbColorOut[3]);
+ void ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime);
+
+
+ // *** Rendering Setup
+
+ bool ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2],
+ const ovrFovPort eyeFovIn[2],
+ const ovrRenderAPIConfig* apiConfig,
+ unsigned distortionCaps);
+
+ ovrPosef BeginEyeRender(ovrEyeType eye);
+ void EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture);
+
+
+ const char* GetLastError()
+ {
+ const char* p = pLastError;
+ pLastError = 0;
+ return p;
+ }
+
+ void NotifyAddDevice(DeviceType deviceType)
+ {
+ if (deviceType == Device_Sensor)
+ AddSensorCount++;
+ else if (deviceType == Device_LatencyTester)
+ {
+ AddLatencyTestCount++;
+ AddLatencyTestDisplayCount++;
+ }
+ }
+
+ bool checkCreateSensor();
+
+ void applyProfileToSensorFusion();
+
+ // INlines so that they can be easily compiled out.
+ // Does debug ASSERT checks for functions that require BeginFrame.
+ // Also verifies that we are on the right thread.
+ void checkBeginFrameScope(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(BeginFrameCalled == true,
+ ("%s called outside ovrHmd_BeginFrame.", functionName));
+ OVR_ASSERT_LOG(BeginFrameThreadId == OVR::GetCurrentThreadId(),
+ ("%s called on a different thread then ovrHmd_BeginFrame.", functionName));
+ }
+
+ void checkRenderingConfigured(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(RenderingConfigured == true,
+ ("%s called without ovrHmd_ConfigureRendering.", functionName));
+ }
+
+ void checkBeginFrameTimingScope(const char* functionName)
+ {
+ OVR_UNUSED1(functionName); // for Release build.
+ OVR_ASSERT_LOG(BeginFrameTimingCalled == true,
+ ("%s called outside ovrHmd_BeginFrameTiming.", functionName));
+ }
+
+
+ HMDState* getThis() { return this; }
+
+ void updateLowPersistenceMode(bool lowPersistence) const;
+ void updateLatencyTestForHmd(bool latencyTesting);
+
+ void updateDK2FeaturesTiedToSensor(bool sensorCreatedJustNow);
+
+ // Get properties by name.
+ float getFloatValue(const char* propertyName, float defaultVal);
+ bool setFloatValue(const char* propertyName, float value);
+ unsigned getFloatArray(const char* propertyName, float values[], unsigned arraySize);
+ bool setFloatArray(const char* propertyName, float values[], unsigned arraySize);
+ const char* getString(const char* propertyName, const char* defaultVal);
+public:
+
+ // Wrapper to support 'const'
+ struct HMDInfoWrapper
+ {
+ HMDInfoWrapper(ovrHmdType hmdType)
+ {
+ HmdTypeEnum t = HmdType_None;
+ if (hmdType == ovrHmd_DK1)
+ t = HmdType_DK1;
+ else if (hmdType == ovrHmd_CrystalCoveProto)
+ t = HmdType_CrystalCoveProto;
+ else if (hmdType == ovrHmd_DK2)
+ t = HmdType_DK2;
+ h = CreateDebugHMDInfo(t);
+ }
+ HMDInfoWrapper(HMDDevice* device) { if (device) device->GetDeviceInfo(&h); }
+ OVR::HMDInfo h;
+ };
+
+ // Note: pHMD can be null if we are representing a virtualized debug HMD.
+ Ptr<HMDDevice> pHMD;
+
+ // HMDInfo shouldn't change, as its string pointers are passed out.
+ const HMDInfoWrapper HMDInfoW;
+ const OVR::HMDInfo& HMDInfo;
+
+ const char* pLastError;
+
+ // Caps enabled for the HMD.
+ unsigned EnabledHmdCaps;
+ // These are the flags actually applied to the Sensor device,
+ // used to track whether SetDisplayReport calls are necessary.
+ unsigned HmdCapsAppliedToSensor;
+
+
+ // *** Sensor
+
+ // Lock used to support thread-safe lifetime access to sensor.
+ Lock DevicesLock;
+
+ // Atomic integer used as a flag that we should check the sensor device.
+ AtomicInt<int> AddSensorCount;
+
+ // All of Sensor variables may be modified/used with DevicesLock, with exception that
+ // the {SensorStarted, SensorCreated} can be read outside the lock to see
+ // if device creation check is necessary.
+ // Whether we called StartSensor() and requested sensor caps.
+ volatile bool SensorStarted;
+ volatile bool SensorCreated;
+ // pSensor may still be null or non-running after start if it wasn't yet available
+ Ptr<SensorDevice> pSensor; // Head
+ unsigned SensorCaps;
+
+ // SensorFusion state may be accessible without a lock.
+ SensorFusion SFusion;
+
+
+ // Vision pose tracker is currently new-allocated
+ OVR_CAPI_VISION_CODE(
+ Vision::PoseTracker* pPoseTracker;
+ )
+
+ // Latency tester
+ Ptr<LatencyTestDevice> pLatencyTester;
+ Util::LatencyTest LatencyUtil;
+ AtomicInt<int> AddLatencyTestCount;
+
+ bool LatencyTestActive;
+ unsigned char LatencyTestDrawColor[3];
+
+ // Using latency tester as debug display
+ Ptr<LatencyTestDevice> pLatencyTesterDisplay;
+ AtomicInt<int> AddLatencyTestDisplayCount;
+ Util::LatencyTest2 LatencyUtil2;
+
+ bool LatencyTest2Active;
+ unsigned char LatencyTest2DrawColor[3];
+ //bool ReadbackColor;
+
+ // Rendering part
+ FrameTimeManager TimeManager;
+ HMDRenderState RenderState;
+ Ptr<DistortionRenderer> pRenderer;
+
+ // Last timing value reported by BeginFrame.
+ double LastFrameTimeSeconds;
+ // Last timing value reported by GetFrameTime. These are separate since the intended
+ // use is from different threads. TBD: Move to FrameTimeManager? Make atomic?
+ double LastGetFrameTimeSeconds;
+
+ // Last cached value returned by ovrHmd_GetString/ovrHmd_GetStringArray.
+ char LastGetStringValue[256];
+
+
+ // Debug flag set after ovrHmd_ConfigureRendering succeeds.
+ bool RenderingConfigured;
+ // Set after BeginFrame succeeds, and its corresponding thread id for debug checks.
+ bool BeginFrameCalled;
+ ThreadId BeginFrameThreadId;
+ // Graphics functions are not re-entrant from other threads.
+ ThreadChecker RenderAPIThreadChecker;
+ //
+ bool BeginFrameTimingCalled;
+
+ // Flags set when we've called BeginEyeRender on a given eye.
+ bool EyeRenderActive[2];
+};
+
+
+}} // namespace OVR::CAPI
+
+
+#endif // OVR_CAPI_HMDState_h
+
+
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
new file mode 100644
index 0000000..21b6509
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : CAPI_GL_DistortionRenderer.h
+Content : Distortion renderer header for GL
+Created : November 11, 2013
+Authors : David Borel, Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#include "CAPI_GL_DistortionRenderer.h"
+
+#include "CAPI_GL_DistortionShaders.h"
+
+#include "../../OVR_CAPI_GL.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+// Distortion pixel shader lookup.
+// Bit 0: Chroma Correction
+// Bit 1: Timewarp
+
+enum {
+ DistortionVertexShaderBitMask = 3,
+ DistortionVertexShaderCount = DistortionVertexShaderBitMask + 1,
+ DistortionPixelShaderBitMask = 1,
+ DistortionPixelShaderCount = DistortionPixelShaderBitMask + 1
+};
+
+struct ShaderInfo
+{
+ const char* ShaderData;
+ size_t ShaderSize;
+ const ShaderBase::Uniform* ReflectionData;
+ size_t ReflectionSize;
+};
+
+// Do add a new distortion shader use these macros (with or w/o reflection)
+#define SI_NOREFL(shader) { shader, sizeof(shader), NULL, 0 }
+#define SI_REFL__(shader) { shader, sizeof(shader), shader ## _refl, sizeof( shader ## _refl )/sizeof(*(shader ## _refl)) }
+
+
+static ShaderInfo DistortionVertexShaderLookup[DistortionVertexShaderCount] =
+{
+ SI_REFL__(Distortion_vs),
+ SI_REFL__(DistortionChroma_vs),
+ SI_REFL__(DistortionTimewarp_vs),
+ SI_REFL__(DistortionTimewarpChroma_vs)
+};
+
+static ShaderInfo DistortionPixelShaderLookup[DistortionPixelShaderCount] =
+{
+ SI_NOREFL(Distortion_fs),
+ SI_NOREFL(DistortionChroma_fs)
+};
+
+void DistortionShaderBitIndexCheck()
+{
+ OVR_COMPILER_ASSERT(ovrDistortionCap_Chromatic == 1);
+ OVR_COMPILER_ASSERT(ovrDistortionCap_TimeWarp == 2);
+}
+
+
+
+struct DistortionVertex
+{
+ Vector2f Pos;
+ Vector2f TexR;
+ Vector2f TexG;
+ Vector2f TexB;
+ Color Col;
+};
+
+
+// Vertex type; same format is used for all shapes for simplicity.
+// Shapes are built by adding vertices to Model.
+struct LatencyVertex
+{
+ Vector3f Pos;
+ LatencyVertex (const Vector3f& p) : Pos(p) {}
+};
+
+
+//----------------------------------------------------------------------------
+// ***** GL::DistortionRenderer
+
+DistortionRenderer::DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+ : CAPI::DistortionRenderer(ovrRenderAPI_OpenGL, hmd, timeManager, renderState)
+ , LatencyVAO(0)
+{
+ DistortionMeshVAOs[0] = 0;
+ DistortionMeshVAOs[1] = 0;
+}
+
+DistortionRenderer::~DistortionRenderer()
+{
+ destroy();
+}
+
+// static
+CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState)
+{
+#if !defined(OVR_OS_MAC)
+ InitGLExtensions();
+#endif
+ return new DistortionRenderer(hmd, timeManager, renderState);
+}
+
+
+bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned distortionCaps)
+{
+ GfxState = *new GraphicsState();
+
+ const ovrGLConfig* config = (const ovrGLConfig*)apiConfig;
+
+ if (!config)
+ {
+ // Cleanup
+ pEyeTextures[0].Clear();
+ pEyeTextures[1].Clear();
+ memset(&RParams, 0, sizeof(RParams));
+ return true;
+ }
+
+ RParams.Multisample = config->OGL.Header.Multisample;
+ RParams.RTSize = config->OGL.Header.RTSize;
+#if defined(OVR_OS_WIN32)
+ RParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow();
+#elif defined(OVR_OS_LINUX)
+ RParams.Disp = (config->OGL.Disp) ? config->OGL.Disp : XOpenDisplay(NULL);
+ RParams.Win = config->OGL.Win;
+ if (!RParams.Win)
+ {
+ int unused;
+ XGetInputFocus(RParams.Disp, &RParams.Win, &unused);
+ }
+#endif
+
+ DistortionCaps = distortionCaps;
+
+ //DistortionWarper.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true);
+
+ pEyeTextures[0] = *new Texture(&RParams, 0, 0);
+ pEyeTextures[1] = *new Texture(&RParams, 0, 0);
+
+ initBuffersAndShaders();
+
+ return true;
+}
+
+
+void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture)
+{
+ // Doesn't do a lot in here??
+ const ovrGLTexture* tex = (const ovrGLTexture*)eyeTexture;
+
+ // Write in values
+ eachEye[eyeId].texture = tex->OGL.TexId;
+
+ if (tex)
+ {
+ // Its only at this point we discover what the viewport of the texture is.
+ // because presumably we allow users to realtime adjust the resolution.
+ eachEye[eyeId].TextureSize = tex->OGL.Header.TextureSize;
+ eachEye[eyeId].RenderViewport = tex->OGL.Header.RenderViewport;
+
+ const ovrEyeRenderDesc& erd = RState.EyeRenderDesc[eyeId];
+
+ ovrHmd_GetRenderScaleAndOffset( erd.Fov,
+ eachEye[eyeId].TextureSize, eachEye[eyeId].RenderViewport,
+ eachEye[eyeId].UVScaleOffset );
+
+ pEyeTextures[eyeId]->UpdatePlaceholderTexture(tex->OGL.TexId,
+ tex->OGL.Header.TextureSize);
+ }
+}
+
+void DistortionRenderer::EndFrame(bool swapBuffers,
+ unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor)
+{
+ if (!TimeManager.NeedDistortionTimeMeasurement())
+ {
+ if (RState.DistortionCaps & ovrDistortionCap_TimeWarp)
+ {
+ // Wait for timewarp distortion if it is time and Gpu idle
+ FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime);
+ }
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+ }
+ else
+ {
+ // If needed, measure distortion time so that TimeManager can better estimate
+ // latency-reducing time-warp wait timing.
+ WaitUntilGpuIdle();
+ double distortionStartTime = ovr_GetTimeInSeconds();
+
+ renderDistortion(pEyeTextures[0], pEyeTextures[1]);
+
+ WaitUntilGpuIdle();
+ TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime);
+ }
+
+ if(latencyTesterDrawColor)
+ {
+ renderLatencyQuad(latencyTesterDrawColor);
+ }
+ else if(latencyTester2DrawColor)
+ {
+ renderLatencyPixel(latencyTester2DrawColor);
+ }
+
+ if (swapBuffers)
+ {
+ bool useVsync = ((RState.EnabledHmdCaps & ovrHmdCap_NoVSync) == 0);
+ int swapInterval = (useVsync) ? 1 : 0;
+#if defined(OVR_OS_WIN32)
+ if (wglGetSwapIntervalEXT() != swapInterval)
+ wglSwapIntervalEXT(swapInterval);
+
+ HDC dc = GetDC(RParams.Window);
+ BOOL success = SwapBuffers(dc);
+ ReleaseDC(RParams.Window, dc);
+ OVR_ASSERT(success);
+ OVR_UNUSED(success);
+#elif defined(OVR_OS_MAC)
+ CGLContextObj context = CGLGetCurrentContext();
+ GLint currentSwapInterval = 0;
+ CGLGetParameter(context, kCGLCPSwapInterval, &currentSwapInterval);
+ if (currentSwapInterval != swapInterval)
+ CGLSetParameter(context, kCGLCPSwapInterval, &swapInterval);
+
+ CGLFlushDrawable(context);
+#elif defined(OVR_OS_LINUX)
+ static const char* extensions = glXQueryExtensionsString(RParams.Disp, 0);
+ static bool supportsVSync = (extensions != NULL && strstr(extensions, "GLX_EXT_swap_control"));
+ if (supportsVSync)
+ {
+ GLuint currentSwapInterval = 0;
+ glXQueryDrawable(RParams.Disp, RParams.Win, GLX_SWAP_INTERVAL_EXT, &currentSwapInterval);
+ if (currentSwapInterval != swapInterval)
+ glXSwapIntervalEXT(RParams.Disp, RParams.Win, swapInterval);
+ }
+
+ glXSwapBuffers(RParams.Disp, RParams.Win);
+#endif
+ }
+}
+
+void DistortionRenderer::WaitUntilGpuIdle()
+{
+ glFlush();
+ glFinish();
+}
+
+double DistortionRenderer::FlushGpuAndWaitTillTime(double absTime)
+{
+ double initialTime = ovr_GetTimeInSeconds();
+ if (initialTime >= absTime)
+ return 0.0;
+
+ glFlush();
+ glFinish();
+
+ double newTime = initialTime;
+ volatile int i;
+
+ while (newTime < absTime)
+ {
+ for (int j = 0; j < 50; j++)
+ i = 0;
+
+ newTime = ovr_GetTimeInSeconds();
+ }
+
+ // How long we waited
+ return newTime - initialTime;
+}
+
+
+DistortionRenderer::GraphicsState::GraphicsState()
+{
+ const char* glVersionString = (const char*)glGetString(GL_VERSION);
+ OVR_DEBUG_LOG(("GL_VERSION STRING: %s", (const char*)glVersionString));
+ char prefix[64];
+ bool foundVersion = false;
+
+ for (int i = 10; i < 30; ++i)
+ {
+ int major = i / 10;
+ int minor = i % 10;
+ OVR_sprintf(prefix, 64, "%d.%d", major, minor);
+ if (strstr(glVersionString, prefix) == glVersionString)
+ {
+ GlMajorVersion = major;
+ GlMinorVersion = minor;
+ foundVersion = true;
+ break;
+ }
+ }
+
+ if (!foundVersion)
+ {
+ glGetIntegerv(GL_MAJOR_VERSION, &GlMajorVersion);
+ glGetIntegerv(GL_MAJOR_VERSION, &GlMinorVersion);
+ }
+
+ OVR_ASSERT(GlMajorVersion >= 2);
+
+ if (GlMajorVersion >= 3)
+ {
+ SupportsVao = true;
+ }
+ else
+ {
+ const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
+ SupportsVao = (strstr("GL_ARB_vertex_array_object", extensions) != NULL);
+ }
+}
+
+
+void DistortionRenderer::GraphicsState::ApplyBool(GLenum Name, GLint Value)
+{
+ if (Value != 0)
+ glEnable(Name);
+ else
+ glDisable(Name);
+}
+
+
+void DistortionRenderer::GraphicsState::Save()
+{
+ glGetIntegerv(GL_VIEWPORT, Viewport);
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, ClearColor);
+ glGetIntegerv(GL_DEPTH_TEST, &DepthTest);
+ glGetIntegerv(GL_CULL_FACE, &CullFace);
+ glGetIntegerv(GL_CURRENT_PROGRAM, &Program);
+ glGetIntegerv(GL_ACTIVE_TEXTURE, &ActiveTexture);
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBinding);
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArray);
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FrameBufferBinding);
+ glGetIntegerv(GL_BLEND, &Blend);
+ glGetIntegerv(GL_COLOR_WRITEMASK, ColorWritemask);
+ glGetIntegerv(GL_DITHER, &Dither);
+ glGetIntegerv(GL_RASTERIZER_DISCARD, &RasterizerDiscard);
+ if (GlMajorVersion >= 3 && GlMajorVersion >= 2)
+ glGetIntegerv(GL_SAMPLE_MASK, &SampleMask);
+ glGetIntegerv(GL_SCISSOR_TEST, &ScissorTest);
+
+ IsValid = true;
+}
+
+
+void DistortionRenderer::GraphicsState::Restore()
+{
+ // Don't allow restore-before-save.
+ if (!IsValid)
+ return;
+
+ glViewport(Viewport[0], Viewport[1], Viewport[2], Viewport[3]);
+ glClearColor(ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]);
+
+ ApplyBool(GL_DEPTH_TEST, DepthTest);
+ ApplyBool(GL_CULL_FACE, CullFace);
+
+ glUseProgram(Program);
+ glActiveTexture(ActiveTexture);
+ glBindTexture(GL_TEXTURE_2D, TextureBinding);
+ if (SupportsVao)
+ glBindVertexArray(VertexArray);
+ glBindFramebuffer(GL_FRAMEBUFFER, FrameBufferBinding);
+
+ ApplyBool(GL_BLEND, Blend);
+
+ glColorMask((GLboolean)ColorWritemask[0], (GLboolean)ColorWritemask[1], (GLboolean)ColorWritemask[2], (GLboolean)ColorWritemask[3]);
+ ApplyBool(GL_DITHER, Dither);
+ ApplyBool(GL_RASTERIZER_DISCARD, RasterizerDiscard);
+ if (GlMajorVersion >= 3 && GlMajorVersion >= 2)
+ ApplyBool(GL_SAMPLE_MASK, SampleMask);
+ ApplyBool(GL_SCISSOR_TEST, ScissorTest);
+}
+
+
+void DistortionRenderer::initBuffersAndShaders()
+{
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ // Allocate & generate distortion mesh vertices.
+ ovrDistortionMesh meshData;
+
+// double startT = ovr_GetTimeInSeconds();
+
+ if (!ovrHmd_CreateDistortionMesh( HMD,
+ RState.EyeRenderDesc[eyeNum].Eye,
+ RState.EyeRenderDesc[eyeNum].Fov,
+ RState.DistortionCaps,
+ &meshData) )
+ {
+ OVR_ASSERT(false);
+ continue;
+ }
+
+ // Now parse the vertex data and create a render ready vertex buffer from it
+ DistortionVertex * pVBVerts = (DistortionVertex*)OVR_ALLOC ( sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionVertex * pCurVBVert = pVBVerts;
+ ovrDistortionVertex* pCurOvrVert = meshData.pVertexData;
+
+ for ( unsigned vertNum = 0; vertNum < meshData.VertexCount; vertNum++ )
+ {
+ pCurVBVert->Pos.x = pCurOvrVert->Pos.x;
+ pCurVBVert->Pos.y = pCurOvrVert->Pos.y;
+ pCurVBVert->TexR = (*(Vector2f*)&pCurOvrVert->TexR);
+ pCurVBVert->TexG = (*(Vector2f*)&pCurOvrVert->TexG);
+ pCurVBVert->TexB = (*(Vector2f*)&pCurOvrVert->TexB);
+ // Convert [0.0f,1.0f] to [0,255]
+ pCurVBVert->Col.R = (OVR::UByte)( pCurOvrVert->VignetteFactor * 255.99f );
+ pCurVBVert->Col.G = pCurVBVert->Col.R;
+ pCurVBVert->Col.B = pCurVBVert->Col.R;
+ pCurVBVert->Col.A = (OVR::UByte)( pCurOvrVert->TimeWarpFactor * 255.99f );;
+ pCurOvrVert++;
+ pCurVBVert++;
+ }
+
+ DistortionMeshVBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshVBs[eyeNum]->Data ( Buffer_Vertex | Buffer_ReadOnly, pVBVerts, sizeof(DistortionVertex) * meshData.VertexCount );
+ DistortionMeshIBs[eyeNum] = *new Buffer(&RParams);
+ DistortionMeshIBs[eyeNum]->Data ( Buffer_Index | Buffer_ReadOnly, meshData.pIndexData, ( sizeof(SInt16) * meshData.IndexCount ) );
+
+ OVR_FREE ( pVBVerts );
+ ovrHmd_DestroyDistortionMesh( &meshData );
+ }
+
+ initShaders();
+}
+
+void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture)
+{
+ GraphicsState* glState = (GraphicsState*)GfxState.GetPtr();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ setViewport( Recti(0,0, RParams.RTSize.w, RParams.RTSize.h) );
+
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ glDisable(GL_DITHER);
+ glDisable(GL_RASTERIZER_DISCARD);
+ if (glState->GlMajorVersion >= 3 && glState->GlMajorVersion >= 2)
+ glDisable(GL_SAMPLE_MASK);
+ glDisable(GL_SCISSOR_TEST);
+
+ glClearColor(
+ RState.ClearColor[0],
+ RState.ClearColor[1],
+ RState.ClearColor[2],
+ RState.ClearColor[3] );
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ ShaderFill distortionShaderFill(DistortionShader);
+ distortionShaderFill.SetTexture(0, eyeNum == 0 ? leftEyeTexture : rightEyeTexture);
+
+ DistortionShader->SetUniform2f("EyeToSourceUVScale", eachEye[eyeNum].UVScaleOffset[0].x, eachEye[eyeNum].UVScaleOffset[0].y);
+ DistortionShader->SetUniform2f("EyeToSourceUVOffset", eachEye[eyeNum].UVScaleOffset[1].x, eachEye[eyeNum].UVScaleOffset[1].y);
+
+ if (DistortionCaps & ovrDistortionCap_TimeWarp)
+ {
+ ovrMatrix4f timeWarpMatrices[2];
+ ovrHmd_GetEyeTimewarpMatrices(HMD, (ovrEyeType)eyeNum,
+ RState.EyeRenderPoses[eyeNum], timeWarpMatrices);
+
+ // Feed identity like matrices in until we get proper timewarp calculation going on
+ DistortionShader->SetUniform4x4f("EyeRotationStart", Matrix4f(timeWarpMatrices[0]).Transposed());
+ DistortionShader->SetUniform4x4f("EyeRotationEnd", Matrix4f(timeWarpMatrices[1]).Transposed());
+
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], true);
+ }
+ else
+ {
+ renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum],
+ 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], true);
+ }
+ }
+}
+
+void DistortionRenderer::createDrawQuad()
+{
+ const int numQuadVerts = 4;
+ LatencyTesterQuadVB = *new Buffer(&RParams);
+ if(!LatencyTesterQuadVB)
+ {
+ return;
+ }
+
+ LatencyTesterQuadVB->Data(Buffer_Vertex, NULL, numQuadVerts * sizeof(LatencyVertex));
+ LatencyVertex* vertices = (LatencyVertex*)LatencyTesterQuadVB->Map(0, numQuadVerts * sizeof(LatencyVertex), Map_Discard);
+ if(!vertices)
+ {
+ OVR_ASSERT(false); // failed to lock vertex buffer
+ return;
+ }
+
+ const float left = -1.0f;
+ const float top = -1.0f;
+ const float right = 1.0f;
+ const float bottom = 1.0f;
+
+ vertices[0] = LatencyVertex(Vector3f(left, top, 0.0f));
+ vertices[1] = LatencyVertex(Vector3f(left, bottom, 0.0f));
+ vertices[2] = LatencyVertex(Vector3f(right, top, 0.0f));
+ vertices[3] = LatencyVertex(Vector3f(right, bottom, 0.0f));
+
+ LatencyTesterQuadVB->Unmap(vertices);
+}
+
+void DistortionRenderer::renderLatencyQuad(unsigned char* latencyTesterDrawColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+ //quadFill.SetInputLayout(SimpleQuadVertexIL);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform2f("Scale", 0.2f, 0.2f);
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ (float)latencyTesterDrawColor[0] / 255.99f,
+ 1.0f);
+
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ SimpleQuadShader->SetUniform2f("PositionOffset", eyeNum == 0 ? -0.4f : 0.4f, 0.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false);
+ }
+}
+
+void DistortionRenderer::renderLatencyPixel(unsigned char* latencyTesterPixelColor)
+{
+ const int numQuadVerts = 4;
+
+ if(!LatencyTesterQuadVB)
+ {
+ createDrawQuad();
+ }
+
+ ShaderFill quadFill(SimpleQuadShader);
+
+ setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h));
+
+ SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ (float)latencyTesterPixelColor[0] / 255.99f,
+ 1.0f);
+
+ Vector2f scale(2.0f / RParams.RTSize.w, 2.0f / RParams.RTSize.h);
+ SimpleQuadShader->SetUniform2f("Scale", scale.x, scale.y);
+ SimpleQuadShader->SetUniform2f("PositionOffset", 1.0f, 1.0f);
+ renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false);
+}
+
+void DistortionRenderer::renderPrimitives(
+ const ShaderFill* fill,
+ Buffer* vertices, Buffer* indices,
+ int offset, int count,
+ PrimitiveType rprim, GLuint* vao, bool isDistortionMesh)
+{
+ GraphicsState* glState = (GraphicsState*)GfxState.GetPtr();
+
+ GLenum prim;
+ switch (rprim)
+ {
+ case Prim_Triangles:
+ prim = GL_TRIANGLES;
+ break;
+ case Prim_Lines:
+ prim = GL_LINES;
+ break;
+ case Prim_TriangleStrip:
+ prim = GL_TRIANGLE_STRIP;
+ break;
+ default:
+ OVR_ASSERT(false);
+ return;
+ }
+
+ fill->Set();
+
+ GLuint prog = fill->GetShaders()->Prog;
+
+ if (vao != NULL)
+ {
+ if (*vao != 0)
+ {
+ glBindVertexArray(*vao);
+
+ if (isDistortionMesh)
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ else
+ glDrawArrays(prim, 0, count);
+ }
+ else
+ {
+ if (glState->SupportsVao)
+ {
+ glGenVertexArrays(1, vao);
+ glBindVertexArray(*vao);
+ }
+
+ int attributeCount = (isDistortionMesh) ? 5 : 1;
+ int* locs = new int[attributeCount];
+
+ glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer);
+
+ if (isDistortionMesh)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer);
+
+ locs[0] = glGetAttribLocation(prog, "Position");
+ locs[1] = glGetAttribLocation(prog, "Color");
+ locs[2] = glGetAttribLocation(prog, "TexCoord0");
+ locs[3] = glGetAttribLocation(prog, "TexCoord1");
+ locs[4] = glGetAttribLocation(prog, "TexCoord2");
+
+ glVertexAttribPointer(locs[0], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, Pos));
+ glVertexAttribPointer(locs[1], 4, GL_UNSIGNED_BYTE, true, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, Col));
+ glVertexAttribPointer(locs[2], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexR));
+ glVertexAttribPointer(locs[3], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexG));
+ glVertexAttribPointer(locs[4], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast<char*>(offset)+offsetof(DistortionVertex, TexB));
+ }
+ else
+ {
+ locs[0] = glGetAttribLocation(prog, "Position");
+
+ glVertexAttribPointer(locs[0], 3, GL_FLOAT, false, sizeof(LatencyVertex), reinterpret_cast<char*>(offset)+offsetof(LatencyVertex, Pos));
+ }
+
+ for (int i = 0; i < attributeCount; ++i)
+ glEnableVertexAttribArray(locs[i]);
+
+ if (isDistortionMesh)
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ else
+ glDrawArrays(prim, 0, count);
+
+
+ if (!glState->SupportsVao)
+ {
+ for (int i = 0; i < attributeCount; ++i)
+ glDisableVertexAttribArray(locs[i]);
+ }
+
+ delete[] locs;
+ }
+ }
+}
+
+void DistortionRenderer::setViewport(const Recti& vp)
+{
+ glViewport(vp.x, vp.y, vp.w, vp.h);
+}
+
+
+void DistortionRenderer::initShaders()
+{
+ GraphicsState* glState = (GraphicsState*)GfxState.GetPtr();
+
+ const char* shaderPrefix =
+ (glState->GlMajorVersion < 3 || (glState->GlMajorVersion == 3 && glState->GlMinorVersion < 2)) ?
+ glsl2Prefix : glsl3Prefix;
+
+ {
+ ShaderInfo vsInfo = DistortionVertexShaderLookup[DistortionVertexShaderBitMask & DistortionCaps];
+
+ size_t vsSize = strlen(shaderPrefix)+vsInfo.ShaderSize;
+ char* vsSource = new char[vsSize];
+ OVR_strcpy(vsSource, vsSize, shaderPrefix);
+ OVR_strcat(vsSource, vsSize, vsInfo.ShaderData);
+
+ Ptr<GL::VertexShader> vs = *new GL::VertexShader(
+ &RParams,
+ (void*)vsSource, vsSize,
+ vsInfo.ReflectionData, vsInfo.ReflectionSize);
+
+ DistortionShader = *new ShaderSet;
+ DistortionShader->SetShader(vs);
+
+ delete[](vsSource);
+
+ ShaderInfo psInfo = DistortionPixelShaderLookup[DistortionPixelShaderBitMask & DistortionCaps];
+
+ size_t psSize = strlen(shaderPrefix)+psInfo.ShaderSize;
+ char* psSource = new char[psSize];
+ OVR_strcpy(psSource, psSize, shaderPrefix);
+ OVR_strcat(psSource, psSize, psInfo.ShaderData);
+
+ Ptr<GL::FragmentShader> ps = *new GL::FragmentShader(
+ &RParams,
+ (void*)psSource, psSize,
+ psInfo.ReflectionData, psInfo.ReflectionSize);
+
+ DistortionShader->SetShader(ps);
+
+ delete[](psSource);
+ }
+ {
+ size_t vsSize = strlen(shaderPrefix)+sizeof(SimpleQuad_vs);
+ char* vsSource = new char[vsSize];
+ OVR_strcpy(vsSource, vsSize, shaderPrefix);
+ OVR_strcat(vsSource, vsSize, SimpleQuad_vs);
+
+ Ptr<GL::VertexShader> vs = *new GL::VertexShader(
+ &RParams,
+ (void*)vsSource, vsSize,
+ SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0]));
+
+ SimpleQuadShader = *new ShaderSet;
+ SimpleQuadShader->SetShader(vs);
+
+ delete[](vsSource);
+
+ size_t psSize = strlen(shaderPrefix)+sizeof(SimpleQuad_fs);
+ char* psSource = new char[psSize];
+ OVR_strcpy(psSource, psSize, shaderPrefix);
+ OVR_strcat(psSource, psSize, SimpleQuad_fs);
+
+ Ptr<GL::FragmentShader> ps = *new GL::FragmentShader(
+ &RParams,
+ (void*)psSource, psSize,
+ SimpleQuad_fs_refl, sizeof(SimpleQuad_fs_refl) / sizeof(SimpleQuad_fs_refl[0]));
+
+ SimpleQuadShader->SetShader(ps);
+
+ delete[](psSource);
+ }
+}
+
+
+void DistortionRenderer::destroy()
+{
+ GraphicsState* glState = (GraphicsState*)GfxState.GetPtr();
+
+ for(int eyeNum = 0; eyeNum < 2; eyeNum++)
+ {
+ if (glState->SupportsVao)
+ glDeleteVertexArrays(1, &DistortionMeshVAOs[eyeNum]);
+
+ DistortionMeshVAOs[eyeNum] = 0;
+
+ DistortionMeshVBs[eyeNum].Clear();
+ DistortionMeshIBs[eyeNum].Clear();
+ }
+
+ if (DistortionShader)
+ {
+ DistortionShader->UnsetShader(Shader_Vertex);
+ DistortionShader->UnsetShader(Shader_Pixel);
+ DistortionShader.Clear();
+ }
+
+ LatencyTesterQuadVB.Clear();
+ LatencyVAO = 0;
+}
+
+}}} // OVR::CAPI::GL
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h
new file mode 100644
index 0000000..60f1a9f
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h
@@ -0,0 +1,178 @@
+/************************************************************************************
+
+Filename : CAPI_GL_DistortionRenderer.h
+Content : Distortion renderer header for GL
+Created : November 11, 2013
+Authors : David Borel, Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+
+#ifndef OVR_CAPI_GL_DistortionRenderer_h
+#define OVR_CAPI_GL_DistortionRenderer_h
+
+#include "../CAPI_DistortionRenderer.h"
+
+#include "../../Kernel/OVR_Log.h"
+#include "CAPI_GL_Util.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+// ***** GL::DistortionRenderer
+
+// Implementation of DistortionRenderer for GL.
+
+class DistortionRenderer : public CAPI::DistortionRenderer
+{
+public:
+ DistortionRenderer(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+ ~DistortionRenderer();
+
+
+ // Creation function for the device.
+ static CAPI::DistortionRenderer* Create(ovrHmd hmd,
+ FrameTimeManager& timeManager,
+ const HMDRenderState& renderState);
+
+
+ // ***** Public DistortionRenderer interface
+
+ virtual bool Initialize(const ovrRenderAPIConfig* apiConfig,
+ unsigned distortionCaps);
+
+ virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture);
+
+ virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor);
+
+ void WaitUntilGpuIdle();
+
+ // Similar to ovr_WaitTillTime but it also flushes GPU.
+ // Note, it exits when time expires, even if GPU is not in idle state yet.
+ double FlushGpuAndWaitTillTime(double absTime);
+
+protected:
+
+
+ class GraphicsState : public CAPI::DistortionRenderer::GraphicsState
+ {
+ public:
+ GraphicsState();
+ virtual void Save();
+ virtual void Restore();
+
+ protected:
+ void ApplyBool(GLenum Name, GLint Value);
+
+ public:
+ GLint GlMajorVersion;
+ GLint GlMinorVersion;
+ bool SupportsVao;
+
+ GLint Viewport[4];
+ GLfloat ClearColor[4];
+ GLint DepthTest;
+ GLint CullFace;
+ GLint Program;
+ GLint ActiveTexture;
+ GLint TextureBinding;
+ GLint VertexArray;
+ GLint FrameBufferBinding;
+
+ GLint Blend;
+ GLint ColorWritemask[4];
+ GLint Dither;
+ GLint Fog;
+ GLint Lighting;
+ GLint RasterizerDiscard;
+ GLint RenderMode;
+ GLint SampleMask;
+ GLint ScissorTest;
+ GLfloat ZoomX;
+ GLfloat ZoomY;
+ };
+
+ // TBD: Should we be using oe from RState instead?
+ unsigned DistortionCaps;
+
+ struct FOR_EACH_EYE
+ {
+ FOR_EACH_EYE() : TextureSize(0), RenderViewport(Sizei(0)) { }
+
+#if 0
+ IDirect3DVertexBuffer9 * dxVerts;
+ IDirect3DIndexBuffer9 * dxIndices;
+#endif
+ int numVerts;
+ int numIndices;
+
+ GLuint texture;
+
+ ovrVector2f UVScaleOffset[2];
+ Sizei TextureSize;
+ Recti RenderViewport;
+ } eachEye[2];
+
+ // GL context and utility variables.
+ RenderParams RParams;
+
+ // Helpers
+ void initBuffersAndShaders();
+ void initShaders();
+ void initFullscreenQuad();
+ void destroy();
+
+ void setViewport(const Recti& vp);
+
+ void renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture);
+
+ void renderPrimitives(const ShaderFill* fill, Buffer* vertices, Buffer* indices,
+ int offset, int count,
+ PrimitiveType rprim, GLuint* vao, bool isDistortionMesh);
+
+ void createDrawQuad();
+ void renderLatencyQuad(unsigned char* latencyTesterDrawColor);
+ void renderLatencyPixel(unsigned char* latencyTesterPixelColor);
+
+ Ptr<Texture> pEyeTextures[2];
+
+ Ptr<Buffer> DistortionMeshVBs[2]; // one per-eye
+ Ptr<Buffer> DistortionMeshIBs[2]; // one per-eye
+ GLuint DistortionMeshVAOs[2]; // one per-eye
+
+ Ptr<ShaderSet> DistortionShader;
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+
+ GLuint LatencyVAO;
+ Ptr<Buffer> LatencyTesterQuadVB;
+ Ptr<ShaderSet> SimpleQuadShader;
+
+ Ptr<Texture> CurRenderTarget;
+ Array<Ptr<Texture> > DepthBuffers;
+ GLuint CurrentFbo;
+
+ GLint SavedViewport[4];
+ GLfloat SavedClearColor[4];
+ GLint SavedDepthTest;
+ GLint SavedCullFace;
+ GLint SavedProgram;
+ GLint SavedActiveTexture;
+ GLint SavedBoundTexture;
+ GLint SavedVertexArray;
+ GLint SavedBoundFrameBuffer;
+};
+
+}}} // OVR::CAPI::GL
+
+#endif // OVR_CAPI_GL_DistortionRenderer_h \ No newline at end of file
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h
new file mode 100644
index 0000000..03fd174
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h
@@ -0,0 +1,326 @@
+/************************************************************************************
+
+ Filename : CAPI_GL_Shaders.h
+ Content : Distortion shader header for GL
+ Created : November 11, 2013
+ Authors : David Borel, Volga Aksoy
+
+ Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+ Use of this software is subject to the terms of the Oculus Inc license
+ agreement provided at the time of installation or download, or which
+ otherwise accompanies this software in either electronic or hard copy form.
+
+ ************************************************************************************/
+
+
+#ifndef OVR_CAPI_GL_Shaders_h
+#define OVR_CAPI_GL_Shaders_h
+
+
+#include "CAPI_GL_Util.h"
+
+namespace OVR { namespace CAPI { namespace GL {
+
+ static const char glsl2Prefix[] =
+ "#version 110\n"
+ "#extension GL_ARB_shader_texture_lod : enable\n"
+ "#define _FRAGCOLOR_DECLARATION\n"
+ "#define _VS_IN attribute\n"
+ "#define _VS_OUT varying\n"
+ "#define _FS_IN varying\n"
+ "#define _TEXTURELOD texture2DLod\n"
+ "#define _FRAGCOLOR gl_FragColor\n";
+
+ static const char glsl3Prefix[] =
+ "#version 150\n"
+ "#define _FRAGCOLOR_DECLARATION out vec4 FragColor;\n"
+ "#define _VS_IN in\n"
+ "#define _VS_OUT out\n"
+ "#define _FS_IN in\n"
+ "#define _TEXTURELOD textureLod\n"
+ "#define _FRAGCOLOR FragColor\n";
+
+ static const char SimpleQuad_vs[] =
+ "uniform vec2 PositionOffset;\n"
+ "uniform vec2 Scale;\n"
+
+ "_VS_IN vec3 Position;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(Position.xy * Scale + PositionOffset, 0.5, 1.0);\n"
+ "}\n";
+
+ const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_vs_refl[] =
+ {
+ { "PositionOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "Scale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ };
+
+ static const char SimpleQuad_fs[] =
+ "uniform vec4 Color;\n"
+
+ "_FRAGCOLOR_DECLARATION\n"
+
+ "void main()\n"
+ "{\n"
+ " _FRAGCOLOR = Color;\n"
+ "}\n";
+
+ const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_fs_refl[] =
+ {
+ { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 },
+ };
+
+
+ static const char Distortion_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+
+ "_VS_IN vec2 Position;\n"
+ "_VS_IN vec4 Color;\n"
+ "_VS_IN vec2 TexCoord0;\n"
+
+ "_VS_OUT vec4 oColor;\n"
+ "_VS_OUT vec2 oTexCoord0;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.5;\n"
+ " gl_Position.w = 1.0;\n"
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0.y = 1.0 - oTexCoord0.y;\n"
+ " oColor = Color;\n" // Used for vignette fade.
+ "}\n";
+
+ const OVR::CAPI::GL::ShaderBase::Uniform Distortion_vs_refl[] =
+ {
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ };
+
+ static const char Distortion_fs[] =
+ "uniform sampler2D Texture0;\n"
+
+ "_FS_IN vec4 oColor;\n"
+ "_FS_IN vec2 oTexCoord0;\n"
+
+ "_FRAGCOLOR_DECLARATION\n"
+
+ "void main()\n"
+ "{\n"
+ " _FRAGCOLOR = _TEXTURELOD(Texture0, oTexCoord0, 0.0);\n"
+ " _FRAGCOLOR.a = 1.0;\n"
+ "}\n";
+
+
+ static const char DistortionTimewarp_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "_VS_IN vec2 Position;\n"
+ "_VS_IN vec4 Color;\n"
+ "_VS_IN vec2 TexCoord0;\n"
+
+ "_FS_IN vec4 oColor;\n"
+ "_FS_IN vec2 oTexCoord0;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ " vec3 TanEyeAngle = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n"
+
+ // Accurate time warp lerp vs. faster
+#if 1
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ " vec3 TransformedStart = (EyeRotationStart * vec4(TanEyeAngle, 0)).xyz;\n"
+ " vec3 TransformedEnd = (EyeRotationEnd * vec4(TanEyeAngle, 0)).xyz;\n"
+ // And blend between them.
+ " vec3 Transformed = mix ( TransformedStart, TransformedEnd, Color.a );\n"
+#else
+ " mat4 EyeRotation = mix ( EyeRotationStart, EyeRotationEnd, Color.a );\n"
+ " vec3 Transformed = EyeRotation * TanEyeAngle;\n"
+#endif
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ " float RecipZ = 1.0 / Transformed.z;\n"
+ " vec2 Flattened = vec2 ( Transformed.x * RecipZ, Transformed.y * RecipZ );\n"
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " vec2 SrcCoord = Flattened * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0 = SrcCoord;\n"
+ " oTexCoord0.y = 1.0-oTexCoord0.y;\n"
+ " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade.
+ "}\n";
+
+
+ const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarp_vs_refl[] =
+ {
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ };
+
+ static const char DistortionChroma_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+
+ "_VS_IN vec2 Position;\n"
+ "_VS_IN vec4 Color;\n"
+ "_VS_IN vec2 TexCoord0;\n"
+ "_VS_IN vec2 TexCoord1;\n"
+ "_VS_IN vec2 TexCoord2;\n"
+
+ "_VS_OUT vec4 oColor;\n"
+ "_VS_OUT vec2 oTexCoord0;\n"
+ "_VS_OUT vec2 oTexCoord1;\n"
+ "_VS_OUT vec2 oTexCoord2;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.5;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord0.y = 1.0-oTexCoord0.y;\n"
+ " oTexCoord1 = TexCoord1 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord1.y = 1.0-oTexCoord1.y;\n"
+ " oTexCoord2 = TexCoord2 * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " oTexCoord2.y = 1.0-oTexCoord2.y;\n"
+
+ " oColor = Color;\n" // Used for vignette fade.
+ "}\n";
+
+ const OVR::CAPI::GL::ShaderBase::Uniform DistortionChroma_vs_refl[] =
+ {
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ };
+
+ static const char DistortionChroma_fs[] =
+ "uniform sampler2D Texture0;\n"
+
+ "_FS_IN vec4 oColor;\n"
+ "_FS_IN vec2 oTexCoord0;\n"
+ "_FS_IN vec2 oTexCoord1;\n"
+ "_FS_IN vec2 oTexCoord2;\n"
+
+ "_FRAGCOLOR_DECLARATION\n"
+
+ "void main()\n"
+ "{\n"
+ " float ResultR = _TEXTURELOD(Texture0, oTexCoord0, 0.0).r;\n"
+ " float ResultG = _TEXTURELOD(Texture0, oTexCoord1, 0.0).g;\n"
+ " float ResultB = _TEXTURELOD(Texture0, oTexCoord2, 0.0).b;\n"
+
+ " _FRAGCOLOR = vec4(ResultR * oColor.r, ResultG * oColor.g, ResultB * oColor.b, 1.0);\n"
+ "}\n";
+
+
+ static const char DistortionTimewarpChroma_vs[] =
+ "uniform vec2 EyeToSourceUVScale;\n"
+ "uniform vec2 EyeToSourceUVOffset;\n"
+ "uniform mat4 EyeRotationStart;\n"
+ "uniform mat4 EyeRotationEnd;\n"
+
+ "_VS_IN vec2 Position;\n"
+ "_VS_IN vec4 Color;\n"
+ "_VS_IN vec2 TexCoord0;\n"
+ "_VS_IN vec2 TexCoord1;\n"
+ "_VS_IN vec2 TexCoord2;\n"
+
+ "_VS_OUT vec4 oColor;\n"
+ "_VS_OUT vec2 oTexCoord0;\n"
+ "_VS_OUT vec2 oTexCoord1;\n"
+ "_VS_OUT vec2 oTexCoord2;\n"
+
+ "void main()\n"
+ "{\n"
+ " gl_Position.x = Position.x;\n"
+ " gl_Position.y = Position.y;\n"
+ " gl_Position.z = 0.0;\n"
+ " gl_Position.w = 1.0;\n"
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ " vec3 TanEyeAngleR = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n"
+ " vec3 TanEyeAngleG = vec3 ( TexCoord1.x, TexCoord1.y, 1.0 );\n"
+ " vec3 TanEyeAngleB = vec3 ( TexCoord2.x, TexCoord2.y, 1.0 );\n"
+
+ // Accurate time warp lerp vs. faster
+#if 1
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ " vec3 TransformedRStart = (EyeRotationStart * vec4(TanEyeAngleR, 0)).xyz;\n"
+ " vec3 TransformedGStart = (EyeRotationStart * vec4(TanEyeAngleG, 0)).xyz;\n"
+ " vec3 TransformedBStart = (EyeRotationStart * vec4(TanEyeAngleB, 0)).xyz;\n"
+ " vec3 TransformedREnd = (EyeRotationEnd * vec4(TanEyeAngleR, 0)).xyz;\n"
+ " vec3 TransformedGEnd = (EyeRotationEnd * vec4(TanEyeAngleG, 0)).xyz;\n"
+ " vec3 TransformedBEnd = (EyeRotationEnd * vec4(TanEyeAngleB, 0)).xyz;\n"
+
+ // And blend between them.
+ " vec3 TransformedR = mix ( TransformedRStart, TransformedREnd, Color.a );\n"
+ " vec3 TransformedG = mix ( TransformedGStart, TransformedGEnd, Color.a );\n"
+ " vec3 TransformedB = mix ( TransformedBStart, TransformedBEnd, Color.a );\n"
+#else
+ " mat3 EyeRotation;\n"
+ " EyeRotation[0] = mix ( EyeRotationStart[0], EyeRotationEnd[0], Color.a ).xyz;\n"
+ " EyeRotation[1] = mix ( EyeRotationStart[1], EyeRotationEnd[1], Color.a ).xyz;\n"
+ " EyeRotation[2] = mix ( EyeRotationStart[2], EyeRotationEnd[2], Color.a ).xyz;\n"
+ " vec3 TransformedR = EyeRotation * TanEyeAngleR;\n"
+ " vec3 TransformedG = EyeRotation * TanEyeAngleG;\n"
+ " vec3 TransformedB = EyeRotation * TanEyeAngleB;\n"
+#endif
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ " float RecipZR = 1.0 / TransformedR.z;\n"
+ " float RecipZG = 1.0 / TransformedG.z;\n"
+ " float RecipZB = 1.0 / TransformedB.z;\n"
+ " vec2 FlattenedR = vec2 ( TransformedR.x * RecipZR, TransformedR.y * RecipZR );\n"
+ " vec2 FlattenedG = vec2 ( TransformedG.x * RecipZG, TransformedG.y * RecipZG );\n"
+ " vec2 FlattenedB = vec2 ( TransformedB.x * RecipZB, TransformedB.y * RecipZB );\n"
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ " vec2 SrcCoordR = FlattenedR * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " vec2 SrcCoordG = FlattenedG * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+ " vec2 SrcCoordB = FlattenedB * EyeToSourceUVScale + EyeToSourceUVOffset;\n"
+
+ " oTexCoord0 = SrcCoordR;\n"
+ " oTexCoord0.y = 1.0-oTexCoord0.y;\n"
+ " oTexCoord1 = SrcCoordG;\n"
+ " oTexCoord1.y = 1.0-oTexCoord1.y;\n"
+ " oTexCoord2 = SrcCoordB;\n"
+ " oTexCoord2.y = 1.0-oTexCoord2.y;\n"
+
+ " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade.
+ "}\n";
+
+
+ const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarpChroma_vs_refl[] =
+ {
+ { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 },
+ { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 },
+ { "EyeRotationStart", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 16, 64 },
+ { "EyeRotationEnd", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 80, 64 },
+ };
+
+}}} // OVR::CAPI::GL
+
+#endif // OVR_CAPI_GL_Shaders_h
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp
new file mode 100644
index 0000000..910e28c
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp
@@ -0,0 +1,530 @@
+/************************************************************************************
+
+Filename : Render_GL_Device.cpp
+Content : RenderDevice implementation for OpenGL
+Created : September 10, 2012
+Authors : David Borel, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+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 "CAPI_GL_Util.h"
+#include "../../Kernel/OVR_Log.h"
+#include <string.h>
+
+namespace OVR { namespace CAPI { namespace GL {
+
+
+
+// GL Hooks for non-Mac.
+#if !defined(OVR_OS_MAC)
+
+#if defined(OVR_OS_WIN32)
+
+PFNWGLGETPROCADDRESS wglGetProcAddress;
+
+PFNGLENABLEPROC glEnable;
+PFNGLDISABLEPROC glDisable;
+PFNGLGETFLOATVPROC glGetFloatv;
+PFNGLGETINTEGERVPROC glGetIntegerv;
+PFNGLGETSTRINGPROC glGetString;
+PFNGLCOLORMASKPROC glColorMask;
+PFNGLCLEARPROC glClear;
+PFNGLCLEARCOLORPROC glClearColor;
+PFNGLCLEARDEPTHPROC glClearDepth;
+PFNGLVIEWPORTPROC glViewport;
+PFNGLDRAWELEMENTSPROC glDrawElements;
+PFNGLTEXPARAMETERIPROC glTexParameteri;
+PFNGLFLUSHPROC glFlush;
+PFNGLFINISHPROC glFinish;
+PFNGLDRAWARRAYSPROC glDrawArrays;
+PFNGLGENTEXTURESPROC glGenTextures;
+PFNGLDELETETEXTURESPROC glDeleteTextures;
+PFNGLBINDTEXTUREPROC glBindTexture;
+
+PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
+
+#elif defined(OVR_OS_LINUX)
+
+PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
+
+#endif
+
+PFNGLDELETESHADERPROC glDeleteShader;
+PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
+PFNGLACTIVETEXTUREPROC glActiveTexture;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+PFNGLBINDBUFFERPROC glBindBuffer;
+PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv;
+PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+PFNGLBUFFERDATAPROC glBufferData;
+PFNGLGENBUFFERSPROC glGenBuffers;
+PFNGLMAPBUFFERPROC glMapBuffer;
+PFNGLUNMAPBUFFERPROC glUnmapBuffer;
+PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
+PFNGLGETSHADERIVPROC glGetShaderiv;
+PFNGLCOMPILESHADERPROC glCompileShader;
+PFNGLSHADERSOURCEPROC glShaderSource;
+PFNGLCREATESHADERPROC glCreateShader;
+PFNGLCREATEPROGRAMPROC glCreateProgram;
+PFNGLATTACHSHADERPROC glAttachShader;
+PFNGLDETACHSHADERPROC glDetachShader;
+PFNGLDELETEPROGRAMPROC glDeleteProgram;
+PFNGLUNIFORM1IPROC glUniform1i;
+PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
+PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
+PFNGLUSEPROGRAMPROC glUseProgram;
+PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
+PFNGLGETPROGRAMIVPROC glGetProgramiv;
+PFNGLLINKPROGRAMPROC glLinkProgram;
+PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+PFNGLUNIFORM4FVPROC glUniform4fv;
+PFNGLUNIFORM3FVPROC glUniform3fv;
+PFNGLUNIFORM2FVPROC glUniform2fv;
+PFNGLUNIFORM1FVPROC glUniform1fv;
+PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
+PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
+PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
+
+
+#if defined(OVR_OS_WIN32)
+
+void* GetFunction(const char* functionName)
+{
+ return wglGetProcAddress(functionName);
+}
+
+#else
+
+void (*GetFunction(const char *functionName))( void )
+{
+ return glXGetProcAddress((GLubyte*)functionName);
+}
+
+#endif
+
+void InitGLExtensions()
+{
+ if (glGenVertexArrays)
+ return;
+
+#if defined(OVR_OS_WIN32)
+ HINSTANCE hInst = LoadLibrary(L"Opengl32.dll");
+ if (!hInst)
+ return;
+
+ glGetFloatv = (PFNGLGETFLOATVPROC) GetProcAddress(hInst, "glGetFloatv");
+ glGetIntegerv = (PFNGLGETINTEGERVPROC) GetProcAddress(hInst, "glGetIntegerv");
+ glGetString = (PFNGLGETSTRINGPROC) GetProcAddress(hInst, "glGetString");
+ glEnable = (PFNGLENABLEPROC) GetProcAddress(hInst, "glEnable");
+ glDisable = (PFNGLDISABLEPROC) GetProcAddress(hInst, "glDisable");
+ glColorMask = (PFNGLCOLORMASKPROC) GetProcAddress(hInst, "glColorMask");
+ glClear = (PFNGLCLEARPROC) GetProcAddress(hInst, "glClear" );
+ glClearColor = (PFNGLCLEARCOLORPROC) GetProcAddress(hInst, "glClearColor");
+ glClearDepth = (PFNGLCLEARDEPTHPROC) GetProcAddress(hInst, "glClearDepth");
+ glViewport = (PFNGLVIEWPORTPROC) GetProcAddress(hInst, "glViewport");
+ glFlush = (PFNGLFLUSHPROC) GetProcAddress(hInst, "glFlush");
+ glFinish = (PFNGLFINISHPROC) GetProcAddress(hInst, "glFinish");
+ glDrawArrays = (PFNGLDRAWARRAYSPROC) GetProcAddress(hInst, "glDrawArrays");
+ glDrawElements = (PFNGLDRAWELEMENTSPROC) GetProcAddress(hInst, "glDrawElements");
+ glGenTextures = (PFNGLGENTEXTURESPROC) GetProcAddress(hInst,"glGenTextures");
+ glDeleteTextures = (PFNGLDELETETEXTURESPROC) GetProcAddress(hInst,"glDeleteTextures");
+ glBindTexture = (PFNGLBINDTEXTUREPROC) GetProcAddress(hInst,"glBindTexture");
+ glTexParameteri = (PFNGLTEXPARAMETERIPROC) GetProcAddress(hInst, "glTexParameteri");
+
+ wglGetProcAddress = (PFNWGLGETPROCADDRESS) GetProcAddress(hInst, "wglGetProcAddress");
+
+ wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) GetFunction("wglGetSwapIntervalEXT");
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) GetFunction("wglSwapIntervalEXT");
+#elif defined(OVR_OS_LINUX)
+ glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) GetFunction("glXSwapIntervalEXT");
+#endif
+
+ glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) GetFunction("glBindFramebufferEXT");
+ glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) GetFunction("glGenVertexArrays");
+ glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) GetFunction("glDeleteVertexArrays");
+ glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) GetFunction("glBindVertexArray");
+ glGenBuffers = (PFNGLGENBUFFERSPROC) GetFunction("glGenBuffers");
+ glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) GetFunction("glDeleteBuffers");
+ glBindBuffer = (PFNGLBINDBUFFERPROC) GetFunction("glBindBuffer");
+ glBufferData = (PFNGLBUFFERDATAPROC) GetFunction("glBufferData");
+ glMapBuffer = (PFNGLMAPBUFFERPROC) GetFunction("glMapBuffer");
+ glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) GetFunction("glUnmapBuffer");
+ glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) GetFunction("glDisableVertexAttribArray");
+ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) GetFunction("glVertexAttribPointer");
+ glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) GetFunction("glEnableVertexAttribArray");
+ glActiveTexture = (PFNGLACTIVETEXTUREPROC) GetFunction("glActiveTexture");
+ glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) GetFunction("glUniformMatrix3fv");
+ glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) GetFunction("glUniformMatrix4fv");
+ glUniform1i = (PFNGLUNIFORM1IPROC) GetFunction("glUniform1i");
+ glUniform1fv = (PFNGLUNIFORM1FVPROC) GetFunction("glUniform1fv");
+ glUniform2fv = (PFNGLUNIFORM2FVPROC) GetFunction("glUniform2fv");
+ glUniform3fv = (PFNGLUNIFORM3FVPROC) GetFunction("glUniform3fv");
+ glUniform2fv = (PFNGLUNIFORM2FVPROC) GetFunction("glUniform2fv");
+ glUniform4fv = (PFNGLUNIFORM4FVPROC) GetFunction("glUniform4fv");
+ glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) GetFunction("glGetUniformLocation");
+ glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) GetFunction("glGetActiveUniform");
+ glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) GetFunction("glGetShaderInfoLog");
+ glGetShaderiv = (PFNGLGETSHADERIVPROC) GetFunction("glGetShaderiv");
+ glCompileShader = (PFNGLCOMPILESHADERPROC) GetFunction("glCompileShader");
+ glShaderSource = (PFNGLSHADERSOURCEPROC) GetFunction("glShaderSource");
+ glCreateShader = (PFNGLCREATESHADERPROC) GetFunction("glCreateShader");
+ glDeleteShader = (PFNGLDELETESHADERPROC) GetFunction("glDeleteShader");
+ glCreateProgram = (PFNGLCREATEPROGRAMPROC) GetFunction("glCreateProgram");
+ glDeleteProgram = (PFNGLDELETEPROGRAMPROC) GetFunction("glDeleteProgram");
+ glUseProgram = (PFNGLUSEPROGRAMPROC) GetFunction("glUseProgram");
+ glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) GetFunction("glGetProgramInfoLog");
+ glGetProgramiv = (PFNGLGETPROGRAMIVPROC) GetFunction("glGetProgramiv");
+ glLinkProgram = (PFNGLLINKPROGRAMPROC) GetFunction("glLinkProgram");
+ glAttachShader = (PFNGLATTACHSHADERPROC) GetFunction("glAttachShader");
+ glDetachShader = (PFNGLDETACHSHADERPROC) GetFunction("glDetachShader");
+ glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) GetFunction("glBindAttribLocation");
+ glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) GetFunction("glGetAttribLocation");
+}
+
+#endif
+
+Buffer::Buffer(RenderParams* rp) : pParams(rp), Size(0), Use(0), GLBuffer(0)
+{
+}
+
+Buffer::~Buffer()
+{
+ if (GLBuffer)
+ glDeleteBuffers(1, &GLBuffer);
+}
+
+bool Buffer::Data(int use, const void* buffer, size_t size)
+{
+ Size = size;
+
+ switch (use & Buffer_TypeMask)
+ {
+ case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break;
+ default: Use = GL_ARRAY_BUFFER; break;
+ }
+
+ if (!GLBuffer)
+ glGenBuffers(1, &GLBuffer);
+
+ int mode = GL_DYNAMIC_DRAW;
+ if (use & Buffer_ReadOnly)
+ mode = GL_STATIC_DRAW;
+
+ glBindBuffer(Use, GLBuffer);
+ glBufferData(Use, size, buffer, mode);
+ return 1;
+}
+
+void* Buffer::Map(size_t, size_t, int)
+{
+ int mode = GL_WRITE_ONLY;
+ //if (flags & Map_Unsynchronized)
+ // mode |= GL_MAP_UNSYNCHRONIZED;
+
+ glBindBuffer(Use, GLBuffer);
+ void* v = glMapBuffer(Use, mode);
+ return v;
+}
+
+bool Buffer::Unmap(void*)
+{
+ glBindBuffer(Use, GLBuffer);
+ int r = glUnmapBuffer(Use);
+ return r != 0;
+}
+
+ShaderSet::ShaderSet()
+{
+ Prog = glCreateProgram();
+}
+ShaderSet::~ShaderSet()
+{
+ glDeleteProgram(Prog);
+}
+
+GLint ShaderSet::GetGLShader(Shader* s)
+{
+ switch (s->Stage)
+ {
+ case Shader_Vertex: {
+ ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER>* gls = (ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER>*)s;
+ return gls->GLShader;
+ } break;
+ case Shader_Fragment: {
+ ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER>* gls = (ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER>*)s;
+ return gls->GLShader;
+ } break;
+ default: break;
+ }
+
+ return -1;
+}
+
+void ShaderSet::SetShader(Shader *s)
+{
+ Shaders[s->Stage] = s;
+ GLint GLShader = GetGLShader(s);
+ glAttachShader(Prog, GLShader);
+ if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment])
+ Link();
+}
+
+void ShaderSet::UnsetShader(int stage)
+{
+ if (Shaders[stage] == NULL)
+ return;
+
+ GLint GLShader = GetGLShader(Shaders[stage]);
+ glDetachShader(Prog, GLShader);
+
+ Shaders[stage] = NULL;
+}
+
+bool ShaderSet::SetUniform(const char* name, int n, const float* v)
+{
+ for (unsigned int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ OVR_ASSERT(UniformInfo[i].Location >= 0);
+ glUseProgram(Prog);
+ switch (UniformInfo[i].Type)
+ {
+ case 1: glUniform1fv(UniformInfo[i].Location, n, v); break;
+ case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break;
+ case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break;
+ case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break;
+ case 12: glUniformMatrix3fv(UniformInfo[i].Location, 1, 1, v); break;
+ case 16: glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, v); break;
+ default: OVR_ASSERT(0);
+ }
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+bool ShaderSet::Link()
+{
+ glLinkProgram(Prog);
+ GLint r;
+ glGetProgramiv(Prog, GL_LINK_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetProgramInfoLog(Prog, sizeof(msg), 0, msg);
+ OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg));
+ if (!r)
+ return 0;
+ }
+ glUseProgram(Prog);
+
+ UniformInfo.Clear();
+ LightingVer = 0;
+ UsesLighting = 0;
+
+ GLint uniformCount = 0;
+ glGetProgramiv(Prog, GL_ACTIVE_UNIFORMS, &uniformCount);
+ OVR_ASSERT(uniformCount >= 0);
+
+ for(GLuint i = 0; i < (GLuint)uniformCount; i++)
+ {
+ GLsizei namelen;
+ GLint size = 0;
+ GLenum type;
+ GLchar name[32];
+ glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name);
+
+ if (size)
+ {
+ int l = glGetUniformLocation(Prog, name);
+ char *np = name;
+ while (*np)
+ {
+ if (*np == '[')
+ *np = 0;
+ np++;
+ }
+ Uniform u;
+ u.Name = name;
+ u.Location = l;
+ u.Size = size;
+ switch (type)
+ {
+ case GL_FLOAT: u.Type = 1; break;
+ case GL_FLOAT_VEC2: u.Type = 2; break;
+ case GL_FLOAT_VEC3: u.Type = 3; break;
+ case GL_FLOAT_VEC4: u.Type = 4; break;
+ case GL_FLOAT_MAT3: u.Type = 12; break;
+ case GL_FLOAT_MAT4: u.Type = 16; break;
+ default:
+ continue;
+ }
+ UniformInfo.PushBack(u);
+ if (!strcmp(name, "LightCount"))
+ UsesLighting = 1;
+ }
+ else
+ break;
+ }
+
+ ProjLoc = glGetUniformLocation(Prog, "Proj");
+ ViewLoc = glGetUniformLocation(Prog, "View");
+ for (int i = 0; i < 8; i++)
+ {
+ char texv[32];
+ OVR_sprintf(texv, 10, "Texture%d", i);
+ TexLoc[i] = glGetUniformLocation(Prog, texv);
+ if (TexLoc[i] < 0)
+ break;
+
+ glUniform1i(TexLoc[i], i);
+ }
+ if (UsesLighting)
+ OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0);
+ return 1;
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+bool ShaderBase::SetUniformBool(const char* name, int n, const bool* v)
+{
+ OVR_UNUSED(n);
+ for(unsigned i = 0; i < UniformReflSize; i++)
+ {
+ if (!strcmp(UniformRefl[i].Name, name))
+ {
+ memcpy(UniformData + UniformRefl[i].Offset, v, UniformRefl[i].Size);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(const Uniform* refl, size_t reflSize)
+{
+ if(!refl)
+ {
+ UniformRefl = NULL;
+ UniformReflSize = 0;
+
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return; // no reflection data
+ }
+
+ UniformRefl = refl;
+ UniformReflSize = reflSize;
+
+ UniformsSize = UniformRefl[UniformReflSize-1].Offset + UniformRefl[UniformReflSize-1].Size;
+ UniformData = (unsigned char*)OVR_ALLOC(UniformsSize);
+}
+
+Texture::Texture(RenderParams* rp, int w, int h) : IsUserAllocated(true), pParams(rp), TexId(0), Width(w), Height(h)
+{
+ if (w && h)
+ glGenTextures(1, &TexId);
+}
+
+Texture::~Texture()
+{
+ if (TexId && !IsUserAllocated)
+ glDeleteTextures(1, &TexId);
+}
+
+void Texture::Set(int slot, ShaderStage) const
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, TexId);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ switch (sm & Sample_FilterMask)
+ {
+ case Sample_Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
+ break;
+
+ case Sample_Anisotropic:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
+ break;
+
+ case Sample_Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
+ break;
+ }
+
+ switch (sm & Sample_AddressMask)
+ {
+ case Sample_Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+
+ case Sample_Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+
+ case Sample_ClampBorder:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ }
+}
+
+void Texture::UpdatePlaceholderTexture(GLuint texId, const Sizei& textureSize)
+{
+ if (!IsUserAllocated && TexId && texId != TexId)
+ glDeleteTextures(1, &TexId);
+
+ TexId = texId;
+ Width = textureSize.w;
+ Height = textureSize.h;
+
+ IsUserAllocated = true;
+}
+
+}}}
diff --git a/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h
new file mode 100644
index 0000000..a410f17
--- /dev/null
+++ b/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h
@@ -0,0 +1,537 @@
+/************************************************************************************
+
+Filename : CAPI_GL_Util.h
+Content : Utility header for OpenGL
+Created : March 27, 2014
+Authors : Andrew Reisse, David Borel
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+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.
+
+************************************************************************************/
+
+#ifndef INC_OVR_CAPI_GL_Util_h
+#define INC_OVR_CAPI_GL_Util_h
+
+#include "../../OVR_CAPI.h"
+#include "../../Kernel/OVR_Array.h"
+#include "../../Kernel/OVR_Math.h"
+#include "../../Kernel/OVR_RefCount.h"
+#include "../../Kernel/OVR_String.h"
+#include "../../Kernel/OVR_Types.h"
+#include "../../Kernel/OVR_Log.h"
+
+#if defined(OVR_OS_WIN32)
+#include <Windows.h>
+#endif
+
+#if defined(OVR_OS_MAC)
+#include <OpenGL/gl3.h>
+#include <OpenGL/gl3ext.h>
+#else
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+#include <GL/gl.h>
+#include <GL/glext.h>
+#if defined(OVR_OS_WIN32)
+#include <GL/wglext.h>
+#elif defined(OVR_OS_LINUX)
+#include <GL/glx.h>
+#endif
+#endif
+
+namespace OVR { namespace CAPI { namespace GL {
+
+// GL extension Hooks for Non-Mac.
+#if !defined(OVR_OS_MAC)
+
+// Let Windows apps build without linking GL.
+#if defined(OVR_OS_WIN32)
+
+typedef void (__stdcall *PFNGLENABLEPROC) (GLenum);
+typedef void (__stdcall *PFNGLDISABLEPROC) (GLenum);
+typedef void (__stdcall *PFNGLGETFLOATVPROC) (GLenum, GLfloat*);
+typedef const GLubyte * (__stdcall *PFNGLGETSTRINGPROC) (GLenum);
+typedef void (__stdcall *PFNGLGETINTEGERVPROC) (GLenum, GLint*);
+typedef PROC (__stdcall *PFNWGLGETPROCADDRESS) (LPCSTR);
+typedef void (__stdcall *PFNGLFLUSHPROC) ();
+typedef void (__stdcall *PFNGLFINISHPROC) ();
+typedef void (__stdcall *PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count);
+typedef void (__stdcall *PFNGLCLEARPROC) (GLbitfield);
+typedef void (__stdcall *PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+typedef void (__stdcall *PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+typedef void (__stdcall *PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
+typedef void (__stdcall *PFNGLDELETETEXTURESPROC) (GLsizei n, GLuint *textures);
+typedef void (__stdcall *PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
+typedef void (__stdcall *PFNGLCLEARCOLORPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+typedef void (__stdcall *PFNGLCLEARDEPTHPROC) (GLclampd depth);
+typedef void (__stdcall *PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (__stdcall *PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+
+extern PFNWGLGETPROCADDRESS wglGetProcAddress;
+extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;
+extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
+
+extern PFNGLENABLEPROC glEnable;
+extern PFNGLDISABLEPROC glDisable;
+extern PFNGLCOLORMASKPROC glColorMask;
+extern PFNGLGETFLOATVPROC glGetFloatv;
+extern PFNGLGETSTRINGPROC glGetString;
+extern PFNGLGETINTEGERVPROC glGetIntegerv;
+extern PFNGLCLEARPROC glClear;
+extern PFNGLCLEARCOLORPROC glClearColor;
+extern PFNGLCLEARDEPTHPROC glClearDepth;
+extern PFNGLVIEWPORTPROC glViewport;
+extern PFNGLDRAWARRAYSPROC glDrawArrays;
+extern PFNGLDRAWELEMENTSPROC glDrawElements;
+extern PFNGLGENTEXTURESPROC glGenTextures;
+extern PFNGLDELETETEXTURESPROC glDeleteTextures;
+extern PFNGLBINDTEXTUREPROC glBindTexture;
+extern PFNGLTEXPARAMETERIPROC glTexParameteri;
+extern PFNGLFLUSHPROC glFlush;
+extern PFNGLFINISHPROC glFinish;
+
+#elif defined(OVR_OS_LINUX)
+
+extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
+
+#endif // defined(OVR_OS_WIN32)
+
+extern PFNGLDELETESHADERPROC glDeleteShader;
+extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
+extern PFNGLACTIVETEXTUREPROC glActiveTexture;
+extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+extern PFNGLBINDBUFFERPROC glBindBuffer;
+extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+extern PFNGLBUFFERDATAPROC glBufferData;
+extern PFNGLGENBUFFERSPROC glGenBuffers;
+extern PFNGLMAPBUFFERPROC glMapBuffer;
+extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
+extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
+extern PFNGLGETSHADERIVPROC glGetShaderiv;
+extern PFNGLCOMPILESHADERPROC glCompileShader;
+extern PFNGLSHADERSOURCEPROC glShaderSource;
+extern PFNGLCREATESHADERPROC glCreateShader;
+extern PFNGLCREATEPROGRAMPROC glCreateProgram;
+extern PFNGLATTACHSHADERPROC glAttachShader;
+extern PFNGLDETACHSHADERPROC glDetachShader;
+extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
+extern PFNGLUNIFORM1IPROC glUniform1i;
+extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
+extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
+extern PFNGLUSEPROGRAMPROC glUseProgram;
+extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
+extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
+extern PFNGLLINKPROGRAMPROC glLinkProgram;
+extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+extern PFNGLUNIFORM4FVPROC glUniform4fv;
+extern PFNGLUNIFORM3FVPROC glUniform3fv;
+extern PFNGLUNIFORM2FVPROC glUniform2fv;
+extern PFNGLUNIFORM1FVPROC glUniform1fv;
+extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
+extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
+extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
+
+extern void InitGLExtensions();
+
+#endif // !defined(OVR_OS_MAC)
+
+
+// Rendering primitive type used to render Model.
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Unknown,
+ Prim_Count
+};
+
+// Types of shaders that can be stored together in a ShaderSet.
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+
+// Buffer types used for uploading geometry & constants.
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x0100,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+// Texture sampling modes.
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+
+// Rendering parameters/pointers describing GL rendering setup.
+struct RenderParams
+{
+#if defined(OVR_OS_WIN32)
+ HWND Window;
+#elif defined(OVR_OS_LINUX)
+ Display* Disp;
+ Window Win;
+#endif
+
+ ovrSizei RTSize;
+ int Multisample;
+};
+
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ RenderParams* pParams;
+ size_t Size;
+ GLenum Use;
+ GLuint GLBuffer;
+
+public:
+ Buffer(RenderParams* r);
+ ~Buffer();
+
+ GLuint GetBuffer() { return GLBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RefCountBase<Texture>
+{
+ bool IsUserAllocated;
+
+public:
+ RenderParams* pParams;
+ GLuint TexId;
+ int Width, Height;
+
+ Texture(RenderParams* rp, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const { return Width; }
+ virtual int GetHeight() const { return Height; }
+
+ virtual void SetSampleMode(int sm);
+
+ // Updates texture to point to specified resources
+ // - used for slave rendering.
+ void UpdatePlaceholderTexture(GLuint texId,
+ const Sizei& textureSize);
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+};
+
+// Base class for vertex and pixel shaders. Stored in ShaderSet.
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+ virtual bool SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+
+// A group of shaders, one per stage.
+// A ShaderSet is applied for rendering with a given fill.
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+ struct Uniform
+ {
+ String Name;
+ int Location, Size;
+ int Type; // currently number of floats in vector
+ };
+ Array<Uniform> UniformInfo;
+
+public:
+ GLuint Prog;
+ GLint ProjLoc, ViewLoc;
+ GLint TexLoc[8];
+ bool UsesLighting;
+ int LightingVer;
+
+ ShaderSet();
+ ~ShaderSet();
+
+ virtual void SetShader(Shader *s);
+ virtual void UnsetShader(int stage);
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ glUseProgram(Prog);
+
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v);
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform3f(const char* name, float x, float y, float z)
+ {
+ const float v[] = {x,y,z};
+ return SetUniform(name, 3, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+
+protected:
+ GLint GetGLShader(Shader* s);
+ bool Link();
+};
+
+
+// Fill combines a ShaderSet (vertex, pixel) with textures, if any.
+// Every model has a fill.
+class ShaderFill : public RefCountBase<ShaderFill>
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<class Texture> Textures[8];
+ void* InputLayout; // HACK this should be abstracted
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { InputLayout = NULL; }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { InputLayout = NULL; }
+
+ ShaderSet* GetShaders() const { return Shaders; }
+ void* GetInputLayout() const { return InputLayout; }
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const {
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ {
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+ }
+ }
+
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+};
+
+
+struct DisplayId
+{
+ // Windows
+ String MonitorName; // Monitor name for fullscreen mode
+
+ // MacOS
+ long CgDisplayId; // CGDirectDisplayID
+
+ DisplayId() : CgDisplayId(0) {}
+ DisplayId(long id) : CgDisplayId(id) {}
+ DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {}
+
+ operator bool () const
+ {
+ return MonitorName.GetLength() || CgDisplayId;
+ }
+
+ bool operator== (const DisplayId& b) const
+ {
+ return CgDisplayId == b.CgDisplayId &&
+ (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) ||
+ strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr()));
+ }
+};
+
+
+class ShaderBase : public Shader
+{
+public:
+ RenderParams* pParams;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ enum VarType
+ {
+ VARTYPE_FLOAT,
+ VARTYPE_INT,
+ VARTYPE_BOOL,
+ };
+
+ struct Uniform
+ {
+ const char* Name;
+ VarType Type;
+ int Offset, Size;
+ };
+ const Uniform* UniformRefl;
+ size_t UniformReflSize;
+
+ ShaderBase(RenderParams* rp, ShaderStage stage) : Shader(stage), pParams(rp), UniformData(0), UniformsSize(0) {}
+ ~ShaderBase()
+ {
+ if (UniformData)
+ OVR_FREE(UniformData);
+ }
+
+ void InitUniforms(const Uniform* refl, size_t reflSize);
+ bool SetUniform(const char* name, int n, const float* v);
+ bool SetUniformBool(const char* name, int n, const bool* v);
+};
+
+
+template<ShaderStage SStage, GLenum SType>
+class ShaderImpl : public ShaderBase
+{
+ friend class ShaderSet;
+
+public:
+ ShaderImpl(RenderParams* rp, void* s, size_t size, const Uniform* refl, size_t reflSize)
+ : ShaderBase(rp, SStage)
+ , GLShader(0)
+ {
+ bool success;
+ OVR_UNUSED(size);
+ success = Compile((const char*) s);
+ OVR_ASSERT(success);
+ InitUniforms(refl, reflSize);
+ }
+ ~ShaderImpl()
+ {
+ if (GLShader)
+ {
+ glDeleteShader(GLShader);
+ GLShader = 0;
+ }
+ }
+ bool Compile(const char* src)
+ {
+ if (!GLShader)
+ GLShader = glCreateShader(GLStage());
+
+ glShaderSource(GLShader, 1, &src, 0);
+ glCompileShader(GLShader);
+ GLint r;
+ glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg);
+ if (msg[0])
+ OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg));
+
+ return 0;
+ }
+ return 1;
+ }
+
+ GLenum GLStage() const
+ {
+ return SType;
+ }
+
+private:
+ GLuint GLShader;
+};
+
+typedef ShaderImpl<Shader_Vertex, GL_VERTEX_SHADER> VertexShader;
+typedef ShaderImpl<Shader_Fragment, GL_FRAGMENT_SHADER> FragmentShader;
+
+}}}
+
+#endif // INC_OVR_CAPI_GL_Util_h
diff --git a/LibOVR/Src/Kernel/OVR_Alg.cpp b/LibOVR/Src/Kernel/OVR_Alg.cpp
new file mode 100644
index 0000000..2e52bc3
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Alg.cpp
@@ -0,0 +1,57 @@
+/************************************************************************************
+
+Filename : OVR_Alg.cpp
+Content : Static lookup tables for Alg functions
+Created : September 19, 2012
+Notes :
+
+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_Types.h"
+
+namespace OVR { namespace Alg {
+
+//------------------------------------------------------------------------
+extern const UByte UpperBitTable[256] =
+{
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+extern const UByte LowerBitTable[256] =
+{
+ 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
+};
+
+
+}} // OVE::Alg
diff --git a/LibOVR/Src/Kernel/OVR_Alg.h b/LibOVR/Src/Kernel/OVR_Alg.h
new file mode 100644
index 0000000..e03cea0
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Alg.h
@@ -0,0 +1,1060 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Alg.h
+Content : Simple general purpose algorithms: Sort, Binary Search, etc.
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Alg_h
+#define OVR_Alg_h
+
+#include "OVR_Types.h"
+#include <string.h>
+
+namespace OVR { namespace Alg {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Operator extensions
+
+template <typename T> OVR_FORCE_INLINE void Swap(T &a, T &b)
+{ T temp(a); a = b; b = temp; }
+
+
+// ***** min/max are not implemented in Visual Studio 6 standard STL
+
+template <typename T> OVR_FORCE_INLINE const T Min(const T a, const T b)
+{ return (a < b) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Max(const T a, const T b)
+{ return (b < a) ? a : b; }
+
+template <typename T> OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal)
+{ return Max<T>(minVal, Min<T>(v, maxVal)); }
+
+template <typename T> OVR_FORCE_INLINE int Chop(T f)
+{ return (int)f; }
+
+template <typename T> OVR_FORCE_INLINE T Lerp(T a, T b, T f)
+{ return (b - a) * f + a; }
+
+
+// These functions stand to fix a stupid VC++ warning (with /Wp64 on):
+// "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data"
+// Use these functions instead of gmin/gmax if the argument has size
+// of the pointer to avoid the warning. Though, functionally they are
+// absolutelly the same as regular gmin/gmax.
+template <typename T> OVR_FORCE_INLINE const T PMin(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (a < b) ? a : b;
+}
+template <typename T> OVR_FORCE_INLINE const T PMax(const T a, const T b)
+{
+ OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt));
+ return (b < a) ? a : b;
+}
+
+
+template <typename T> OVR_FORCE_INLINE const T Abs(const T v)
+{ return (v>=0) ? v : -v; }
+
+
+//-----------------------------------------------------------------------------------
+// ***** OperatorLess
+//
+template<class T> struct OperatorLess
+{
+ static bool Compare(const T& a, const T& b)
+ {
+ return a < b;
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do i++; while( less(arr[i], arr[base]) );
+ do j--; while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+// Same as corresponding G_QuickSortSliced but with checking array limits to avoid
+// crash in the case of wrong comparator functor.
+template<class Array, class Less>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less)
+{
+ enum
+ {
+ Threshold = 9
+ };
+
+ if(end - start < 2) return true;
+
+ SPInt stack[80];
+ SPInt* top = stack;
+ SPInt base = (SPInt)start;
+ SPInt limit = (SPInt)end;
+
+ for(;;)
+ {
+ SPInt len = limit - base;
+ SPInt i, j, pivot;
+
+ if(len > Threshold)
+ {
+ // we use base + len/2 as the pivot
+ pivot = base + len / 2;
+ Swap(arr[base], arr[pivot]);
+
+ i = base + 1;
+ j = limit - 1;
+
+ // now ensure that *i <= *base <= *j
+ if(less(arr[j], arr[i])) Swap(arr[j], arr[i]);
+ if(less(arr[base], arr[i])) Swap(arr[base], arr[i]);
+ if(less(arr[j], arr[base])) Swap(arr[j], arr[base]);
+
+ for(;;)
+ {
+ do
+ {
+ i++;
+ if (i >= limit)
+ return false;
+ } while( less(arr[i], arr[base]) );
+ do
+ {
+ j--;
+ if (j < 0)
+ return false;
+ } while( less(arr[base], arr[j]) );
+
+ if( i > j )
+ {
+ break;
+ }
+
+ Swap(arr[i], arr[j]);
+ }
+
+ Swap(arr[base], arr[j]);
+
+ // now, push the largest sub-array
+ if(j - base > limit - i)
+ {
+ top[0] = base;
+ top[1] = j;
+ base = i;
+ }
+ else
+ {
+ top[0] = i;
+ top[1] = limit;
+ limit = j;
+ }
+ top += 2;
+ }
+ else
+ {
+ // the sub-array is small, perform insertion sort
+ j = base;
+ i = j + 1;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j == base)
+ {
+ break;
+ }
+ }
+ }
+ if(top > stack)
+ {
+ top -= 2;
+ base = top[0];
+ limit = top[1];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template<class Array>
+bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+template<class Array, class Less>
+void QuickSort(Array& arr, Less less)
+{
+ QuickSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+// checks for boundaries
+template<class Array, class Less>
+bool QuickSortSafe(Array& arr, Less less)
+{
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** QuickSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void QuickSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+template<class Array>
+bool QuickSortSafe(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The comparison predicate must be specified.
+// Unlike Quick Sort, the Insertion Sort works much slower in average,
+// but may be much faster on almost sorted arrays. Besides, it guarantees
+// that the elements will not be swapped if not necessary. For example,
+// an array with all equal elements will remain "untouched", while
+// Quick Sort will considerably shuffle the elements in this case.
+template<class Array, class Less>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less)
+{
+ UPInt j = start;
+ UPInt i = j + 1;
+ UPInt limit = end;
+
+ for(; i < limit; j = i, i++)
+ {
+ for(; less(arr[j + 1], arr[j]); j--)
+ {
+ Swap(arr[j + 1], arr[j]);
+ if(j <= start)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSortSliced
+//
+// Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe.
+// The range is specified with start, end, where "end" is exclusive!
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSortSliced(Array& arr, UPInt start, UPInt end)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, start, end, OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The comparison predicate must be specified.
+
+template<class Array, class Less>
+void InsertionSort(Array& arr, Less less)
+{
+ InsertionSortSliced(arr, 0, arr.GetSize(), less);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** InsertionSort
+//
+// Sort an array Array, ArrayPaged, ArrayUnsafe.
+// The array must have GetSize() function.
+// The data type must have a defined "<" operator.
+template<class Array>
+void InsertionSort(Array& arr)
+{
+ typedef typename Array::ValueType ValueType;
+ InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess<ValueType>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Median
+// Returns a median value of the input array.
+// Caveats: partially sorts the array, returns a reference to the array element
+// TBD: This needs to be optimized and generalized
+//
+template<class Array>
+typename Array::ValueType& Median(Array& arr)
+{
+ UPInt count = arr.GetSize();
+ UPInt mid = (count - 1) / 2;
+ OVR_ASSERT(count > 0);
+
+ for (UPInt j = 0; j <= mid; j++)
+ {
+ UPInt min = j;
+ for (UPInt k = j + 1; k < count; k++)
+ if (arr[k] < arr[min])
+ min = k;
+ Swap(arr[j], arr[min]);
+ }
+ return arr[mid];
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(arr[middle], val))
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ else
+ {
+ len = half;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSliced
+//
+template<class Array, class Value>
+UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return LowerBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBoundSized
+//
+template<class Array, class Value>
+UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value, class Less>
+UPInt LowerBound(const Array& arr, const Value& val, Less less)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** LowerBound
+//
+template<class Array, class Value>
+UPInt LowerBound(const Array& arr, const Value& val)
+{
+ return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value, class Less>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less)
+{
+ SPInt first = (SPInt)start;
+ SPInt len = (SPInt)(end - start);
+ SPInt half;
+ SPInt middle;
+
+ while(len > 0)
+ {
+ half = len >> 1;
+ middle = first + half;
+ if(less(val, arr[middle]))
+ {
+ len = half;
+ }
+ else
+ {
+ first = middle + 1;
+ len = len - half - 1;
+ }
+ }
+ return (UPInt)first;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSliced
+//
+template<class Array, class Value>
+UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val)
+{
+ return UpperBoundSliced(arr, start, end, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBoundSized
+//
+template<class Array, class Value>
+UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, size, val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value, class Less>
+UPInt UpperBound(const Array& arr, const Value& val, Less less)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, less);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** UpperBound
+//
+template<class Array, class Value>
+UPInt UpperBound(const Array& arr, const Value& val)
+{
+ return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess<Value>::Compare);
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** ReverseArray
+//
+template<class Array> void ReverseArray(Array& arr)
+{
+ SPInt from = 0;
+ SPInt to = arr.GetSize() - 1;
+ while(from < to)
+ {
+ Swap(arr[from], arr[to]);
+ ++from;
+ --to;
+ }
+}
+
+
+// ***** AppendArray
+//
+template<class CDst, class CSrc>
+void AppendArray(CDst& dst, const CSrc& src)
+{
+ UPInt i;
+ for(i = 0; i < src.GetSize(); i++)
+ dst.PushBack(src[i]);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayAdaptor
+//
+// A simple adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in QuickSort and such.
+template<class T> class ArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ArrayAdaptor() : Data(0), Size(0) {}
+ ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+ T& operator [] (UPInt i) { return Data[i]; }
+private:
+ T* Data;
+ UPInt Size;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** GConstArrayAdaptor
+//
+// A simple const adapter that provides the GetSize() method and overloads
+// operator []. Used to wrap plain arrays in LowerBound and such.
+template<class T> class ConstArrayAdaptor
+{
+public:
+ typedef T ValueType;
+ ConstArrayAdaptor() : Data(0), Size(0) {}
+ ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {}
+ UPInt GetSize() const { return Size; }
+ const T& operator [] (UPInt i) const { return Data[i]; }
+private:
+ const T* Data;
+ UPInt Size;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+extern const UByte UpperBitTable[256];
+extern const UByte LowerBitTable[256];
+
+
+
+//-----------------------------------------------------------------------------------
+inline UByte UpperBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+
+#else
+
+ if (val & 0xFFFFFFFF00000000)
+ {
+ if (val & 0xFFFF000000000000)
+ {
+ return (val & 0xFF00000000000000) ?
+ UpperBitTable[(val >> 56) ] + 56:
+ UpperBitTable[(val >> 48) & 0xFF] + 48;
+ }
+ return (val & 0xFF0000000000) ?
+ UpperBitTable[(val >> 40) & 0xFF] + 40:
+ UpperBitTable[(val >> 32) & 0xFF] + 32;
+ }
+ else
+ {
+ if (val & 0xFFFF0000)
+ {
+ return (val & 0xFF000000) ?
+ UpperBitTable[(val >> 24) ] + 24:
+ UpperBitTable[(val >> 16) & 0xFF] + 16;
+ }
+ return (val & 0xFF00) ?
+ UpperBitTable[(val >> 8) & 0xFF] + 8:
+ UpperBitTable[(val ) & 0xFF];
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------------
+inline UByte LowerBit(UPInt val)
+{
+#ifndef OVR_64BIT_POINTERS
+
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+
+#else
+
+ if (val & 0xFFFFFFFF)
+ {
+ if (val & 0xFFFF)
+ {
+ return (val & 0xFF) ?
+ LowerBitTable[ val & 0xFF]:
+ LowerBitTable[(val >> 8) & 0xFF] + 8;
+ }
+ return (val & 0xFF0000) ?
+ LowerBitTable[(val >> 16) & 0xFF] + 16:
+ LowerBitTable[(val >> 24) & 0xFF] + 24;
+ }
+ else
+ {
+ if (val & 0xFFFF00000000)
+ {
+ return (val & 0xFF00000000) ?
+ LowerBitTable[(val >> 32) & 0xFF] + 32:
+ LowerBitTable[(val >> 40) & 0xFF] + 40;
+ }
+ return (val & 0xFF000000000000) ?
+ LowerBitTable[(val >> 48) & 0xFF] + 48:
+ LowerBitTable[(val >> 56) & 0xFF] + 56;
+ }
+
+#endif
+}
+
+
+
+// ******* Special (optimized) memory routines
+// Note: null (bad) pointer is not tested
+class MemUtil
+{
+public:
+
+ // Memory compare
+ static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); }
+ static int Cmp16(const void* p1, const void* p2, UPInt int16Count);
+ static int Cmp32(const void* p1, const void* p2, UPInt int32Count);
+ static int Cmp64(const void* p1, const void* p2, UPInt int64Count);
+};
+
+// ** Inline Implementation
+
+inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count)
+{
+ SInt16* pa = (SInt16*)p1;
+ SInt16* pb = (SInt16*)p2;
+ unsigned ic = 0;
+ if (int16Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int16Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count)
+{
+ SInt32* pa = (SInt32*)p1;
+ SInt32* pb = (SInt32*)p2;
+ unsigned ic = 0;
+ if (int32Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int32Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count)
+{
+ SInt64* pa = (SInt64*)p1;
+ SInt64* pb = (SInt64*)p2;
+ unsigned ic = 0;
+ if (int64Count == 0)
+ return 0;
+ while (pa[ic] == pb[ic])
+ if (++ic==int64Count)
+ return 0;
+ return pa[ic] > pb[ic] ? 1 : -1;
+}
+
+// ** End Inline Implementation
+
+
+//-----------------------------------------------------------------------------------
+// ******* Byte Order Conversions
+namespace ByteUtil {
+
+ // *** Swap Byte Order
+
+ // Swap the byte order of a byte array
+ inline void SwapOrder(void* pv, int size)
+ {
+ UByte* pb = (UByte*)pv;
+ UByte temp;
+ for (int i = 0; i < size>>1; i++)
+ {
+ temp = pb[size-1-i];
+ pb[size-1-i] = pb[i];
+ pb[i] = temp;
+ }
+ }
+
+ // Swap the byte order of primitive types
+ inline UByte SwapOrder(UByte v) { return v; }
+ inline SByte SwapOrder(SByte v) { return v; }
+ inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); }
+ inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); }
+ inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); }
+ inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); }
+ inline UInt64 SwapOrder(UInt64 v)
+ {
+ return (v>>56) |
+ ((v&UInt64(0x00FF000000000000ULL))>>40) |
+ ((v&UInt64(0x0000FF0000000000ULL))>>24) |
+ ((v&UInt64(0x000000FF00000000ULL))>>8) |
+ ((v&UInt64(0x00000000FF000000ULL))<<8) |
+ ((v&UInt64(0x0000000000FF0000ULL))<<24) |
+ ((v&UInt64(0x000000000000FF00ULL))<<40) |
+ (v<<56);
+ }
+ inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); }
+ inline float SwapOrder(float p)
+ {
+ union {
+ float p;
+ UInt32 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ inline double SwapOrder(double p)
+ {
+ union {
+ double p;
+ UInt64 v;
+ } u;
+ u.p = p;
+ u.v = SwapOrder(u.v);
+ return u.p;
+ }
+
+ // *** Byte-order conversion
+
+#if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN)
+ // Little Endian to System (LE)
+ inline UByte LEToSystem(UByte v) { return v; }
+ inline SByte LEToSystem(SByte v) { return v; }
+ inline UInt16 LEToSystem(UInt16 v) { return v; }
+ inline SInt16 LEToSystem(SInt16 v) { return v; }
+ inline UInt32 LEToSystem(UInt32 v) { return v; }
+ inline SInt32 LEToSystem(SInt32 v) { return v; }
+ inline UInt64 LEToSystem(UInt64 v) { return v; }
+ inline SInt64 LEToSystem(SInt64 v) { return v; }
+ inline float LEToSystem(float v) { return v; }
+ inline double LEToSystem(double v) { return v; }
+
+ // Big Endian to System (LE)
+ inline UByte BEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte BEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float BEToSystem(float v) { return SwapOrder(v); }
+ inline double BEToSystem(double v) { return SwapOrder(v); }
+
+ // System (LE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return v; }
+ inline SByte SystemToLE(SByte v) { return v; }
+ inline UInt16 SystemToLE(UInt16 v) { return v; }
+ inline SInt16 SystemToLE(SInt16 v) { return v; }
+ inline UInt32 SystemToLE(UInt32 v) { return v; }
+ inline SInt32 SystemToLE(SInt32 v) { return v; }
+ inline UInt64 SystemToLE(UInt64 v) { return v; }
+ inline SInt64 SystemToLE(SInt64 v) { return v; }
+ inline float SystemToLE(float v) { return v; }
+ inline double SystemToLE(double v) { return v; }
+
+ // System (LE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToBE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToBE(float v) { return SwapOrder(v); }
+ inline double SystemToBE(double v) { return SwapOrder(v); }
+
+#elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN)
+ // Little Endian to System (BE)
+ inline UByte LEToSystem(UByte v) { return SwapOrder(v); }
+ inline SByte LEToSystem(SByte v) { return SwapOrder(v); }
+ inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); }
+ inline float LEToSystem(float v) { return SwapOrder(v); }
+ inline double LEToSystem(double v) { return SwapOrder(v); }
+
+ // Big Endian to System (BE)
+ inline UByte BEToSystem(UByte v) { return v; }
+ inline SByte BEToSystem(SByte v) { return v; }
+ inline UInt16 BEToSystem(UInt16 v) { return v; }
+ inline SInt16 BEToSystem(SInt16 v) { return v; }
+ inline UInt32 BEToSystem(UInt32 v) { return v; }
+ inline SInt32 BEToSystem(SInt32 v) { return v; }
+ inline UInt64 BEToSystem(UInt64 v) { return v; }
+ inline SInt64 BEToSystem(SInt64 v) { return v; }
+ inline float BEToSystem(float v) { return v; }
+ inline double BEToSystem(double v) { return v; }
+
+ // System (BE) to Little Endian
+ inline UByte SystemToLE(UByte v) { return SwapOrder(v); }
+ inline SByte SystemToLE(SByte v) { return SwapOrder(v); }
+ inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); }
+ inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); }
+ inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); }
+ inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); }
+ inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); }
+ inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); }
+ inline float SystemToLE(float v) { return SwapOrder(v); }
+ inline double SystemToLE(double v) { return SwapOrder(v); }
+
+ // System (BE) to Big Endian
+ inline UByte SystemToBE(UByte v) { return v; }
+ inline SByte SystemToBE(SByte v) { return v; }
+ inline UInt16 SystemToBE(UInt16 v) { return v; }
+ inline SInt16 SystemToBE(SInt16 v) { return v; }
+ inline UInt32 SystemToBE(UInt32 v) { return v; }
+ inline SInt32 SystemToBE(SInt32 v) { return v; }
+ inline UInt64 SystemToBE(UInt64 v) { return v; }
+ inline SInt64 SystemToBE(SInt64 v) { return v; }
+ inline float SystemToBE(float v) { return v; }
+ inline double SystemToBE(double v) { return v; }
+
+#else
+ #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN"
+#endif
+
+} // namespace ByteUtil
+
+
+
+// Used primarily for hardware interfacing such as sensor reports, firmware, etc.
+// Reported data is all little-endian.
+inline UInt16 DecodeUInt16(const UByte* buffer)
+{
+ return ByteUtil::LEToSystem ( *(const UInt16*)buffer );
+}
+
+inline SInt16 DecodeSInt16(const UByte* buffer)
+{
+ return ByteUtil::LEToSystem ( *(const SInt16*)buffer );
+}
+
+inline UInt32 DecodeUInt32(const UByte* buffer)
+{
+ return ByteUtil::LEToSystem ( *(const UInt32*)buffer );
+}
+
+inline SInt32 DecodeSInt32(const UByte* buffer)
+{
+ return ByteUtil::LEToSystem ( *(const SInt32*)buffer );
+}
+
+inline float DecodeFloat(const UByte* buffer)
+{
+ union {
+ UInt32 U;
+ float F;
+ };
+
+ U = DecodeUInt32(buffer);
+ return F;
+}
+
+inline void EncodeUInt16(UByte* buffer, UInt16 val)
+{
+ *(UInt16*)buffer = ByteUtil::SystemToLE ( val );
+}
+
+inline void EncodeSInt16(UByte* buffer, SInt16 val)
+{
+ *(SInt16*)buffer = ByteUtil::SystemToLE ( val );
+}
+
+inline void EncodeUInt32(UByte* buffer, UInt32 val)
+{
+ *(UInt32*)buffer = ByteUtil::SystemToLE ( val );
+}
+
+inline void EncodeSInt32(UByte* buffer, SInt32 val)
+{
+ *(SInt32*)buffer = ByteUtil::SystemToLE ( val );
+}
+
+inline void EncodeFloat(UByte* buffer, float val)
+{
+ union {
+ UInt32 U;
+ float F;
+ };
+
+ F = val;
+ EncodeUInt32(buffer, U);
+}
+
+// Converts an 8-bit binary-coded decimal
+inline SByte DecodeBCD(UByte byte)
+{
+ UByte digit1 = (byte >> 4) & 0x0f;
+ UByte digit2 = byte & 0x0f;
+ int decimal = digit1 * 10 + digit2; // maximum value = 99
+ return (SByte)decimal;
+}
+
+
+}} // OVR::Alg
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.cpp b/LibOVR/Src/Kernel/OVR_Allocator.cpp
new file mode 100644
index 0000000..0f82561
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Allocator.cpp
@@ -0,0 +1,95 @@
+/************************************************************************************
+
+Filename : OVR_Allocator.cpp
+Content : Installable memory allocator implementation
+Created : September 19, 2012
+Notes :
+
+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_Allocator.h"
+#ifdef OVR_OS_MAC
+ #include <stdlib.h>
+#else
+ #include <malloc.h>
+#endif
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+Allocator* Allocator::pInstance = 0;
+
+// Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding.
+void* Allocator::AllocAligned(UPInt size, UPInt align)
+{
+ OVR_ASSERT((align & (align-1)) == 0);
+ align = (align > sizeof(UPInt)) ? align : sizeof(UPInt);
+ UPInt p = (UPInt)Alloc(size+align);
+ UPInt aligned = 0;
+ if (p)
+ {
+ aligned = (UPInt(p) + align-1) & ~(align-1);
+ if (aligned == p)
+ aligned += align;
+ *(((UPInt*)aligned)-1) = aligned-p;
+ }
+ return (void*)aligned;
+}
+
+void Allocator::FreeAligned(void* p)
+{
+ UPInt src = UPInt(p) - *(((UPInt*)p)-1);
+ Free((void*)src);
+}
+
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+void* DefaultAllocator::Alloc(UPInt size)
+{
+ return malloc(size);
+}
+void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line)
+{
+#if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC)
+ return _malloc_dbg(size, _NORMAL_BLOCK, file, line);
+#else
+ OVR_UNUSED2(file, line);
+ return malloc(size);
+#endif
+}
+
+void* DefaultAllocator::Realloc(void* p, UPInt newSize)
+{
+ return realloc(p, newSize);
+}
+void DefaultAllocator::Free(void *p)
+{
+ return free(p);
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Allocator.h b/LibOVR/Src/Kernel/OVR_Allocator.h
new file mode 100644
index 0000000..b862557
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Allocator.h
@@ -0,0 +1,347 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Allocator.h
+Content : Installable memory allocator
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Allocator_h
+#define OVR_Allocator_h
+
+#include "OVR_Types.h"
+
+//-----------------------------------------------------------------------------------
+
+// ***** Disable template-unfriendly MS VC++ warnings
+#if defined(OVR_CC_MSVC)
+// Pragma to prevent long name warnings in in VC++
+#pragma warning(disable : 4503)
+#pragma warning(disable : 4786)
+// In MSVC 7.1, warning about placement new POD default initializer
+#pragma warning(disable : 4345)
+#endif
+
+// Un-define new so that placement constructors work
+#undef new
+
+
+//-----------------------------------------------------------------------------------
+// ***** Placement new overrides
+
+// Calls constructor on own memory created with "new(ptr) type"
+#ifndef __PLACEMENT_NEW_INLINE
+#define __PLACEMENT_NEW_INLINE
+
+# if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU)
+# include <new>
+# else
+ // Useful on MSVC
+ OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; }
+ OVR_FORCE_INLINE void operator delete (void *, void *) { }
+# endif
+
+#endif // __PLACEMENT_NEW_INLINE
+
+
+
+//------------------------------------------------------------------------
+// ***** Macros to redefine class new/delete operators
+
+// Types specifically declared to allow disambiguation of address in
+// class member operator new.
+
+#define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \
+ void* operator new(UPInt sz) \
+ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \
+ void* operator new(UPInt sz, const char* file, int line) \
+ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \
+ void operator delete(void *p) \
+ { check_delete(class_name, p); OVR_FREE(p); } \
+ void operator delete(void *p, const char*, int) \
+ { check_delete(class_name, p); OVR_FREE(p); }
+
+#define OVR_MEMORY_DEFINE_PLACEMENT_NEW \
+ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \
+ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); }
+
+
+#define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p)
+
+// Redefined all delete/new operators in a class without custom memory initialization
+#define OVR_MEMORY_REDEFINE_NEW(class_name) \
+ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE)
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Construct / Destruct
+
+// Construct/Destruct functions are useful when new is redefined, as they can
+// be called instead of placement new constructors.
+
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p)
+{
+ return ::new(p) T();
+}
+
+template <class T>
+OVR_FORCE_INLINE T* Construct(void *p, const T& source)
+{
+ return ::new(p) T(source);
+}
+
+// Same as above, but allows for a different type of constructor.
+template <class T, class S>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source)
+{
+ return ::new(p) T(source);
+}
+
+template <class T, class S1, class S2>
+OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2)
+{
+ return ::new(p) T(src1, src2);
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source)
+{
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ {
+ Construct<T>(pdata, source);
+ }
+}
+
+template <class T>
+OVR_FORCE_INLINE void Destruct(T *pobj)
+{
+ pobj->~T();
+ OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning.
+}
+
+template <class T>
+OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count)
+{
+ for (UPInt i=0; i<count; ++i, ++pobj)
+ pobj->~T();
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Allocator
+
+// Allocator defines a memory allocation interface that developers can override
+// to to provide memory for OVR; an instance of this class is typically created on
+// application startup and passed into System or OVR::System constructor.
+//
+//
+// Users implementing this interface must provide three functions: Alloc, Free,
+// and Realloc. Implementations of these functions must honor the requested alignment.
+// Although arbitrary alignment requests are possible, requested alignment will
+// typically be small, such as 16 bytes or less.
+
+class Allocator
+{
+ friend class System;
+public:
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified size with default alignment.
+ // Alloc of size==0 will allocate a tiny block & return a valid pointer;
+ // this makes it suitable for new operator.
+ virtual void* Alloc(UPInt size) = 0;
+ // Same as Alloc, but provides an option of passing debug data.
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line)
+ { OVR_UNUSED2(file, line); return Alloc(size); }
+
+ // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to
+ // new memory block, which may be the same as original pointer. Will return 0 if reallocation
+ // failed, in which case previous memory is still valid.
+ // Realloc to decrease size will never fail.
+ // Realloc of pointer == 0 is equivalent to Alloc
+ // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free().
+ virtual void* Realloc(void* p, UPInt newSize) = 0;
+
+ // Frees memory allocated by Alloc/Realloc.
+ // Free of null pointer is valid and will do nothing.
+ virtual void Free(void *p) = 0;
+
+
+ // *** Standard Alignment Alloc/Free
+
+ // Allocate memory of specified alignment.
+ // Memory allocated with AllocAligned MUST be freed with FreeAligned.
+ // Default implementation will delegate to Alloc/Free after doing rounding.
+ virtual void* AllocAligned(UPInt size, UPInt align);
+ // Frees memory allocated with AllocAligned.
+ virtual void FreeAligned(void* p);
+
+ // Returns the pointer to the current globally installed Allocator instance.
+ // This pointer is used for most of the memory allocations.
+ static Allocator* GetInstance() { return pInstance; }
+
+
+protected:
+ // onSystemShutdown is called on the allocator during System::Shutdown.
+ // At this point, all allocations should've been freed.
+ virtual void onSystemShutdown() { }
+
+public:
+ static void setInstance(Allocator* palloc)
+ {
+ OVR_ASSERT((pInstance == 0) || (palloc == 0));
+ pInstance = palloc;
+ }
+
+private:
+
+ static Allocator* pInstance;
+};
+
+
+
+//------------------------------------------------------------------------
+// ***** Allocator_SingletonSupport
+
+// Allocator_SingletonSupport is a Allocator wrapper class that implements
+// the InitSystemSingleton static function, used to create a global singleton
+// used for the OVR::System default argument initialization.
+//
+// End users implementing custom Allocator interface don't need to make use of this base
+// class; they can just create an instance of their own class on stack and pass it to System.
+
+template<class D>
+class Allocator_SingletonSupport : public Allocator
+{
+ struct AllocContainer
+ {
+ UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)];
+ bool Initialized;
+ AllocContainer() : Initialized(0) { }
+ };
+
+ AllocContainer* pContainer;
+
+public:
+ Allocator_SingletonSupport() : pContainer(0) { }
+
+ // Creates a singleton instance of this Allocator class used
+ // on OVR_DEFAULT_ALLOCATOR during System initialization.
+ static D* InitSystemSingleton()
+ {
+ static AllocContainer Container;
+ OVR_ASSERT(Container.Initialized == false);
+
+ Allocator_SingletonSupport<D> *presult = Construct<D>((void*)Container.Data);
+ presult->pContainer = &Container;
+ Container.Initialized = true;
+ return (D*)presult;
+ }
+
+protected:
+ virtual void onSystemShutdown()
+ {
+ Allocator::onSystemShutdown();
+ if (pContainer)
+ {
+ pContainer->Initialized = false;
+ Destruct((D*)this);
+ pContainer = 0;
+ }
+ }
+};
+
+//------------------------------------------------------------------------
+// ***** Default Allocator
+
+// This allocator is created and used if no other allocator is installed.
+// Default allocator delegates to system malloc.
+
+class DefaultAllocator : public Allocator_SingletonSupport<DefaultAllocator>
+{
+public:
+ virtual void* Alloc(UPInt size);
+ virtual void* AllocDebug(UPInt size, const char* file, unsigned line);
+ virtual void* Realloc(void* p, UPInt newSize);
+ virtual void Free(void *p);
+};
+
+
+//------------------------------------------------------------------------
+// ***** Memory Allocation Macros
+
+// These macros should be used for global allocation. In the future, these
+// macros will allows allocation to be extended with debug file/line information
+// if necessary.
+
+#define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s))
+#define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p))
+#define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a))
+#define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p))
+
+#ifdef OVR_BUILD_DEBUG
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__)
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l)
+#else
+#define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s))
+#define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s))
+#endif
+
+//------------------------------------------------------------------------
+
+// Base class that overrides the new and delete operators.
+// Deriving from this class, even as a multiple base, incurs no space overhead.
+class NewOverrideBase
+{
+public:
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW(NewOverrideBase)
+};
+
+
+} // OVR
+
+
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+#endif // OVR_Memory
diff --git a/LibOVR/Src/Kernel/OVR_Array.h b/LibOVR/Src/Kernel/OVR_Array.h
new file mode 100644
index 0000000..7a715ba
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Array.h
@@ -0,0 +1,833 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Array.h
+Content : Template implementation for Array
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Array_h
+#define OVR_Array_h
+
+#include "OVR_ContainerAllocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDefaultPolicy
+//
+// Default resize behavior. No minimal capacity, Granularity=4,
+// Shrinking as needed. ArrayConstPolicy actually is the same as
+// ArrayDefaultPolicy, but parametrized with constants.
+// This struct is used only in order to reduce the template "matroska".
+struct ArrayDefaultPolicy
+{
+ ArrayDefaultPolicy() : Capacity(0) {}
+ ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return 0; }
+ UPInt GetGranularity() const { return 4; }
+ bool NeverShrinking() const { return 0; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayConstPolicy
+//
+// Statically parametrized resizing behavior:
+// MinCapacity, Granularity, and Shrinking flag.
+template<int MinCapacity=0, int Granularity=4, bool NeverShrink=false>
+struct ArrayConstPolicy
+{
+ typedef ArrayConstPolicy<MinCapacity, Granularity, NeverShrink> SelfType;
+
+ ArrayConstPolicy() : Capacity(0) {}
+ ArrayConstPolicy(const SelfType&) : Capacity(0) {}
+
+ UPInt GetMinCapacity() const { return MinCapacity; }
+ UPInt GetGranularity() const { return Granularity; }
+ bool NeverShrinking() const { return NeverShrink; }
+
+ UPInt GetCapacity() const { return Capacity; }
+ void SetCapacity(UPInt capacity) { Capacity = capacity; }
+private:
+ UPInt Capacity;
+};
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataBase
+//
+// Basic operations with array data: Reserve, Resize, Free, ArrayPolicy.
+// For internal use only: ArrayData,ArrayDataCC and others.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataBase
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataBase()
+ : Data(0), Size(0), Policy() {}
+
+ ArrayDataBase(const SizePolicy& p)
+ : Data(0), Size(0), Policy(p) {}
+
+ ~ArrayDataBase()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ }
+
+ UPInt GetCapacity() const
+ {
+ return Policy.GetCapacity();
+ }
+
+ void ClearAndRelease()
+ {
+ Allocator::DestructArray(Data, Size);
+ Allocator::Free(Data);
+ Data = 0;
+ Size = 0;
+ Policy.SetCapacity(0);
+ }
+
+ void Reserve(UPInt newCapacity)
+ {
+ if (Policy.NeverShrinking() && newCapacity < GetCapacity())
+ return;
+
+ if (newCapacity < Policy.GetMinCapacity())
+ newCapacity = Policy.GetMinCapacity();
+
+ // Resize the buffer.
+ if (newCapacity == 0)
+ {
+ if (Data)
+ {
+ Allocator::Free(Data);
+ Data = 0;
+ }
+ Policy.SetCapacity(0);
+ }
+ else
+ {
+ UPInt gran = Policy.GetGranularity();
+ newCapacity = (newCapacity + gran - 1) / gran * gran;
+ if (Data)
+ {
+ if (Allocator::IsMovable())
+ {
+ Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity);
+ }
+ else
+ {
+ T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ UPInt i, s;
+ s = (Size < newCapacity) ? Size : newCapacity;
+ for (i = 0; i < s; ++i)
+ {
+ Allocator::Construct(&newData[i], Data[i]);
+ Allocator::Destruct(&Data[i]);
+ }
+ for (i = s; i < Size; ++i)
+ {
+ Allocator::Destruct(&Data[i]);
+ }
+ Allocator::Free(Data);
+ Data = newData;
+ }
+ }
+ else
+ {
+ Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity);
+ //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this?
+ }
+ Policy.SetCapacity(newCapacity);
+ // OVR_ASSERT(Data); // need to throw (or something) on alloc failure!
+ }
+ }
+
+ // This version of Resize DOES NOT construct the elements.
+ // It's done to optimize PushBack, which uses a copy constructor
+ // instead of the default constructor and assignment
+ void ResizeNoConstruct(UPInt newSize)
+ {
+ UPInt oldSize = Size;
+
+ if (newSize < oldSize)
+ {
+ Allocator::DestructArray(Data + newSize, oldSize - newSize);
+ if (newSize < (Policy.GetCapacity() >> 1))
+ {
+ Reserve(newSize);
+ }
+ }
+ else if(newSize >= Policy.GetCapacity())
+ {
+ Reserve(newSize + (newSize >> 2));
+ }
+ //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable
+ // array may use this array and may traverse it during Reserve (in the case, if
+ // collection occurs because of heap limit exceeded).
+ Size = newSize;
+ }
+
+ ValueType* Data;
+ UPInt Size;
+ SizePolicy Policy;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayData
+//
+// General purpose array data.
+// For internal use only in Array, ArrayLH, ArrayPOD and so on.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayData : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayData <T, Allocator, SizePolicy> SelfType;
+
+ ArrayData()
+ : BaseType() { }
+
+ ArrayData(UPInt size)
+ : BaseType() { Resize(size); }
+
+ ArrayData(const SelfType& a)
+ : BaseType(a.Policy) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayDataCC
+//
+// A modification of ArrayData that always copy-constructs new elements
+// using a specified DefaultValue. For internal use only in ArrayCC.
+template<class T, class Allocator, class SizePolicy>
+struct ArrayDataCC : ArrayDataBase<T, Allocator, SizePolicy>
+{
+ typedef T ValueType;
+ typedef Allocator AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayDataBase<T, Allocator, SizePolicy> BaseType;
+ typedef ArrayDataCC <T, Allocator, SizePolicy> SelfType;
+
+ ArrayDataCC(const ValueType& defval)
+ : BaseType(), DefaultValue(defval) { }
+
+ ArrayDataCC(const ValueType& defval, UPInt size)
+ : BaseType(), DefaultValue(defval) { Resize(size); }
+
+ ArrayDataCC(const SelfType& a)
+ : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); }
+
+
+ void Resize(UPInt newSize)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(newSize);
+ if(newSize > oldSize)
+ Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue);
+ }
+
+ void PushBack(const ValueType& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::Construct(this->Data + this->Size - 1, val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ BaseType::ResizeNoConstruct(this->Size + 1);
+ Allocator::ConstructAlt(this->Data + this->Size - 1, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ if (count)
+ {
+ UPInt oldSize = this->Size;
+ BaseType::ResizeNoConstruct(this->Size + count);
+ Allocator::ConstructArray(this->Data + oldSize, count, other);
+ }
+ }
+
+ ValueType DefaultValue;
+};
+
+
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ArrayBase
+//
+// Resizable array. The behavior can be POD (suffix _POD) and
+// Movable (no suffix) depending on the allocator policy.
+// In case of _POD the constructors and destructors are not called.
+//
+// Arrays can't handle non-movable objects! Don't put anything in here
+// that can't be moved around by bitwise copy.
+//
+// The addresses of elements are not persistent! Don't keep the address
+// of an element; the array contents will move around as it gets resized.
+template<class ArrayData>
+class ArrayBase
+{
+public:
+ typedef typename ArrayData::ValueType ValueType;
+ typedef typename ArrayData::AllocatorType AllocatorType;
+ typedef typename ArrayData::SizePolicyType SizePolicyType;
+ typedef ArrayBase<ArrayData> SelfType;
+
+
+#undef new
+ OVR_MEMORY_REDEFINE_NEW(ArrayBase)
+// Redefine operator 'new' if necessary.
+#if defined(OVR_DEFINE_NEW)
+#define new OVR_DEFINE_NEW
+#endif
+
+
+ ArrayBase()
+ : Data() {}
+ ArrayBase(UPInt size)
+ : Data(size) {}
+ ArrayBase(const SelfType& a)
+ : Data(a.Data) {}
+
+ ArrayBase(const ValueType& defval)
+ : Data(defval) {}
+ ArrayBase(const ValueType& defval, UPInt size)
+ : Data(defval, size) {}
+
+ SizePolicyType* GetSizePolicy() const { return Data.Policy; }
+ void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; }
+
+ bool NeverShrinking()const { return Data.Policy.NeverShrinking(); }
+ UPInt GetSize() const { return Data.Size; }
+ bool IsEmpty() const { return Data.Size == 0; }
+ UPInt GetCapacity() const { return Data.GetCapacity(); }
+ UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); }
+
+ void ClearAndRelease() { Data.ClearAndRelease(); }
+ void Clear() { Data.Resize(0); }
+ void Resize(UPInt newSize) { Data.Resize(newSize); }
+
+ // Reserve can only increase the capacity
+ void Reserve(UPInt newCapacity)
+ {
+ if (newCapacity > Data.GetCapacity())
+ Data.Reserve(newCapacity);
+ }
+
+ // Basic access.
+ ValueType& At(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& At(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ ValueType ValueAt(UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Basic access.
+ ValueType& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+ const ValueType& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < Data.Size);
+ return Data.Data[index];
+ }
+
+ // Raw pointer to the data. Use with caution!
+ const ValueType* GetDataPtr() const { return Data.Data; }
+ ValueType* GetDataPtr() { return Data.Data; }
+
+ // Insert the given element at the end of the array.
+ void PushBack(const ValueType& val)
+ {
+ // DO NOT pass elements of your own vector into
+ // push_back()! Since we're using references,
+ // resize() may munge the element storage!
+ // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]);
+ Data.PushBack(val);
+ }
+
+ template<class S>
+ void PushBackAlt(const S& val)
+ {
+ Data.PushBackAlt(val);
+ }
+
+ // Remove the last element.
+ void PopBack(UPInt count = 1)
+ {
+ OVR_ASSERT(Data.Size >= count);
+ Data.Resize(Data.Size - count);
+ }
+
+ ValueType& PushDefault()
+ {
+ Data.PushBack(ValueType());
+ return Back();
+ }
+
+ ValueType Pop()
+ {
+ ValueType t = Back();
+ PopBack();
+ return t;
+ }
+
+
+ // Access the first element.
+ ValueType& Front() { return At(0); }
+ const ValueType& Front() const { return At(0); }
+
+ // Access the last element.
+ ValueType& Back() { return At(Data.Size - 1); }
+ const ValueType& Back() const { return At(Data.Size - 1); }
+
+ // Array copy. Copies the contents of a into this array.
+ const SelfType& operator = (const SelfType& a)
+ {
+ Resize(a.GetSize());
+ for (UPInt i = 0; i < Data.Size; i++) {
+ *(Data.Data + i) = a[i];
+ }
+ return *this;
+ }
+
+ // Removing multiple elements from the array.
+ void RemoveMultipleAt(UPInt index, UPInt num)
+ {
+ OVR_ASSERT(index + num <= Data.Size);
+ if (Data.Size == num)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::DestructArray(Data.Data + index, num);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + num,
+ Data.Size - num - index);
+ Data.Size -= num;
+ }
+ }
+
+ // Removing an element from the array is an expensive operation!
+ // It compacts only after removing the last element.
+ // If order of elements in the array is not important then use
+ // RemoveAtUnordered, that could be much faster than the regular
+ // RemoveAt.
+ void RemoveAt(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ if (Data.Size == 1)
+ {
+ Clear();
+ }
+ else
+ {
+ AllocatorType::Destruct(Data.Data + index);
+ AllocatorType::CopyArrayForward(
+ Data.Data + index,
+ Data.Data + index + 1,
+ Data.Size - 1 - index);
+ --Data.Size;
+ }
+ }
+
+ // Removes an element from the array without respecting of original order of
+ // elements for better performance. Do not use on array where order of elements
+ // is important, otherwise use it instead of regular RemoveAt().
+ void RemoveAtUnordered(UPInt index)
+ {
+ OVR_ASSERT(index < Data.Size);
+ if (Data.Size == 1)
+ {
+ Clear();
+ }
+ else
+ {
+ // copy the last element into the 'index' position
+ // and decrement the size (instead of moving all elements
+ // in [index + 1 .. size - 1] range).
+ const UPInt lastElemIndex = Data.Size - 1;
+ if (index < lastElemIndex)
+ {
+ AllocatorType::Destruct(Data.Data + index);
+ AllocatorType::Construct(Data.Data + index, Data.Data[lastElemIndex]);
+ }
+ AllocatorType::Destruct(Data.Data + lastElemIndex);
+ --Data.Size;
+ }
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertAt(UPInt index, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + 1);
+ if (index < Data.Size - 1)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + 1,
+ Data.Data + index,
+ Data.Size - 1 - index);
+ }
+ AllocatorType::Construct(Data.Data + index, val);
+ }
+
+ // Insert the given object at the given index shifting all the elements up.
+ void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType())
+ {
+ OVR_ASSERT(index <= Data.Size);
+
+ Data.Resize(Data.Size + num);
+ if (index < Data.Size - num)
+ {
+ AllocatorType::CopyArrayBackward(
+ Data.Data + index + num,
+ Data.Data + index,
+ Data.Size - num - index);
+ }
+ for (UPInt i = 0; i < num; ++i)
+ AllocatorType::Construct(Data.Data + index + i, val);
+ }
+
+ // Append the given data to the array.
+ void Append(const SelfType& other)
+ {
+ Append(other.Data.Data, other.GetSize());
+ }
+
+ // Append the given data to the array.
+ void Append(const ValueType other[], UPInt count)
+ {
+ Data.Append(other, count);
+ }
+
+ class Iterator
+ {
+ SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ Iterator() : pArray(0), CurIndex(-1) {}
+ Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ Iterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (SPInt)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator++(int)
+ {
+ Iterator it(*this);
+ operator++();
+ return it;
+ }
+ Iterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ Iterator operator--(int)
+ {
+ Iterator it(*this);
+ operator--();
+ return it;
+ }
+ Iterator operator+(int delta) const
+ {
+ return Iterator(pArray, CurIndex + delta);
+ }
+ Iterator operator-(int delta) const
+ {
+ return Iterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const Iterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ void Remove()
+ {
+ if (!IsFinished())
+ pArray->RemoveAt(CurIndex);
+ }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+
+ Iterator Begin() { return Iterator(this); }
+ Iterator End() { return Iterator(this, (SPInt)GetSize()); }
+ Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); }
+
+ class ConstIterator
+ {
+ const SelfType* pArray;
+ SPInt CurIndex;
+
+ public:
+ ConstIterator() : pArray(0), CurIndex(-1) {}
+ ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {}
+
+ bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; }
+ bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; }
+
+ ConstIterator& operator++()
+ {
+ if (pArray)
+ {
+ if (CurIndex < (int)pArray->GetSize())
+ ++CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator++(int)
+ {
+ ConstIterator it(*this);
+ operator++();
+ return it;
+ }
+ ConstIterator& operator--()
+ {
+ if (pArray)
+ {
+ if (CurIndex >= 0)
+ --CurIndex;
+ }
+ return *this;
+ }
+ ConstIterator operator--(int)
+ {
+ ConstIterator it(*this);
+ operator--();
+ return it;
+ }
+ ConstIterator operator+(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex + delta);
+ }
+ ConstIterator operator-(int delta) const
+ {
+ return ConstIterator(pArray, CurIndex - delta);
+ }
+ SPInt operator-(const ConstIterator& right) const
+ {
+ OVR_ASSERT(pArray == right.pArray);
+ return CurIndex - right.CurIndex;
+ }
+ const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; }
+ const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+ const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; }
+
+ bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); }
+
+ SPInt GetIndex() const { return CurIndex; }
+ };
+ ConstIterator Begin() const { return ConstIterator(this); }
+ ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); }
+ ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); }
+
+protected:
+ ArrayData Data;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Array
+//
+// General purpose array for movable objects that require explicit
+// construction/destruction.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class Array : public ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef Array<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ Array() : BaseType() {}
+ Array(UPInt size) : BaseType(size) {}
+ Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ Array(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+// ***** ArrayPOD
+//
+// General purpose array for movable objects that DOES NOT require
+// construction/destruction. Constructors and destructors are not called!
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayPOD : public ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_POD<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayPOD<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_POD<T>, SizePolicy> > BaseType;
+
+ ArrayPOD() : BaseType() {}
+ ArrayPOD(UPInt size) : BaseType(size) {}
+ ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayPOD(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCPP
+//
+// General purpose, fully C++ compliant array. Can be used with non-movable data.
+// Global heap is in use.
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCPP : public ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator_CPP<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCPP<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayData<T, ContainerAllocator_CPP<T>, SizePolicy> > BaseType;
+
+ ArrayCPP() : BaseType() {}
+ ArrayCPP(UPInt size) : BaseType(size) {}
+ ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); }
+ ArrayCPP(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+
+// ***** ArrayCC
+//
+// A modification of the array that uses the given default value to
+// construct the elements. The constructors and destructors are
+// properly called, the objects must be movable.
+
+template<class T, class SizePolicy=ArrayDefaultPolicy>
+class ArrayCC : public ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> >
+{
+public:
+ typedef T ValueType;
+ typedef ContainerAllocator<T> AllocatorType;
+ typedef SizePolicy SizePolicyType;
+ typedef ArrayCC<T, SizePolicy> SelfType;
+ typedef ArrayBase<ArrayDataCC<T, ContainerAllocator<T>, SizePolicy> > BaseType;
+
+ ArrayCC(const ValueType& defval) : BaseType(defval) {}
+ ArrayCC(const ValueType& defval, UPInt size) : BaseType(defval, size) {}
+ ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); }
+ ArrayCC(const SelfType& a) : BaseType(a) {}
+ const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.cpp b/LibOVR/Src/Kernel/OVR_Atomic.cpp
new file mode 100644
index 0000000..9ea6e76
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Atomic.cpp
@@ -0,0 +1,162 @@
+/************************************************************************************
+
+Filename : OVR_Atomic.cpp
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementation if not available.
+Created : September 19, 2012
+Notes :
+
+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_Atomic.h"
+#include "OVR_Allocator.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+// Include Windows 8-Metro compatible Synchronization API
+#if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+#include <synchapi.h>
+#endif
+
+
+namespace OVR {
+
+// ***** Windows Lock implementation
+
+#if defined(OVR_OS_WIN32)
+
+// ***** Standard Win32 Lock implementation
+
+// Constructors
+Lock::Lock(unsigned spinCount)
+{
+#if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8)
+ // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility
+ InitializeCriticalSectionEx(&cs, spinCount,
+ OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO));
+#else
+ // Spin count init critical section function prototype for Window NT
+ typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount)
+ (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
+
+
+ // Try to load function dynamically so that we don't require NT
+ // On Windows NT we will use InitializeCriticalSectionAndSpinCount
+ static bool initTried = 0;
+ static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0;
+
+ if (!initTried)
+ {
+ HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll"));
+ pInitFn = (Function_InitializeCriticalSectionAndSpinCount)
+ ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount");
+ initTried = true;
+ }
+
+ // Initialize the critical section
+ if (pInitFn)
+ pInitFn(&cs, spinCount);
+ else
+ ::InitializeCriticalSection(&cs);
+#endif
+
+}
+
+
+Lock::~Lock()
+{
+ DeleteCriticalSection(&cs);
+}
+
+
+#endif
+
+
+//-------------------------------------------------------------------------------------
+// ***** SharedLock
+
+// This is a general purpose globally shared Lock implementation that should probably be
+// moved to Kernel.
+// May in theory busy spin-wait if we hit contention on first lock creation,
+// but this shouldn't matter in practice since Lock* should be cached.
+
+
+enum { LockInitMarker = 0xFFFFFFFF };
+
+Lock* SharedLock::GetLockAddRef()
+{
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ if (oldUseCount == LockInitMarker)
+ continue;
+
+ if (oldUseCount == 0)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 0, LockInitMarker))
+ {
+ Construct<Lock>(Buffer);
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 1));
+ return toLock();
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1));
+
+ return toLock();
+}
+
+void SharedLock::ReleaseLock(Lock* plock)
+{
+ OVR_UNUSED(plock);
+ OVR_ASSERT(plock == toLock());
+
+ int oldUseCount;
+
+ do {
+ oldUseCount = UseCount;
+ OVR_ASSERT(oldUseCount != LockInitMarker);
+
+ if (oldUseCount == 1)
+ {
+ // Initialize marker
+ if (AtomicOps<int>::CompareAndSet_Sync(&UseCount, 1, LockInitMarker))
+ {
+ Destruct<Lock>(toLock());
+
+ do { }
+ while (!AtomicOps<int>::CompareAndSet_Sync(&UseCount, LockInitMarker, 0));
+
+ return;
+ }
+ continue;
+ }
+
+ } while (!AtomicOps<int>::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1));
+}
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_Atomic.h b/LibOVR/Src/Kernel/OVR_Atomic.h
new file mode 100644
index 0000000..b826251
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Atomic.h
@@ -0,0 +1,890 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Atomic.h
+Content : Contains atomic operations and inline fastest locking
+ functionality. Will contain #ifdefs for OS efficiency.
+ Have non-thread-safe implementaion if not available.
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+#ifndef OVR_Atomic_h
+#define OVR_Atomic_h
+
+#include "OVR_Types.h"
+
+// Include System thread functionality.
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+
+namespace OVR {
+
+
+// ****** Declared classes
+
+// If there is NO thread support we implement AtomicOps and
+// Lock objects as no-ops. The other classes are not defined.
+template<class C> class AtomicOps;
+template<class T> class AtomicInt;
+template<class T> class AtomicPtr;
+
+class Lock;
+
+
+//-----------------------------------------------------------------------------------
+// ***** AtomicOps
+
+// Atomic operations are provided by the AtomicOps templates class,
+// implemented through system-specific AtomicOpsRaw specializations.
+// It provides several fundamental operations such as Exchange, ExchangeAdd
+// CompareAndSet, and Store_Release. Each function includes several memory
+// synchronization versions, important for multiprocessing CPUs with weak
+// memory consistency. The following memory fencing strategies are supported:
+//
+// - NoSync. No memory synchronization is done for atomic op.
+// - Release. All other memory writes are completed before atomic op
+// writes its results.
+// - Acquire. Further memory reads are forced to wait until atomic op
+// executes, guaranteeing that the right values will be seen.
+// - Sync. A combination of Release and Acquire.
+
+
+// *** AtomicOpsRaw
+
+// AtomicOpsRaw is a specialized template that provides atomic operations
+// used by AtomicOps. This class has two fundamental qualities: (1) it
+// defines a type T of correct size, and (2) provides operations that work
+// atomically, such as Exchange_Sync and CompareAndSet_Release.
+
+// AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw.
+// The primary thing is does is define sync class objects, whose destructor and
+// constructor provide places to insert appropriate synchronization calls, on
+// systems where such calls are necessary. So far, the breakdown is as follows:
+//
+// - X86 systems don't need custom syncs, since their exchange/atomic
+// instructions are implicitly synchronized.
+// - PowerPC requires lwsync/isync instructions that can use this mechanism.
+// - If some other systems require a mechanism where syncing type is associated
+// with a particular instruction, the default implementation (which implements
+// all Sync, Acquire, and Release modes in terms of NoSync and fence) may not
+// work. Ii that case it will need to be #ifdef-ed conditionally.
+
+struct AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE)
+ // Need to have empty constructor to avoid class 'unused' variable warning.
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_MIPS)
+ struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } };
+
+#elif defined(OVR_CPU_ARM)
+ struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } };
+ struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } };
+ struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } };
+
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4)
+ // __sync functions are already full sync
+ struct FullSync { inline FullSync() { } };
+ struct AcquireSync { inline AcquireSync() { } };
+ struct ReleaseSync { inline ReleaseSync() { } };
+#endif
+};
+
+
+// 4-Byte raw data atomic op implementation class.
+struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt32 T;
+
+ // *** Thread - Safe Atomic Versions.
+
+#elif defined(OVR_OS_WIN32)
+
+ // Use special defined for VC6, where volatile is not used and
+ // InterlockedCompareExchange is declared incorrectly.
+ typedef LONG T;
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300)
+ typedef T* InterlockTPtr;
+ typedef LPVOID ET;
+ typedef ET* InterlockETPtr;
+#else
+ typedef volatile T* InterlockTPtr;
+ typedef T ET;
+ typedef InterlockTPtr InterlockETPtr;
+#endif
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; }
+
+#elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC)
+ typedef UInt32 T;
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "stwcx. %[j],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stwcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "lwarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stwcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CPU_MIPS)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret;
+
+ asm volatile("1:\n\t"
+ "ll %[r],0(%[i])\n\t"
+ "addu %[j],%[r],%[j]\n\t"
+ "sc %[j],0(%[i])\n\t"
+ "beq %[j],$0,1b\n\t"
+ "nop \n"
+ : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "move %[r],$0\n\t"
+ "ll %[o],0(%[i])\n\t"
+ "bne %[o],%[c],2f\n\t"
+ "move %[r],%[v]\n\t"
+ "sc %[r],0(%[i])\n\t"
+ "beq %[r],$0,1b\n\t"
+ "nop \n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value)
+ : "cc", "memory");
+
+ return ret;
+ }
+
+#elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(j, i) == 0)
+ return r;
+ }
+ }
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (__strex(r + j, i) == 0)
+ return r;
+ }
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ for(;;)
+ {
+ T r = __ldrex(i);
+ if (r != c)
+ return 0;
+ if (__strex(value, i) == 0)
+ return 1;
+ }
+ }
+
+#elif defined(OVR_CPU_ARM)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "strex %[t],%[j],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ UInt32 ret, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[r],[%[i]]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "strex %[t],%[o],[%[i]]\n\t"
+ "cmp %[t],#0\n\t"
+ "bne 1b\n\t"
+ : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret = 1, dummy, test;
+
+ asm volatile("1:\n\t"
+ "ldrex %[o],[%[i]]\n\t"
+ "cmp %[o],%[c]\n\t"
+ "bne 2f\n\t"
+ "strex %[r],%[v],[%[i]]\n\t"
+ "cmp %[r],#0\n\t"
+ "bne 1b\n\t"
+ "2:\n"
+ : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value)
+ : "cc", "memory");
+
+ return !ret;
+ }
+
+#elif defined(OVR_CPU_X86)
+ typedef UInt32 T;
+
+ static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("xchgl %1,%[i]\n"
+ : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j)
+ {
+ asm volatile("lock; xaddl %1,%[i]\n"
+ : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory");
+
+ return j;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value)
+ {
+ UInt32 ret;
+
+ asm volatile("lock; cmpxchgl %[v],%[i]\n"
+ : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory");
+
+ return (ret == c);
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt32 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// 8-Byte raw data data atomic op implementation class.
+// Currently implementation is provided only on systems with 64-bit pointers.
+struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase
+{
+#if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS)
+
+ // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl.
+ typedef UInt64 T;
+
+ // *** Thread - Safe OS specific versions.
+#elif defined(OVR_OS_WIN32)
+
+ // This is only for 64-bit systems.
+ typedef LONG64 T;
+ typedef volatile T* InterlockTPtr;
+ inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); }
+ inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); }
+ inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; }
+
+#elif defined(OVR_CPU_PPC64)
+
+ typedef UInt64 T;
+
+ static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "mr %[o],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j)
+ {
+ UInt64 dummy, ret;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "add %[o],%[r],%[j]\n\t"
+ "stdcx. %[o],0,%[i]\n\t"
+ "bne- 1b\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc");
+
+ return ret;
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value)
+ {
+ UInt64 ret, dummy;
+
+ asm volatile("1:\n\t"
+ "ldarx %[r],0,%[i]\n\t"
+ "cmpw 0,%[r],%[cmp]\n\t"
+ "mfcr %[r]\n\t"
+ "bne- 2f\n\t"
+ "stdcx. %[val],0,%[i]\n\t"
+ "bne- 1b\n\t"
+ "2:\n"
+ : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc");
+
+ return (ret & 0x20000000) ? 1 : 0;
+ }
+
+#elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1)
+
+ typedef UInt64 T;
+
+ static inline T Exchange_NoSync(volatile T *i, T j)
+ {
+ T v;
+ do {
+ v = *i;
+ } while (!__sync_bool_compare_and_swap(i, v, j));
+ return v;
+ }
+
+ static inline T ExchangeAdd_NoSync(volatile T *i, T j)
+ {
+ return __sync_fetch_and_add(i, j);
+ }
+
+ static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value)
+ {
+ return __sync_bool_compare_and_swap(i, c, value);
+ }
+
+#endif // OS
+};
+
+
+// Default implementation for AtomicOpsRaw; provides implementation of mem-fenced
+// atomic operations where fencing is done with a sync object wrapped around a NoSync
+// operation implemented in the base class. If such implementation is not possible
+// on a given platform, #ifdefs can be used to disable it and then op functions can be
+// implemented individually in the appropriate AtomicOpsRaw<size> class.
+
+template<class O>
+struct AtomicOpsRaw_DefImpl : public O
+{
+ typedef typename O::T O_T;
+ typedef typename O::FullSync O_FullSync;
+ typedef typename O::AcquireSync O_AcquireSync;
+ typedef typename O::ReleaseSync O_ReleaseSync;
+
+ // If there is no thread support, provide the default implementation. In this case,
+ // the base class (0) must still provide the T declaration.
+#ifndef OVR_ENABLE_THREADS
+
+ // Atomic exchange of val with argument. Returns old val.
+ inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; }
+ // Adds a new val to argument; returns its old val.
+ inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; }
+ // Compares the argument data with 'c' val.
+ // If succeeded, stores val int '*p' and returns true; otherwise returns false.
+ inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; }
+
+#endif
+
+ // If NoSync wrapped implementation may not be possible, it this block should be
+ // replaced with per-function implementation in O.
+ // "AtomicOpsRaw_DefImpl<O>::" prefix in calls below.
+ inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::Exchange_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::ExchangeAdd_NoSync(p, val); }
+ inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+ inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl<O>::CompareAndSet_NoSync(p,c,val); }
+
+ // Loads and stores with memory fence. These have only the relevant versions.
+#ifdef OVR_CPU_X86
+ // On X86, Store_Release is implemented as exchange. Note that we can also
+ // consider 'sfence' in the future, although it is not as compatible with older CPUs.
+ inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); }
+#else
+ inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; }
+#endif
+ inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; }
+};
+
+
+template<int size>
+struct AtomicOpsRaw : public AtomicOpsRawBase { };
+
+template<>
+struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>
+{
+ // Ensure that assigned type size is correct.
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_4ByteImpl>::T) == 4); }
+};
+template<>
+struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>
+{
+ AtomicOpsRaw()
+ { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl<AtomicOpsRaw_8ByteImpl>::T) == 8); }
+};
+
+
+// *** AtomicOps - implementation of atomic Ops for specified class
+
+// Implements atomic ops on a class, provided that the object is either
+// 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations
+// available). Relies on AtomicOpsRaw for much of implementation.
+
+template<class C>
+class AtomicOps
+{
+ typedef AtomicOpsRaw<sizeof(C)> Ops;
+ typedef typename Ops::T T;
+ typedef volatile typename Ops::T* PT;
+ // We cast through unions to (1) avoid pointer size compiler warnings
+ // and (2) ensure that there are no problems with strict pointer aliasing.
+ union C2T_union { C c; T t; };
+
+public:
+ // General purpose implementation for standard syncs.
+ inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; }
+ inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; }
+ inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; }
+ inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; }
+ inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; }
+ inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); }
+ inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); }
+ // Loads and stores with memory fence. These have only the relevant versions.
+ inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); }
+ inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; }
+};
+
+
+
+// Atomic value base class - implements operations shared for integers and pointers.
+template<class T>
+class AtomicValueBase
+{
+protected:
+ typedef AtomicOps<T> Ops;
+public:
+
+ volatile T Value;
+
+ inline AtomicValueBase() { }
+ explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); }
+
+ // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire
+ // here, since most algorithms do not require atomic loads. Needs some research.
+ inline operator T() const { return Value; }
+
+ // *** Standard Atomic inlines
+ inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); }
+ inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); }
+ inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); }
+ inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); }
+ inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); }
+ inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); }
+ inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); }
+ inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); }
+ // Load & Store.
+ inline void Store_Release(T val) { Ops::Store_Release(&Value, val); }
+ inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); }
+};
+
+
+// ***** AtomicPtr - Atomic pointer template
+
+// This pointer class supports atomic assignments with release,
+// increment / decrement operations, and conditional compare + set.
+
+template<class T>
+class AtomicPtr : public AtomicValueBase<T*>
+{
+ typedef typename AtomicValueBase<T*>::Ops Ops;
+
+public:
+ // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor.
+ inline AtomicPtr() : AtomicValueBase<T*>() { this->Value = 0; }
+ explicit inline AtomicPtr(T* val) : AtomicValueBase<T*>(val) { }
+
+ // Pointer access.
+ inline T* operator -> () const { return this->Load_Acquire(); }
+
+ // It looks like it is convenient to have Load_Acquire characteristics
+ // for this, since that is convenient for algorithms such as linked
+ // list traversals that can be added to bu another thread.
+ inline operator T* () const { return this->Load_Acquire(); }
+
+
+ // *** Standard Atomic inlines (applicable to pointers)
+
+ // ExhangeAdd considers pointer size for pointers.
+ template<class I>
+ inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); }
+ template<class I>
+ inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); }
+
+ // *** Atomic Operators
+
+ inline T* operator = (T* val) { this->Store_Release(val); return val; }
+
+ template<class I>
+ inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; }
+ template<class I>
+ inline T* operator -= (I val) { return operator += (-val); }
+
+ inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; }
+ inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; }
+ inline T* operator ++ (int) { return ExchangeAdd_Sync(1); }
+ inline T* operator -- (int) { return ExchangeAdd_Sync(-1); }
+};
+
+
+// ***** AtomicInt - Atomic integer template
+
+// Implements an atomic integer type; the exact type to use is provided
+// as an argument. Supports atomic Acquire / Release semantics, atomic
+// arithmetic operations, and atomic conditional compare + set.
+
+template<class T>
+class AtomicInt : public AtomicValueBase<T>
+{
+ typedef typename AtomicValueBase<T>::Ops Ops;
+
+public:
+ inline AtomicInt() : AtomicValueBase<T>() { }
+ explicit inline AtomicInt(T val) : AtomicValueBase<T>(val) { }
+
+
+ // *** Standard Atomic inlines (applicable to int)
+ inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); }
+ inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); }
+ inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); }
+ inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); }
+ // These increments could be more efficient because they don't return a value.
+ inline void Increment_Sync() { ExchangeAdd_Sync((T)1); }
+ inline void Increment_Release() { ExchangeAdd_Release((T)1); }
+ inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); }
+ inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); }
+
+ // *** Atomic Operators
+
+ inline T operator = (T val) { this->Store_Release(val); return val; }
+ inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; }
+ inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; }
+
+ inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; }
+ inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; }
+ inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); }
+ inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); }
+
+ // More complex atomic operations. Leave it to compiler whether to optimize them or not.
+ T operator &= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp & arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator |= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp | arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator ^= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp ^ arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator *= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp * arg;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator /= (T arg)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp / arg;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator >>= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp >> bits;
+ } while(!CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+
+ T operator <<= (unsigned bits)
+ {
+ T comp, newVal;
+ do {
+ comp = this->Value;
+ newVal = comp << bits;
+ } while(!this->CompareAndSet_Sync(comp, newVal));
+ return newVal;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Lock
+
+// Lock is a simplest and most efficient mutual-exclusion lock class.
+// Unlike Mutex, it cannot be waited on.
+
+class Lock
+{
+ // NOTE: Locks are not allocatable and they themselves should not allocate
+ // memory by standard means. This is the case because StandardAllocator
+ // relies on this class.
+ // Make 'delete' private. Don't do this for 'new' since it can be redefined.
+ void operator delete(void*) {}
+
+
+ // *** Lock implementation for various platforms.
+
+#if !defined(OVR_ENABLE_THREADS)
+
+public:
+ // With no thread support, lock does nothing.
+ inline Lock() { }
+ inline Lock(unsigned) { }
+ inline ~Lock() { }
+ inline void DoLock() { }
+ inline void Unlock() { }
+
+ // Windows.
+#elif defined(OVR_OS_WIN32)
+
+ CRITICAL_SECTION cs;
+public:
+ Lock(unsigned spinCount = 0);
+ ~Lock();
+ // Locking functions.
+ inline void DoLock() { ::EnterCriticalSection(&cs); }
+ inline void Unlock() { ::LeaveCriticalSection(&cs); }
+
+#else
+ pthread_mutex_t mutex;
+
+public:
+ static pthread_mutexattr_t RecursiveAttr;
+ static bool RecursiveAttrInit;
+
+ Lock (unsigned dummy = 0)
+ {
+ OVR_UNUSED(dummy);
+ if (!RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&RecursiveAttr);
+ pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ RecursiveAttrInit = 1;
+ }
+ pthread_mutex_init(&mutex,&RecursiveAttr);
+ }
+ ~Lock () { pthread_mutex_destroy(&mutex); }
+ inline void DoLock() { pthread_mutex_lock(&mutex); }
+ inline void Unlock() { pthread_mutex_unlock(&mutex); }
+
+#endif // OVR_ENABLE_THREDS
+
+
+public:
+ // Locker class, used for automatic locking
+ class Locker
+ {
+ public:
+ Lock *pLock;
+ inline Locker(Lock *plock)
+ { pLock = plock; pLock->DoLock(); }
+ inline ~Locker()
+ { pLock->Unlock(); }
+ };
+};
+
+
+//-------------------------------------------------------------------------------------
+// Globally shared Lock implementation used for MessageHandlers, etc.
+
+class SharedLock
+{
+public:
+ SharedLock() : UseCount(0) {}
+
+ Lock* GetLockAddRef();
+ void ReleaseLock(Lock* plock);
+
+private:
+ Lock* toLock() { return (Lock*)Buffer; }
+
+ // UseCount and max alignment.
+ volatile int UseCount;
+ UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)];
+};
+
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Color.h b/LibOVR/Src/Kernel/OVR_Color.h
new file mode 100644
index 0000000..cf536da
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Color.h
@@ -0,0 +1,66 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Color.h
+Content : Contains color struct.
+Created : February 7, 2013
+Notes :
+
+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.
+
+************************************************************************************/
+#ifndef OVR_Color_h
+#define OVR_Color_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+struct Color
+{
+ UByte R,G,B,A;
+
+ Color() {}
+
+ // Constructs color by channel. Alpha is set to 0xFF (fully visible)
+ // if not specified.
+ Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF)
+ : R(r), G(g), B(b), A(a) { }
+
+ // 0xAARRGGBB - Common HTML color Hex layout
+ Color(unsigned c)
+ : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)),
+ B((unsigned char)c), A((unsigned char)(c>>24)) { }
+
+ bool operator==(const Color& b) const
+ {
+ return R == b.R && G == b.G && B == b.B && A == b.A;
+ }
+
+ void GetRGBA(float *r, float *g, float *b, float* a) const
+ {
+ *r = R / 255.0f;
+ *g = G / 255.0f;
+ *b = B / 255.0f;
+ *a = A / 255.0f;
+ }
+};
+
+}
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_ContainerAllocator.h b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
new file mode 100644
index 0000000..afc0e6a
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
@@ -0,0 +1,267 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_ContainerAllocator.h
+Content : Template allocators and constructors for containers.
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_ContainerAllocator_h
+#define OVR_ContainerAllocator_h
+
+#include "OVR_Allocator.h"
+#include <string.h>
+
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator
+
+// ContainerAllocator serves as a template argument for allocations done by
+// containers, such as Array and Hash; replacing it could allow allocator
+// substitution in containers.
+
+class ContainerAllocatorBase
+{
+public:
+ static void* Alloc(UPInt size) { return OVR_ALLOC(size); }
+ static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); }
+ static void Free(void *p) { OVR_FREE(p); }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Constructors, Destructors, Copiers
+
+// Plain Old Data - movable, no special constructors/destructor.
+template<class T>
+class ConstructorPOD
+{
+public:
+ static void Construct(void *) {}
+ static void Construct(void *p, const T& source)
+ {
+ *(T*)p = source;
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void *p, const S& source)
+ {
+ *(T*)p = source;
+ }
+
+ static void ConstructArray(void*, UPInt) {}
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte *pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ *(T*)pdata = source;
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ memcpy(p, psource, sizeof(T) * count);
+ }
+
+ static void Destruct(T*) {}
+ static void DestructArray(T*, UPInt) {}
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorMov
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorMov
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ memmove(dst, src, count * sizeof(T));
+ }
+
+ static bool IsMovable() { return true; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** ConstructorCPP
+//
+// Correct C++ construction and destruction for movable objects
+template<class T>
+class ConstructorCPP
+{
+public:
+ static void Construct(void* p)
+ {
+ OVR::Construct<T>(p);
+ }
+
+ static void Construct(void* p, const T& source)
+ {
+ OVR::Construct<T>(p, source);
+ }
+
+ // Same as above, but allows for a different type of constructor.
+ template <class S>
+ static void ConstructAlt(void* p, const S& source)
+ {
+ OVR::ConstructAlt<T,S>(p, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T& source)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, source);
+ }
+
+ static void ConstructArray(void* p, UPInt count, const T* psource)
+ {
+ UByte* pdata = (UByte*)p;
+ for (UPInt i=0; i< count; ++i, pdata += sizeof(T))
+ Construct(pdata, *psource++);
+ }
+
+ static void Destruct(T* p)
+ {
+ p->~T();
+ OVR_UNUSED(p); // Suppress silly MSVC warning
+ }
+
+ static void DestructArray(T* p, UPInt count)
+ {
+ p += count - 1;
+ for (UPInt i=0; i<count; ++i, --p)
+ p->~T();
+ }
+
+ static void CopyArrayForward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = 0; i < count; ++i)
+ dst[i] = src[i];
+ }
+
+ static void CopyArrayBackward(T* dst, const T* src, UPInt count)
+ {
+ for(UPInt i = count; i; --i)
+ dst[i-1] = src[i-1];
+ }
+
+ static bool IsMovable() { return false; }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Container Allocator with movement policy
+//
+// Simple wraps as specialized allocators
+template<class T> struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD<T> {};
+template<class T> struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov<T> {};
+template<class T> struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP<T> {};
+
+
+} // OVR
+
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Deque.h b/LibOVR/Src/Kernel/OVR_Deque.h
new file mode 100644
index 0000000..ca242ad
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Deque.h
@@ -0,0 +1,296 @@
+/************************************************************************************
+
+Filename : OVR_Deque.h
+Content : Deque container
+Created : Nov. 15, 2013
+Authors : Dov Katz
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Deque_h
+#define OVR_Deque_h
+
+namespace OVR{
+
+template <class Elem>
+class Deque
+{
+public:
+ enum
+ {
+ DefaultCapacity = 500
+ };
+
+ Deque(int capacity = DefaultCapacity);
+ virtual ~Deque(void);
+
+ virtual void PushBack (const Elem &Item); // Adds Item to the end
+ virtual void PushFront (const Elem &Item); // Adds Item to the beginning
+ virtual Elem PopBack (void); // Removes Item from the end
+ virtual Elem PopFront (void); // Removes Item from the beginning
+ virtual const Elem& PeekBack (int count = 0) const; // Returns count-th Item from the end
+ virtual const Elem& PeekFront (int count = 0) const; // Returns count-th Item from the beginning
+
+ virtual inline UPInt GetSize (void) const; // Returns Number of Elements
+ virtual inline UPInt GetCapacity(void) const; // Returns the maximum possible number of elements
+ virtual void Clear (void); // Remove all elements
+ virtual inline bool IsEmpty () const;
+ virtual inline bool IsFull () const;
+
+protected:
+ Elem *Data; // The actual Data array
+ const int Capacity; // Deque capacity
+ int Beginning; // Index of the first element
+ int End; // Index of the next after last element
+
+ // Instead of calculating the number of elements, using this variable
+ // is much more convenient.
+ int ElemCount;
+
+private:
+ Deque& operator= (const Deque& q) { }; // forbidden
+ Deque(const Deque<Elem> &OtherDeque) { };
+};
+
+template <class Elem>
+class InPlaceMutableDeque : public Deque<Elem>
+{
+public:
+ InPlaceMutableDeque( int capacity = Deque<Elem>::DefaultCapacity ) : Deque<Elem>( capacity ) {}
+ virtual ~InPlaceMutableDeque() {};
+
+ using Deque<Elem>::PeekBack;
+ using Deque<Elem>::PeekFront;
+ virtual Elem& PeekBack (int count = 0); // Returns count-th Item from the end
+ virtual Elem& PeekFront (int count = 0); // Returns count-th Item from the beginning
+};
+
+// Same as Deque, but allows to write more elements than maximum capacity
+// Old elements are lost as they are overwritten with the new ones
+template <class Elem>
+class CircularBuffer : public InPlaceMutableDeque<Elem>
+{
+public:
+ CircularBuffer(int MaxSize = Deque<Elem>::DefaultCapacity) : InPlaceMutableDeque<Elem>(MaxSize) { };
+
+ // The following methods are inline as a workaround for a VS bug causing erroneous C4505 warnings
+ // See: http://stackoverflow.com/questions/3051992/compiler-warning-at-c-template-base-class
+ inline virtual void PushBack (const Elem &Item); // Adds Item to the end, overwriting the oldest element at the beginning if necessary
+ inline virtual void PushFront (const Elem &Item); // Adds Item to the beginning, overwriting the oldest element at the end if necessary
+};
+
+//----------------------------------------------------------------------------------
+
+// Deque Constructor function
+template <class Elem>
+Deque<Elem>::Deque(int capacity) :
+Capacity( capacity ), Beginning(0), End(0), ElemCount(0)
+{
+ Data = (Elem*) OVR_ALLOC(Capacity * sizeof(Elem));
+ ConstructArray<Elem>(Data, Capacity);
+}
+
+// Deque Destructor function
+template <class Elem>
+Deque<Elem>::~Deque(void)
+{
+ DestructArray<Elem>(Data, Capacity);
+ OVR_FREE(Data);
+}
+
+template <class Elem>
+void Deque<Elem>::Clear()
+{
+ Beginning = 0;
+ End = 0;
+ ElemCount = 0;
+
+ DestructArray<Elem>(Data, Capacity);
+ ConstructArray<Elem>(Data, Capacity);
+}
+
+// Push functions
+template <class Elem>
+void Deque<Elem>::PushBack(const Elem &Item)
+{
+ // Error Check: Make sure we aren't
+ // exceeding our maximum storage space
+ OVR_ASSERT( ElemCount < Capacity );
+
+ Data[ End++ ] = Item;
+ ++ElemCount;
+
+ // Check for wrap-around
+ if (End >= Capacity)
+ End -= Capacity;
+}
+
+template <class Elem>
+void Deque<Elem>::PushFront(const Elem &Item)
+{
+ // Error Check: Make sure we aren't
+ // exceeding our maximum storage space
+ OVR_ASSERT( ElemCount < Capacity );
+
+ Beginning--;
+ // Check for wrap-around
+ if (Beginning < 0)
+ Beginning += Capacity;
+
+ Data[ Beginning ] = Item;
+ ++ElemCount;
+}
+
+// Pop functions
+template <class Elem>
+Elem Deque<Elem>::PopFront(void)
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( ElemCount > 0 );
+
+ Elem ReturnValue = Data[ Beginning ];
+ Destruct<Elem>(&Data[ Beginning ]);
+ Construct<Elem>(&Data[ Beginning ]);
+
+ ++Beginning;
+ --ElemCount;
+
+ // Check for wrap-around
+ if (Beginning >= Capacity)
+ Beginning -= Capacity;
+
+ return ReturnValue;
+}
+
+template <class Elem>
+Elem Deque<Elem>::PopBack(void)
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( ElemCount > 0 );
+
+ End--;
+ --ElemCount;
+
+ // Check for wrap-around
+ if (End < 0)
+ End += Capacity;
+
+ Elem ReturnValue = Data[ End ];
+ Destruct<Elem>(&Data[ End ]);
+ Construct<Elem>(&Data[ End ]);
+
+ return ReturnValue;
+}
+
+// Peek functions
+template <class Elem>
+const Elem& Deque<Elem>::PeekFront(int count) const
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( ElemCount > count );
+
+ int idx = Beginning + count;
+ if (idx >= Capacity)
+ idx -= Capacity;
+ return Data[ idx ];
+}
+
+template <class Elem>
+const Elem& Deque<Elem>::PeekBack(int count) const
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( ElemCount > count );
+
+ int idx = End - count - 1;
+ if (idx < 0)
+ idx += Capacity;
+ return Data[ idx ];
+}
+
+// Mutable Peek functions
+template <class Elem>
+Elem& InPlaceMutableDeque<Elem>::PeekFront(int count)
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( Deque<Elem>::ElemCount > count );
+
+ int idx = Deque<Elem>::Beginning + count;
+ if (idx >= Deque<Elem>::Capacity)
+ idx -= Deque<Elem>::Capacity;
+ return Deque<Elem>::Data[ idx ];
+}
+
+template <class Elem>
+Elem& InPlaceMutableDeque<Elem>::PeekBack(int count)
+{
+ // Error Check: Make sure we aren't reading from an empty Deque
+ OVR_ASSERT( Deque<Elem>::ElemCount > count );
+
+ int idx = Deque<Elem>::End - count - 1;
+ if (idx < 0)
+ idx += Deque<Elem>::Capacity;
+ return Deque<Elem>::Data[ idx ];
+}
+
+template <class Elem>
+inline UPInt Deque<Elem>::GetCapacity(void) const
+{
+ return Deque<Elem>::Capacity;
+}
+
+template <class Elem>
+inline UPInt Deque<Elem>::GetSize(void) const
+{
+ return Deque<Elem>::ElemCount;
+}
+
+template <class Elem>
+inline bool Deque<Elem>::IsEmpty(void) const
+{
+ return Deque<Elem>::ElemCount==0;
+}
+
+template <class Elem>
+inline bool Deque<Elem>::IsFull(void) const
+{
+ return Deque<Elem>::ElemCount==Deque<Elem>::Capacity;
+}
+
+// ******* CircularBuffer<Elem> *******
+// Push functions
+template <class Elem>
+void CircularBuffer<Elem>::PushBack(const Elem &Item)
+{
+ if (this->IsFull())
+ this->PopFront();
+ Deque<Elem>::PushBack(Item);
+}
+
+template <class Elem>
+void CircularBuffer<Elem>::PushFront(const Elem &Item)
+{
+ if (this->IsFull())
+ this->PopBack();
+ Deque<Elem>::PushFront(Item);
+}
+
+};
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_File.cpp b/LibOVR/Src/Kernel/OVR_File.cpp
new file mode 100644
index 0000000..31ab516
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_File.cpp
@@ -0,0 +1,582 @@
+/**************************************************************************
+
+Filename : OVR_File.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+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.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// Buffered file adds buffering to an existing file
+// FILEBUFFER_SIZE defines the size of internal buffer, while
+// FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer
+#define FILEBUFFER_SIZE (8192-8)
+#define FILEBUFFER_TOLERANCE 4096
+
+// ** Constructor/Destructor
+
+// Hidden constructor
+// Not supposed to be used
+BufferedFile::BufferedFile() : DelegatedFile(0)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = 0;
+ Pos = 0;
+ DataSize = 0;
+}
+
+// Takes another file as source
+BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile)
+{
+ pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE);
+ BufferMode = NoBuffer;
+ FilePos = pfile->LTell();
+ Pos = 0;
+ DataSize = 0;
+}
+
+
+// Destructor
+BufferedFile::~BufferedFile()
+{
+ // Flush in case there's data
+ if (pFile)
+ FlushBuffer();
+ // Get rid of buffer
+ if (pBuffer)
+ OVR_FREE(pBuffer);
+}
+
+/*
+bool BufferedFile::VCopy(const Object &source)
+{
+ if (!DelegatedFile::VCopy(source))
+ return 0;
+
+ // Data members
+ BufferedFile *psource = (BufferedFile*)&source;
+
+ // Buffer & the mode it's in
+ pBuffer = psource->pBuffer;
+ BufferMode = psource->BufferMode;
+ Pos = psource->Pos;
+ DataSize = psource->DataSize;
+ return 1;
+}
+*/
+
+// Initializes buffering to a certain mode
+bool BufferedFile::SetBufferMode(BufferModeType mode)
+{
+ if (!pBuffer)
+ return false;
+ if (mode == BufferMode)
+ return true;
+
+ FlushBuffer();
+
+ // Can't set write mode if we can't write
+ if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) )
+ return 0;
+
+ // And SetMode
+ BufferMode = mode;
+ Pos = 0;
+ DataSize = 0;
+ return 1;
+}
+
+// Flushes buffer
+void BufferedFile::FlushBuffer()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ // Write data in buffer
+ FilePos += pFile->Write(pBuffer,Pos);
+ Pos = 0;
+ break;
+
+ case ReadBuffer:
+ // Seek back & reset buffer data
+ if ((DataSize-Pos)>0)
+ FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur);
+ DataSize = 0;
+ Pos = 0;
+ break;
+ default:
+ // not handled!
+ break;
+ }
+}
+
+// Reloads data for ReadBuffer
+void BufferedFile::LoadBuffer()
+{
+ if (BufferMode == ReadBuffer)
+ {
+ // We should only reload once all of pre-loaded buffer is consumed.
+ OVR_ASSERT(Pos == DataSize);
+
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE);
+ DataSize = sz<0 ? 0 : (unsigned)sz;
+ Pos = 0;
+ FilePos += DataSize;
+ }
+}
+
+
+// ** Overridden functions
+
+// We override all the functions that can possibly
+// require buffer mode switch, flush, or extra calculations
+
+// Tell() requires buffer adjustment
+int BufferedFile::Tell()
+{
+ if (BufferMode == ReadBuffer)
+ return int (FilePos - DataSize + Pos);
+
+ int pos = pFile->Tell();
+ // Adjust position based on buffer mode & data
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+SInt64 BufferedFile::LTell()
+{
+ if (BufferMode == ReadBuffer)
+ return FilePos - DataSize + Pos;
+
+ SInt64 pos = pFile->LTell();
+ if (pos!=-1)
+ {
+ OVR_ASSERT(BufferMode != ReadBuffer);
+ if (BufferMode == WriteBuffer)
+ pos += Pos;
+ }
+ return pos;
+}
+
+int BufferedFile::GetLength()
+{
+ int len = pFile->GetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ int currPos = pFile->Tell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+SInt64 BufferedFile::LGetLength()
+{
+ SInt64 len = pFile->LGetLength();
+ // If writing through buffer, file length may actually be bigger
+ if ((len!=-1) && (BufferMode==WriteBuffer))
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos>len)
+ len = currPos;
+ }
+ return len;
+}
+
+/*
+bool BufferedFile::Stat(FileStats *pfs)
+{
+ // Have to fix up length is stat
+ if (pFile->Stat(pfs))
+ {
+ if (BufferMode==WriteBuffer)
+ {
+ SInt64 currPos = pFile->LTell() + Pos;
+ if (currPos > pfs->Size)
+ {
+ pfs->Size = currPos;
+ // ??
+ pfs->Blocks = (pfs->Size+511) >> 9;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+*/
+
+int BufferedFile::Write(const UByte *psourceBuffer, int numBytes)
+{
+ if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer))
+ {
+ // If not data space in buffer, flush
+ if ((FILEBUFFER_SIZE-(int)Pos)<numBytes)
+ {
+ FlushBuffer();
+ // If bigger then tolerance, just write directly
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+ }
+ }
+
+ // Enough space in buffer.. so copy to it
+ memcpy(pBuffer+Pos, psourceBuffer, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+ int sz = pFile->Write(psourceBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+int BufferedFile::Read(UByte *pdestBuffer, int numBytes)
+{
+ if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer))
+ {
+ // Data in buffer... copy it
+ if ((int)(DataSize-Pos) >= numBytes)
+ {
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes;
+ }
+
+ // Not enough data in buffer, copy buffer
+ int readBytes = DataSize-Pos;
+ memcpy(pdestBuffer, pBuffer+Pos, readBytes);
+ numBytes -= readBytes;
+ pdestBuffer += readBytes;
+ Pos = DataSize;
+
+ // Don't reload buffer if more then tolerance
+ // (No major advantage, and we don't want to write a loop)
+ if (numBytes>FILEBUFFER_TOLERANCE)
+ {
+ numBytes = pFile->Read(pdestBuffer,numBytes);
+ if (numBytes > 0)
+ {
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ return readBytes + ((numBytes==-1) ? 0 : numBytes);
+ }
+
+ // Reload the buffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ LoadBuffer();
+ if ((int)(DataSize-Pos) < numBytes)
+ numBytes = (int)DataSize-Pos;
+
+ memcpy(pdestBuffer, pBuffer+Pos, numBytes);
+ Pos += numBytes;
+ return numBytes + readBytes;
+
+ /*
+ // Alternative Read implementation. The one above is probably better
+ // due to FILEBUFFER_TOLERANCE.
+ int total = 0;
+
+ do {
+ int bufferBytes = (int)(DataSize-Pos);
+ int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes;
+
+ memcpy(pdestBuffer, pBuffer+Pos, copyBytes);
+ numBytes -= copyBytes;
+ pdestBuffer += copyBytes;
+ Pos += copyBytes;
+ total += copyBytes;
+
+ if (numBytes == 0)
+ break;
+ LoadBuffer();
+
+ } while (DataSize > 0);
+
+ return total;
+ */
+ }
+ int sz = pFile->Read(pdestBuffer,numBytes);
+ if (sz > 0)
+ FilePos += sz;
+ return sz;
+}
+
+
+int BufferedFile::SkipBytes(int numBytes)
+{
+ int skippedBytes = 0;
+
+ // Special case for skipping a little data in read buffer
+ if (BufferMode==ReadBuffer)
+ {
+ skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos);
+ Pos += skippedBytes;
+ numBytes -= skippedBytes;
+ }
+
+ if (numBytes)
+ {
+ numBytes = pFile->SkipBytes(numBytes);
+ // Make sure we return the actual number skipped, or error
+ if (numBytes!=-1)
+ {
+ skippedBytes += numBytes;
+ FilePos += numBytes;
+ Pos = DataSize = 0;
+ }
+ else if (skippedBytes <= 0)
+ skippedBytes = -1;
+ }
+ return skippedBytes;
+}
+
+int BufferedFile::BytesAvailable()
+{
+ int available = pFile->BytesAvailable();
+ // Adjust available size based on buffers
+ switch(BufferMode)
+ {
+ case ReadBuffer:
+ available += DataSize-Pos;
+ break;
+ case WriteBuffer:
+ available -= Pos;
+ if (available<0)
+ available= 0;
+ break;
+ default:
+ break;
+ }
+ return available;
+}
+
+bool BufferedFile::Flush()
+{
+ FlushBuffer();
+ return pFile->Flush();
+}
+
+// Seeking could be optimized better..
+int BufferedFile::Seek(int offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += offset;
+ return int (FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0);
+ offset = (int)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((unsigned)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0);
+ Pos = (unsigned)offset - (unsigned)(FilePos-DataSize);
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+ /*
+ // Old Seek Logic
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset));
+ Pos += offset;
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+ else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos)
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ OVR_ASSERT(int (Pos) >= 0);
+ return int (FilePos - DataSize + Pos);
+ }
+
+ FlushBuffer();
+ */
+
+
+ FilePos = pFile->Seek(offset,origin);
+ return int (FilePos);
+}
+
+SInt64 BufferedFile::LSeek(SInt64 offset, int origin)
+{
+ if (BufferMode == ReadBuffer)
+ {
+ if (origin == Seek_Cur)
+ {
+ // Seek can fall either before or after Pos in the buffer,
+ // but it must be within bounds.
+ if (((unsigned(offset) + Pos)) <= DataSize)
+ {
+ Pos += (unsigned)offset;
+ return SInt64(FilePos - DataSize + Pos);
+ }
+
+ // Lightweight buffer "Flush". We do this to avoid an extra seek
+ // back operation which would take place if we called FlushBuffer directly.
+ origin = Seek_Set;
+ offset = (SInt64)(FilePos - DataSize + Pos) + offset;
+ Pos = DataSize = 0;
+ }
+ else if (origin == Seek_Set)
+ {
+ if (((UInt64)offset - (FilePos-DataSize)) <= DataSize)
+ {
+ Pos = (unsigned)((UInt64)offset - (FilePos-DataSize));
+ return offset;
+ }
+ Pos = DataSize = 0;
+ }
+ else
+ {
+ FlushBuffer();
+ }
+ }
+ else
+ {
+ FlushBuffer();
+ }
+
+/*
+ OVR_ASSERT(BufferMode != NoBuffer);
+
+ if (origin == Seek_Cur && offset + Pos < DataSize)
+ {
+ Pos += int (offset);
+ return FilePos - DataSize + Pos;
+ }
+ else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos))
+ {
+ Pos = unsigned(offset - FilePos + DataSize);
+ return FilePos - DataSize + Pos;
+ }
+
+ FlushBuffer();
+ */
+
+ FilePos = pFile->LSeek(offset,origin);
+ return FilePos;
+}
+
+int BufferedFile::CopyFromStream(File *pstream, int byteSize)
+{
+ // We can't rely on overridden Write()
+ // because delegation doesn't override virtual pointers
+ // So, just re-implement
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while(byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff,szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff,szRead);
+
+ count +=szWritten;
+ byteSize-=szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+// Closing files
+bool BufferedFile::Close()
+{
+ switch(BufferMode)
+ {
+ case WriteBuffer:
+ FlushBuffer();
+ break;
+ case ReadBuffer:
+ // No need to seek back on close
+ BufferMode = NoBuffer;
+ break;
+ default:
+ break;
+ }
+ return pFile->Close();
+}
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl)
+{
+ UPInt len = OVR_strlen(purl);
+ for (UPInt i=len; i>0; i--)
+ if (purl[i]=='\\' || purl[i]=='/')
+ return purl+i+1;
+ return purl;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_File.h b/LibOVR/Src/Kernel/OVR_File.h
new file mode 100644
index 0000000..a8dc615
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_File.h
@@ -0,0 +1,529 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_File.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of BaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_File_h
+#define OVR_File_h
+
+#include "OVR_RefCount.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+#include <stdio.h>
+#include "OVR_String.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class FileConstants;
+class File;
+class DelegatedFile;
+class BufferedFile;
+
+
+// ***** Flags for File & Directory accesses
+
+class FileConstants
+{
+public:
+
+ // *** File open flags
+ enum OpenFlags
+ {
+ Open_Read = 1,
+ Open_Write = 2,
+ Open_ReadWrite = 3,
+
+ // Opens file and truncates it to zero length
+ // - file must have write permission
+ // - when used with Create, it opens an existing
+ // file and empties it or creates a new file
+ Open_Truncate = 4,
+
+ // Creates and opens new file
+ // - does not erase contents if file already
+ // exists unless combined with Truncate
+ Open_Create = 8,
+
+ // Returns an error value if the file already exists
+ Open_CreateOnly = 24,
+
+ // Open file with buffering
+ Open_Buffered = 32
+ };
+
+ // *** File Mode flags
+ enum Modes
+ {
+ Mode_Read = 0444,
+ Mode_Write = 0222,
+ Mode_Execute = 0111,
+
+ Mode_ReadWrite = 0666
+ };
+
+ // *** Seek operations
+ enum SeekOps
+ {
+ Seek_Set = 0,
+ Seek_Cur = 1,
+ Seek_End = 2
+ };
+
+ // *** Errors
+ enum Errors
+ {
+ Error_FileNotFound = 0x1001,
+ Error_Access = 0x1002,
+ Error_IOError = 0x1003,
+ Error_DiskFull = 0x1004
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** File Class
+
+// The pure virtual base random-access file
+// This is a base class to all files
+
+class File : public RefCountBase<File>, public FileConstants
+{
+public:
+ File() { }
+ // ** Location Information
+
+ // Returns a file name path relative to the 'reference' directory
+ // This is often a path that was used to create a file
+ // (this is not a global path, global path can be obtained with help of directory)
+ virtual const char* GetFilePath() = 0;
+
+
+ // ** File Information
+
+ // Return 1 if file's usable (open)
+ virtual bool IsValid() = 0;
+ // Return 1 if file's writable, otherwise 0
+ virtual bool IsWritable() = 0;
+
+ // Return position
+ virtual int Tell() = 0;
+ virtual SInt64 LTell() = 0;
+
+ // File size
+ virtual int GetLength() = 0;
+ virtual SInt64 LGetLength() = 0;
+
+ // Returns file stats
+ // 0 for failure
+ //virtual bool Stat(FileStats *pfs) = 0;
+
+ // Return errno-based error code
+ // Useful if any other function failed
+ virtual int GetErrorCode() = 0;
+
+
+ // ** Stream implementation & I/O
+
+ // Blocking write, will write in the given number of bytes to the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read
+ virtual int Write(const UByte *pbufer, int numBytes) = 0;
+ // Blocking read, will read in the given number of bytes or less from the stream
+ // Returns : -1 for error
+ // Otherwise number of bytes read,
+ // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed
+ virtual int Read(UByte *pbufer, int numBytes) = 0;
+
+ // Skips (ignores) a given # of bytes
+ // Same return values as Read
+ virtual int SkipBytes(int numBytes) = 0;
+
+ // Returns the number of bytes available to read from a stream without blocking
+ // For a file, this should generally be number of bytes to the end
+ virtual int BytesAvailable() = 0;
+
+ // Causes any implementation's buffered data to be delivered to destination
+ // Return 0 for error
+ virtual bool Flush() = 0;
+
+
+ // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking
+ inline bool IsEOF() { return !BytesAvailable(); }
+
+
+ // Seeking
+ // Returns new position, -1 for error
+ virtual int Seek(int offset, int origin=Seek_Set) = 0;
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0;
+ // Seek simplification
+ int SeekToBegin() {return Seek(0); }
+ int SeekToEnd() {return Seek(0,Seek_End); }
+ int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); }
+
+
+ // Appends other file data from a stream
+ // Return -1 for error, else # of bytes written
+ virtual int CopyFromStream(File *pstream, int byteSize) = 0;
+
+ // Closes the file
+ // After close, file cannot be accessed
+ virtual bool Close() = 0;
+
+
+ // ***** Inlines for convenient primitive type serialization
+
+ // Read/Write helpers
+private:
+ UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; }
+ UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; }
+ UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; }
+ UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; }
+ void PWrite64(UInt64 v) { Write((UByte*)&v, 8); }
+ void PWrite32(UInt32 v) { Write((UByte*)&v, 4); }
+ void PWrite16(UInt16 v) { Write((UByte*)&v, 2); }
+ void PWrite8(UByte v) { Write((UByte*)&v, 1); }
+
+public:
+
+ // Writing primitive types - Little Endian
+ inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); }
+ inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); }
+ inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); }
+ // Writing primitive types - Big Endian
+ inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); }
+ inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); }
+ inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); }
+
+ // Reading primitive types - Little Endian
+ inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); }
+ inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); }
+ inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); }
+ // Reading primitive types - Big Endian
+ inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); }
+ inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); }
+ inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); }
+ inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); }
+ inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); }
+ inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); }
+};
+
+
+// *** Delegated File
+
+class DelegatedFile : public File
+{
+protected:
+ // Delegating file pointer
+ Ptr<File> pFile;
+
+ // Hidden default constructor
+ DelegatedFile() : pFile(0) { }
+ DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); }
+public:
+ // Constructors
+ DelegatedFile(File *pfile) : pFile(pfile) { }
+
+ // ** Location Information
+ virtual const char* GetFilePath() { return pFile->GetFilePath(); }
+
+ // ** File Information
+ virtual bool IsValid() { return pFile && pFile->IsValid(); }
+ virtual bool IsWritable() { return pFile->IsWritable(); }
+// virtual bool IsRecoverable() { return pFile->IsRecoverable(); }
+
+ virtual int Tell() { return pFile->Tell(); }
+ virtual SInt64 LTell() { return pFile->LTell(); }
+
+ virtual int GetLength() { return pFile->GetLength(); }
+ virtual SInt64 LGetLength() { return pFile->LGetLength(); }
+
+ //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); }
+
+ virtual int GetErrorCode() { return pFile->GetErrorCode(); }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); }
+
+ virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); }
+
+ virtual int BytesAvailable() { return pFile->BytesAvailable(); }
+
+ virtual bool Flush() { return pFile->Flush(); }
+
+ // Seeking
+ virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); }
+
+ // Closing the file
+ virtual bool Close() { return pFile->Close(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Buffered File
+
+// This file class adds buffering to an existing file
+// Buffered file never fails by itself; if there's not
+// enough memory for buffer, no buffer's used
+
+class BufferedFile : public DelegatedFile
+{
+protected:
+ enum BufferModeType
+ {
+ NoBuffer,
+ ReadBuffer,
+ WriteBuffer
+ };
+
+ // Buffer & the mode it's in
+ UByte* pBuffer;
+ BufferModeType BufferMode;
+ // Position in buffer
+ unsigned Pos;
+ // Data in buffer if reading
+ unsigned DataSize;
+ // Underlying file position
+ UInt64 FilePos;
+
+ // Initializes buffering to a certain mode
+ bool SetBufferMode(BufferModeType mode);
+ // Flushes buffer
+ // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position
+ void FlushBuffer();
+ // Loads data into ReadBuffer
+ // WARNING: Right now LoadBuffer() assumes the buffer's empty
+ void LoadBuffer();
+
+ // Hidden constructor
+ BufferedFile();
+ inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); }
+public:
+
+ // Constructor
+ // - takes another file as source
+ BufferedFile(File *pfile);
+ ~BufferedFile();
+
+
+ // ** Overridden functions
+
+ // We override all the functions that can possibly
+ // require buffer mode switch, flush, or extra calculations
+ virtual int Tell();
+ virtual SInt64 LTell();
+
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(GFileStats *pfs);
+
+ virtual int Write(const UByte *pbufer, int numBytes);
+ virtual int Read(UByte *pbufer, int numBytes);
+
+ virtual int SkipBytes(int numBytes);
+
+ virtual int BytesAvailable();
+
+ virtual bool Flush();
+
+ virtual int Seek(int offset, int origin=Seek_Set);
+ virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set);
+
+ virtual int CopyFromStream(File *pstream, int byteSize);
+
+ virtual bool Close();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Memory File
+
+class MemoryFile : public File
+{
+public:
+
+ const char* GetFilePath() { return FilePath.ToCStr(); }
+
+ bool IsValid() { return Valid; }
+ bool IsWritable() { return false; }
+
+ bool Flush() { return true; }
+ int GetErrorCode() { return 0; }
+
+ int Tell() { return FileIndex; }
+ SInt64 LTell() { return (SInt64) FileIndex; }
+
+ int GetLength() { return FileSize; }
+ SInt64 LGetLength() { return (SInt64) FileSize; }
+
+ bool Close()
+ {
+ Valid = false;
+ return false;
+ }
+
+ int CopyFromStream(File *pstream, int byteSize)
+ { OVR_UNUSED2(pstream, byteSize);
+ return 0;
+ }
+
+ int Write(const UByte *pbuffer, int numBytes)
+ { OVR_UNUSED2(pbuffer, numBytes);
+ return 0;
+ }
+
+ int Read(UByte *pbufer, int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ if (numBytes > 0)
+ {
+ ::memcpy (pbufer, &FileData [FileIndex], numBytes);
+
+ FileIndex += numBytes;
+ }
+
+ return numBytes;
+ }
+
+ int SkipBytes(int numBytes)
+ {
+ if (FileIndex + numBytes > FileSize)
+ {
+ numBytes = FileSize - FileIndex;
+ }
+
+ FileIndex += numBytes;
+
+ return numBytes;
+ }
+
+ int BytesAvailable()
+ {
+ return (FileSize - FileIndex);
+ }
+
+ int Seek(int offset, int origin = Seek_Set)
+ {
+ switch (origin)
+ {
+ case Seek_Set : FileIndex = offset; break;
+ case Seek_Cur : FileIndex += offset; break;
+ case Seek_End : FileIndex = FileSize - offset; break;
+ }
+
+ return FileIndex;
+ }
+
+ SInt64 LSeek(SInt64 offset, int origin = Seek_Set)
+ {
+ return (SInt64) Seek((int) offset, origin);
+ }
+
+public:
+
+ MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize)
+ : FilePath(fileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false;
+ }
+
+ // pfileName should be encoded as UTF-8 to support international file names.
+ MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize)
+ : FilePath(pfileName)
+ {
+ FileData = pBuffer;
+ FileSize = buffSize;
+ FileIndex = 0;
+ Valid = (pfileName && pBuffer && buffSize > 0) ? true : false;
+ }
+private:
+
+ String FilePath;
+ const UByte *FileData;
+ int FileSize;
+ int FileIndex;
+ bool Valid;
+};
+
+
+// ***** Global path helpers
+
+// Find trailing short filename in a path.
+const char* OVR_CDECL GetShortFilename(const char* purl);
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_FileFILE.cpp b/LibOVR/Src/Kernel/OVR_FileFILE.cpp
new file mode 100644
index 0000000..8478086
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_FileFILE.cpp
@@ -0,0 +1,595 @@
+/**************************************************************************
+
+Filename : OVR_FileFILE.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+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.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+#include "OVR_Types.h"
+#include "OVR_Log.h"
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+#ifndef OVR_OS_WINCE
+#include <sys/stat.h>
+#endif
+
+#include "OVR_SysFile.h"
+
+#ifndef OVR_OS_WINCE
+#include <errno.h>
+#endif
+
+namespace OVR {
+
+// ***** File interface
+
+// ***** FILEFile - C streams file
+
+static int SFerror ()
+{
+ if (errno == ENOENT)
+ return FileConstants::Error_FileNotFound;
+ else if (errno == EACCES || errno == EPERM)
+ return FileConstants::Error_Access;
+ else if (errno == ENOSPC)
+ return FileConstants::Error_DiskFull;
+ else
+ return FileConstants::Error_IOError;
+};
+
+#ifdef OVR_OS_WIN32
+#include "windows.h"
+// A simple helper class to disable/enable system error mode, if necessary
+// Disabling happens conditionally only if a drive name is involved
+class SysErrorModeDisabler
+{
+ BOOL Disabled;
+ UINT OldMode;
+public:
+ SysErrorModeDisabler(const char* pfileName)
+ {
+ if (pfileName && (pfileName[0]!=0) && pfileName[1]==':')
+ {
+ Disabled = 1;
+ OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
+ }
+ else
+ Disabled = 0;
+ }
+
+ ~SysErrorModeDisabler()
+ {
+ if (Disabled) ::SetErrorMode(OldMode);
+ }
+};
+#else
+class SysErrorModeDisabler
+{
+public:
+ SysErrorModeDisabler(const char* pfileName) { OVR_UNUSED(pfileName); }
+};
+#endif // OVR_OS_WIN32
+
+
+// This macro enables verification of I/O results after seeks against a pre-loaded
+// full file buffer copy. This is generally not necessary, but can been used to debug
+// memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory
+// under FMOD with XP64 (32-bit) and Realtek HA Audio driver.
+//#define GFILE_VERIFY_SEEK_ERRORS
+
+
+// This is the simplest possible file implementation, it wraps around the descriptor
+// This file is delegated to by SysFile.
+
+class FILEFile : public File
+{
+protected:
+
+ // Allocated filename
+ String FileName;
+
+ // File handle & open mode
+ bool Opened;
+ FILE* fs;
+ int OpenFlags;
+ // Error code for last request
+ int ErrorCode;
+
+ int LastOp;
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ UByte* pFileTestBuffer;
+ unsigned FileTestLength;
+ unsigned TestPos; // File pointer position during tests.
+#endif
+
+public:
+
+ FILEFile()
+ {
+ Opened = 0; FileName = "";
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ pFileTestBuffer =0;
+ FileTestLength =0;
+ TestPos =0;
+#endif
+ }
+ // Initialize file by opening it
+ FILEFile(const String& fileName, int flags, int Mode);
+ // The 'pfileName' should be encoded as UTF-8 to support international file names.
+ FILEFile(const char* pfileName, int flags, int Mode);
+
+ ~FILEFile()
+ {
+ if (Opened)
+ Close();
+ }
+
+ virtual const char* GetFilePath();
+
+ // ** File Information
+ virtual bool IsValid();
+ virtual bool IsWritable();
+
+ // Return position / file size
+ virtual int Tell();
+ virtual SInt64 LTell();
+ virtual int GetLength();
+ virtual SInt64 LGetLength();
+
+// virtual bool Stat(FileStats *pfs);
+ virtual int GetErrorCode();
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes);
+ virtual int Read(UByte *pbuffer, int numBytes);
+ virtual int SkipBytes(int numBytes);
+ virtual int BytesAvailable();
+ virtual bool Flush();
+ virtual int Seek(int offset, int origin);
+ virtual SInt64 LSeek(SInt64 offset, int origin);
+
+ virtual int CopyFromStream(File *pStream, int byteSize);
+ virtual bool Close();
+private:
+ void init();
+};
+
+
+// Initialize file by opening it
+FILEFile::FILEFile(const String& fileName, int flags, int mode)
+ : FileName(fileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+// The 'pfileName' should be encoded as UTF-8 to support international file names.
+FILEFile::FILEFile(const char* pfileName, int flags, int mode)
+ : FileName(pfileName), OpenFlags(flags)
+{
+ OVR_UNUSED(mode);
+ init();
+}
+
+void FILEFile::init()
+{
+ // Open mode for file's open
+ const char *omode = "rb";
+
+ if (OpenFlags & Open_Truncate)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "w+b";
+ else
+ omode = "wb";
+ }
+ else if (OpenFlags & Open_Create)
+ {
+ if (OpenFlags & Open_Read)
+ omode = "a+b";
+ else
+ omode = "ab";
+ }
+ else if (OpenFlags & Open_Write)
+ omode = "r+b";
+
+#ifdef OVR_OS_WIN32
+ SysErrorModeDisabler disabler(FileName.ToCStr());
+#endif
+
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ wchar_t womode[16];
+ wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t));
+ UTF8Util::DecodeString(pwFileName, FileName.ToCStr());
+ OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0]));
+ UTF8Util::DecodeString(womode, omode);
+ _wfopen_s(&fs, pwFileName, womode);
+ OVR_FREE(pwFileName);
+#else
+ fs = fopen(FileName.ToCStr(), omode);
+#endif
+ if (fs)
+ rewind (fs);
+ Opened = (fs != NULL);
+ // Set error code
+ if (!Opened)
+ ErrorCode = SFerror();
+ else
+ {
+ // If we are testing file seek correctness, pre-load the entire file so
+ // that we can do comparison tests later.
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ TestPos = 0;
+ fseek(fs, 0, SEEK_END);
+ FileTestLength = ftell(fs);
+ fseek(fs, 0, SEEK_SET);
+ pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength);
+ if (pFileTestBuffer)
+ {
+ OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength));
+ Seek(0, Seek_Set);
+ }
+#endif
+
+ ErrorCode = 0;
+ }
+ LastOp = 0;
+}
+
+
+const char* FILEFile::GetFilePath()
+{
+ return FileName.ToCStr();
+}
+
+
+// ** File Information
+bool FILEFile::IsValid()
+{
+ return Opened;
+}
+bool FILEFile::IsWritable()
+{
+ return IsValid() && (OpenFlags&Open_Write);
+}
+/*
+bool FILEFile::IsRecoverable()
+{
+ return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC);
+}
+*/
+
+// Return position / file size
+int FILEFile::Tell()
+{
+ int pos = (int)ftell (fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+SInt64 FILEFile::LTell()
+{
+ SInt64 pos = ftell(fs);
+ if (pos < 0)
+ ErrorCode = SFerror();
+ return pos;
+}
+
+int FILEFile::GetLength()
+{
+ int pos = Tell();
+ if (pos >= 0)
+ {
+ Seek (0, Seek_End);
+ int size = Tell();
+ Seek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+SInt64 FILEFile::LGetLength()
+{
+ SInt64 pos = LTell();
+ if (pos >= 0)
+ {
+ LSeek (0, Seek_End);
+ SInt64 size = LTell();
+ LSeek (pos, Seek_Set);
+ return size;
+ }
+ return -1;
+}
+
+int FILEFile::GetErrorCode()
+{
+ return ErrorCode;
+}
+
+// ** Stream implementation & I/O
+int FILEFile::Write(const UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Write)
+ fflush(fs);
+ LastOp = Open_Write;
+ int written = (int) fwrite(pbuffer, 1, numBytes, fs);
+ if (written < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (written > 0)
+ TestPos += written;
+#endif
+
+ return written;
+}
+
+int FILEFile::Read(UByte *pbuffer, int numBytes)
+{
+ if (LastOp && LastOp != Open_Read)
+ fflush(fs);
+ LastOp = Open_Read;
+ int read = (int) fread(pbuffer, 1, numBytes, fs);
+ if (read < numBytes)
+ ErrorCode = SFerror();
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (read > 0)
+ {
+ // Read-in data must match our pre-loaded buffer data!
+ UByte* pcompareBuffer = pFileTestBuffer + TestPos;
+ for (int i=0; i< read; i++)
+ {
+ OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]);
+ }
+
+ //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read));
+ TestPos += read;
+ OVR_ASSERT(ftell(fs) == (int)TestPos);
+ }
+#endif
+
+ return read;
+}
+
+// Seeks ahead to skip bytes
+int FILEFile::SkipBytes(int numBytes)
+{
+ SInt64 pos = LTell();
+ SInt64 newPos = LSeek(numBytes, Seek_Cur);
+
+ // Return -1 for major error
+ if ((pos==-1) || (newPos==-1))
+ {
+ return -1;
+ }
+ //ErrorCode = ((NewPos-Pos)<numBytes) ? errno : 0;
+
+ return int (newPos-(int)pos);
+}
+
+// Return # of bytes till EOF
+int FILEFile::BytesAvailable()
+{
+ SInt64 pos = LTell();
+ SInt64 endPos = LGetLength();
+
+ // Return -1 for major error
+ if ((pos==-1) || (endPos==-1))
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ ErrorCode = 0;
+
+ return int (endPos-(int)pos);
+}
+
+// Flush file contents
+bool FILEFile::Flush()
+{
+ return !fflush(fs);
+}
+
+int FILEFile::Seek(int offset, int origin)
+{
+ int newOrigin = 0;
+ switch(origin)
+ {
+ case Seek_Set: newOrigin = SEEK_SET; break;
+ case Seek_Cur: newOrigin = SEEK_CUR; break;
+ case Seek_End: newOrigin = SEEK_END; break;
+ }
+
+ if (newOrigin == SEEK_SET && offset == Tell())
+ return Tell();
+
+ if (fseek (fs, offset, newOrigin))
+ {
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ OVR_ASSERT(0);
+#endif
+ return -1;
+ }
+
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ // Track file position after seeks for read verification later.
+ switch(origin)
+ {
+ case Seek_Set: TestPos = offset; break;
+ case Seek_Cur: TestPos += offset; break;
+ case Seek_End: TestPos = FileTestLength + offset; break;
+ }
+ OVR_ASSERT((int)TestPos == Tell());
+#endif
+
+ return (int)Tell();
+}
+
+SInt64 FILEFile::LSeek(SInt64 offset, int origin)
+{
+ return Seek((int)offset,origin);
+}
+
+int FILEFile::CopyFromStream(File *pstream, int byteSize)
+{
+ UByte buff[0x4000];
+ int count = 0;
+ int szRequest, szRead, szWritten;
+
+ while (byteSize)
+ {
+ szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize;
+
+ szRead = pstream->Read(buff, szRequest);
+ szWritten = 0;
+ if (szRead > 0)
+ szWritten = Write(buff, szRead);
+
+ count += szWritten;
+ byteSize -= szWritten;
+ if (szWritten < szRequest)
+ break;
+ }
+ return count;
+}
+
+
+bool FILEFile::Close()
+{
+#ifdef OVR_FILE_VERIFY_SEEK_ERRORS
+ if (pFileTestBuffer)
+ {
+ OVR_FREE(pFileTestBuffer);
+ pFileTestBuffer = 0;
+ FileTestLength = 0;
+ }
+#endif
+
+ bool closeRet = !fclose(fs);
+
+ if (!closeRet)
+ {
+ ErrorCode = SFerror();
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fs = 0;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate
+ /*
+ if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ {
+ // Delete original file (if it existed)
+ DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName);
+ if (oldAttributes!=0xFFFFFFFF)
+ if (!FileUtilWin32::DeleteFile(FileName))
+ {
+ // Try to remove the readonly attribute
+ FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) );
+ // And delete the file again
+ if (!FileUtilWin32::DeleteFile(FileName))
+ return 0;
+ }
+
+ // Rename temp file to real filename
+ if (!FileUtilWin32::MoveFile(TempName, FileName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ }
+ */
+ return 1;
+}
+
+/*
+bool FILEFile::CloseCancel()
+{
+ bool closeRet = (bool)::CloseHandle(fd);
+
+ if (!closeRet)
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ else
+ {
+ Opened = 0;
+ fd = INVALID_HANDLE_VALUE;
+ ErrorCode = 0;
+ }
+
+ // Handle safe truncate (delete tmp file, leave original unchanged)
+ if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC)
+ if (!FileUtilWin32::DeleteFile(TempName))
+ {
+ //ErrorCode = errno;
+ return 0;
+ }
+ return 1;
+}
+*/
+
+Ptr<File> FileFILEOpen(const String& path, int flags, int mode)
+{
+ Ptr<File> result = *new FILEFile(path, flags, mode);
+ return result;
+}
+
+// Helper function: obtain file information time.
+bool SysFile::GetFileStat(FileStat* pfileStat, const String& path)
+{
+#if defined(OVR_OS_WIN32)
+ // 64-bit implementation on Windows.
+ struct __stat64 fileStat;
+ // Stat returns 0 for success.
+ wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t));
+ UTF8Util::DecodeString(pwpath, path.ToCStr());
+
+ int ret = _wstat64(pwpath, &fileStat);
+ OVR_FREE(pwpath);
+ if (ret) return false;
+#else
+ struct stat fileStat;
+ // Stat returns 0 for success.
+ if (stat(path, &fileStat) != 0)
+ return false;
+#endif
+ pfileStat->AccessTime = fileStat.st_atime;
+ pfileStat->ModifyTime = fileStat.st_mtime;
+ pfileStat->FileSize = fileStat.st_size;
+ return true;
+}
+
+} // Namespace OVR
diff --git a/LibOVR/Src/Kernel/OVR_Hash.h b/LibOVR/Src/Kernel/OVR_Hash.h
new file mode 100644
index 0000000..04c4db8
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Hash.h
@@ -0,0 +1,1302 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Hash.h
+Content : Template hash-table/set implementation
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Hash_h
+#define OVR_Hash_h
+
+#include "OVR_ContainerAllocator.h"
+#include "OVR_Alg.h"
+
+// 'new' operator is redefined/used in this file.
+#undef new
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Hash Table Implementation
+
+// HastSet and Hash.
+//
+// Hash table, linear probing, internal chaining. One interesting/nice thing
+// about this implementation is that the table itself is a flat chunk of memory
+// containing no pointers, only relative indices. If the key and value types
+// of the Hash contain no pointers, then the Hash can be serialized using raw IO.
+//
+// Never shrinks, unless you explicitly Clear() it. Expands on
+// demand, though. For best results, if you know roughly how big your
+// table will be, default it to that size when you create it.
+//
+// Key usability feature:
+//
+// 1. Allows node hash values to either be cached or not.
+//
+// 2. Allows for alternative keys with methods such as GetAlt(). Handy
+// if you need to search nodes by their components; no need to create
+// temporary nodes.
+//
+
+
+// *** Hash functors:
+//
+// IdentityHash - use when the key is already a good hash
+// HFixedSizeHash - general hash based on object's in-memory representation.
+
+
+// Hash is just the input value; can use this for integer-indexed hash tables.
+template<class C>
+class IdentityHash
+{
+public:
+ UPInt operator()(const C& data) const
+ { return (UPInt) data; }
+};
+
+// Computes a hash of an object's representation.
+template<class C>
+class FixedSizeHash
+{
+public:
+ // Alternative: "sdbm" hash function, suggested at same web page
+ // above, http::/www.cs.yorku.ca/~oz/hash.html
+ // This is somewhat slower then Bernstein, but it works way better than the above
+ // hash function for hashing large numbers of 32-bit ints.
+ static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381)
+ {
+ const UByte* data = (const UByte*) data_in;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = (h << 16) + (h << 6) - h + (UPInt)data[size];
+ }
+ return h;
+ }
+
+ UPInt operator()(const C& data) const
+ {
+ unsigned char* p = (unsigned char*) &data;
+ int size = sizeof(C);
+
+ return SDBM_Hash(p, size);
+ }
+};
+
+
+
+// *** HashsetEntry Entry types.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetEntry()
+ : NextInChain(-2) { }
+ HashsetEntry(const HashsetEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt) {}
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedEntry()
+ : NextInChain(-2) { }
+ HashsetCachedEntry(const HashsetCachedEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+
+ // Cached hash value access - can be optimized bu storing hash locally.
+ // Mask value only needs to be used if SetCachedHash is not implemented.
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// *** HashSet implementation - relies on either cached or regular entries.
+//
+// Use: Entry = HashsetCachedEntry<C, HashF> if hashes are expensive to
+// compute and thus need caching in entries.
+// Entry = HashsetEntry<C, HashF> if hashes are already externally cached.
+//
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSetBase
+{
+ enum { HashMinSize = 8 };
+
+public:
+ OVR_MEMORY_REDEFINE_NEW(HashSetBase)
+
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSetBase() : pTable(NULL) { }
+ HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); }
+ HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); }
+
+ ~HashSetBase()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Free();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+
+ void Assign(const SelfType& src)
+ {
+ Clear();
+ if (src.IsEmpty() == false)
+ {
+ SetCapacity(src.GetSize());
+
+ for (ConstIterator it = src.Begin(); it != src.End(); ++it)
+ {
+ Add(*it);
+ }
+ }
+ }
+
+
+ // Remove all entries from the HashSet table.
+ void Clear()
+ {
+ if (pTable)
+ {
+ // Delete the entries.
+ for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (!e->IsEmpty())
+ e->Clear();
+ }
+
+ Allocator::Free(pTable);
+ pTable = NULL;
+ }
+ }
+
+ // Returns true if the HashSet is empty.
+ bool IsEmpty() const
+ {
+ return pTable == NULL || pTable->EntryCount == 0;
+ }
+
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ SPInt index = (SPInt)-1;
+
+ if (pTable != NULL)
+ index = findIndexCore(key, hashValue & pTable->SizeMask);
+
+ if (index >= 0)
+ {
+ E(index).Value = key;
+ }
+ else
+ {
+ // Entry under key doesn't exist.
+ add(key, hashValue);
+ }
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ UPInt hashValue = HashF()(key);
+ add(key, hashValue);
+ }
+
+ // Remove by alternative key.
+ template<class K>
+ void RemoveAlt(const K& key)
+ {
+ if (pTable == NULL)
+ return;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & pTable->SizeMask;
+
+ Entry* e = &E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &E(index);
+ }
+
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ pTable->EntryCount --;
+ // Should we check the size to condense hash? ...
+ }
+
+ // Remove by main key.
+ template<class CRef>
+ void Remove(const CRef& key)
+ {
+ RemoveAlt(key);
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ template<class K>
+ C* Get(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ const C* Get(const K& key) const
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ // Alternative key versions of Get. Used by Hash.
+ template<class K>
+ const C* GetAlt(const K& key) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ C* GetAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return &E(index).Value;
+ return 0;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, C* pval) const
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ {
+ if (pval)
+ *pval = E(index).Value;
+ return true;
+ }
+ return false;
+ }
+
+
+ UPInt GetSize() const
+ {
+ return pTable == NULL ? 0 : (UPInt)pTable->EntryCount;
+ }
+
+
+ // Resize the HashSet table to fit one more Entry. Often this
+ // doesn't involve any action.
+ void CheckExpand()
+ {
+ if (pTable == NULL)
+ {
+ // Initial creation of table. Make a minimum-sized table.
+ setRawCapacity(HashMinSize);
+ }
+ else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4)
+ {
+ // pTable is more than 5/4 ths full. Expand.
+ setRawCapacity((pTable->SizeMask + 1) * 2);
+ }
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ // Not really sure what this means in relation to
+ // STLport's hash_map... they say they "increase the
+ // bucket count to at least n" -- but does that mean
+ // their real capacity after Resize(n) is more like
+ // n*2 (since they do linked-list chaining within
+ // buckets?).
+ SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ UPInt newRawSize = (newSize * 5) / 4;
+ if (newRawSize <= GetSize())
+ return;
+ setRawCapacity(newRawSize);
+ }
+
+ // Disable inappropriate 'operator ->' warning on MSVC6.
+#ifdef OVR_CC_MSVC
+#if (OVR_CC_MSVC < 1300)
+# pragma warning(disable : 4284)
+#endif
+#endif
+
+ // Iterator API, like STL.
+ struct ConstIterator
+ {
+ const C& operator * () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return pHash->E(Index).Value;
+ }
+
+ const C* operator -> () const
+ {
+ OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask);
+ return &pHash->E(Index).Value;
+ }
+
+ void operator ++ ()
+ {
+ // Find next non-empty Entry.
+ if (Index <= (SPInt)pHash->pTable->SizeMask)
+ {
+ Index++;
+ while ((UPInt)Index <= pHash->pTable->SizeMask &&
+ pHash->E(Index).IsEmpty())
+ {
+ Index++;
+ }
+ }
+ }
+
+ bool operator == (const ConstIterator& it) const
+ {
+ if (IsEnd() && it.IsEnd())
+ {
+ return true;
+ }
+ else
+ {
+ return (pHash == it.pHash) && (Index == it.Index);
+ }
+ }
+
+ bool operator != (const ConstIterator& it) const
+ {
+ return ! (*this == it);
+ }
+
+
+ bool IsEnd() const
+ {
+ return (pHash == NULL) ||
+ (pHash->pTable == NULL) ||
+ (Index > (SPInt)pHash->pTable->SizeMask);
+ }
+
+ ConstIterator()
+ : pHash(NULL), Index(0)
+ { }
+
+ public:
+ // Constructor was intentionally made public to allow create
+ // iterator with arbitrary index.
+ ConstIterator(const SelfType* h, SPInt index)
+ : pHash(h), Index(index)
+ { }
+
+ const SelfType* GetContainer() const
+ {
+ return pHash;
+ }
+ SPInt GetIndex() const
+ {
+ return Index;
+ }
+
+ protected:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ const SelfType* pHash;
+ SPInt Index;
+ };
+
+ friend struct ConstIterator;
+
+
+ // Non-const Iterator; Get most of it from ConstIterator.
+ struct Iterator : public ConstIterator
+ {
+ // Allow non-const access to entries.
+ C& operator*() const
+ {
+ OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask);
+ return const_cast<SelfType*>(ConstIterator::pHash)->E(ConstIterator::Index).Value;
+ }
+
+ C* operator->() const
+ {
+ return &(operator*());
+ }
+
+ Iterator()
+ : ConstIterator(NULL, 0)
+ { }
+
+ // Removes current element from Hash
+ void Remove()
+ {
+ RemoveAlt(operator*());
+ }
+
+ template <class K>
+ void RemoveAlt(const K& key)
+ {
+ SelfType* phash = const_cast<SelfType*>(ConstIterator::pHash);
+ //Entry* ee = &phash->E(ConstIterator::Index);
+ //const C& key = ee->Value;
+
+ UPInt hashValue = AltHashF()(key);
+ SPInt index = hashValue & phash->pTable->SizeMask;
+
+ Entry* e = &phash->E(index);
+
+ // If empty node or occupied by collider, we have nothing to remove.
+ if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index))
+ return;
+
+ // Save index
+ SPInt naturalIndex = index;
+ SPInt prevIndex = -1;
+
+ while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key))
+ {
+ // Keep looking through the chain.
+ prevIndex = index;
+ index = e->NextInChain;
+ if (index == -1)
+ return; // End of chain, item not found
+ e = &phash->E(index);
+ }
+
+ if (index == (SPInt)ConstIterator::Index)
+ {
+ // Found it - our item is at index
+ if (naturalIndex == index)
+ {
+ // If we have a follower, move it to us
+ if (!e->IsEndOfChain())
+ {
+ Entry* enext = &phash->E(e->NextInChain);
+ e->Clear();
+ new (e) Entry(*enext);
+ // Point us to the follower's cell that will be cleared
+ e = enext;
+ --ConstIterator::Index;
+ }
+ }
+ else
+ {
+ // We are not at natural index, so deal with the prev items next index
+ phash->E(prevIndex).NextInChain = e->NextInChain;
+ }
+
+ // Clear us, of the follower cell that was moved.
+ e->Clear();
+ phash->pTable->EntryCount --;
+ }
+ else
+ OVR_ASSERT(0); //?
+ }
+
+ private:
+ friend class HashSetBase<C, HashF, AltHashF, Allocator, Entry>;
+
+ Iterator(SelfType* h, SPInt i0)
+ : ConstIterator(h, i0)
+ { }
+ };
+
+ friend struct Iterator;
+
+ Iterator Begin()
+ {
+ if (pTable == 0)
+ return Iterator(NULL, 0);
+
+ // Scan till we hit the First valid Entry.
+ UPInt i0 = 0;
+ while (i0 <= pTable->SizeMask && E(i0).IsEmpty())
+ {
+ i0++;
+ }
+ return Iterator(this, i0);
+ }
+ Iterator End() { return Iterator(NULL, 0); }
+
+ ConstIterator Begin() const { return const_cast<SelfType*>(this)->Begin(); }
+ ConstIterator End() const { return const_cast<SelfType*>(this)->End(); }
+
+ template<class K>
+ Iterator Find(const K& key)
+ {
+ SPInt index = findIndex(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ Iterator FindAlt(const K& key)
+ {
+ SPInt index = findIndexAlt(key);
+ if (index >= 0)
+ return Iterator(this, index);
+ return Iterator(NULL, 0);
+ }
+
+ template<class K>
+ ConstIterator Find(const K& key) const { return const_cast<SelfType*>(this)->Find(key); }
+
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return const_cast<SelfType*>(this)->FindAlt(key); }
+
+private:
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndex(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = HashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ template<class K>
+ SPInt findIndexAlt(const K& key) const
+ {
+ if (pTable == NULL)
+ return -1;
+ UPInt hashValue = AltHashF()(key) & pTable->SizeMask;
+ return findIndexCore(key, hashValue);
+ }
+
+ // Find the index of the matching Entry. If no match, then return -1.
+ template<class K>
+ SPInt findIndexCore(const K& key, UPInt hashValue) const
+ {
+ // Table must exist.
+ OVR_ASSERT(pTable != 0);
+ // Hash key must be 'and-ed' by the caller.
+ OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0);
+
+ UPInt index = hashValue;
+ const Entry* e = &E(index);
+
+ // If empty or occupied by a collider, not found.
+ if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index))
+ return -1;
+
+ while(1)
+ {
+ OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue);
+
+ if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key)
+ {
+ // Found it.
+ return index;
+ }
+ // Values can not be equal at this point.
+ // That would mean that the hash key for the same value differs.
+ OVR_ASSERT(!(e->Value == key));
+
+ // Keep looking through the chain.
+ index = e->NextInChain;
+ if (index == (UPInt)-1)
+ break; // end of chain
+
+ e = &E(index);
+ OVR_ASSERT(!e->IsEmpty());
+ }
+ return -1;
+ }
+
+
+ // Add a new value to the HashSet table, under the specified key.
+ template<class CRef>
+ void add(const CRef& key, UPInt hashValue)
+ {
+ CheckExpand();
+ hashValue &= pTable->SizeMask;
+
+ pTable->EntryCount++;
+
+ SPInt index = hashValue;
+ Entry* naturalEntry = &(E(index));
+
+ if (naturalEntry->IsEmpty())
+ {
+ // Put the new Entry in.
+ new (naturalEntry) Entry(key, -1);
+ }
+ else
+ {
+ // Find a blank spot.
+ SPInt blankIndex = index;
+ do {
+ blankIndex = (blankIndex + 1) & pTable->SizeMask;
+ } while(!E(blankIndex).IsEmpty());
+
+ Entry* blankEntry = &E(blankIndex);
+
+ if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index)
+ {
+ // Collision. Link into this chain.
+
+ // Move existing list head.
+ new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor
+
+ // Put the new info in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = blankIndex;
+ }
+ else
+ {
+ // Existing Entry does not naturally
+ // belong in this slot. Existing
+ // Entry must be moved.
+
+ // Find natural location of collided element (i.e. root of chain)
+ SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask);
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ for (;;)
+ {
+ Entry* e = &E(collidedIndex);
+ if (e->NextInChain == index)
+ {
+ // Here's where we need to splice.
+ new (blankEntry) Entry(*naturalEntry);
+ e->NextInChain = blankIndex;
+ break;
+ }
+ collidedIndex = e->NextInChain;
+ OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask);
+ }
+
+ // Put the new data in the natural Entry.
+ naturalEntry->Value = key;
+ naturalEntry->NextInChain = -1;
+ }
+ }
+
+ // Record hash value: has effect only if cached node is used.
+ naturalEntry->SetCachedHash(hashValue);
+ }
+
+ // Index access helpers.
+ Entry& E(UPInt index)
+ {
+ // Must have pTable and access needs to be within bounds.
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+ const Entry& E(UPInt index) const
+ {
+ OVR_ASSERT(index <= pTable->SizeMask);
+ return *(((Entry*) (pTable + 1)) + index);
+ }
+
+
+ // Resize the HashSet table to the given size (Rehash the
+ // contents of the current table). The arg is the number of
+ // HashSet table entries, not the number of elements we should
+ // actually contain (which will be less than this).
+ void setRawCapacity(UPInt newSize)
+ {
+ if (newSize == 0)
+ {
+ // Special case.
+ Clear();
+ return;
+ }
+
+ // Minimum size; don't incur rehashing cost when expanding
+ // very small tables. Not that we perform this check before
+ // 'log2f' call to avoid fp exception with newSize == 1.
+ if (newSize < HashMinSize)
+ newSize = HashMinSize;
+ else
+ {
+ // Force newSize to be a power of two.
+ int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1);
+ OVR_ASSERT((UPInt(1) << bits) >= newSize);
+ newSize = UPInt(1) << bits;
+ }
+
+ SelfType newHash;
+ newHash.pTable = (TableType*)
+ Allocator::Alloc(
+ sizeof(TableType) + sizeof(Entry) * newSize);
+ // Need to do something on alloc failure!
+ OVR_ASSERT(newHash.pTable);
+
+ newHash.pTable->EntryCount = 0;
+ newHash.pTable->SizeMask = newSize - 1;
+ UPInt i, n;
+
+ // Mark all entries as empty.
+ for (i = 0; i < newSize; i++)
+ newHash.E(i).NextInChain = -2;
+
+ // Copy stuff to newHash
+ if (pTable)
+ {
+ for (i = 0, n = pTable->SizeMask; i <= n; i++)
+ {
+ Entry* e = &E(i);
+ if (e->IsEmpty() == false)
+ {
+ // Insert old Entry into new HashSet.
+ newHash.Add(e->Value);
+ // placement delete of old element
+ e->Clear();
+ }
+ }
+
+ // Delete our old data buffer.
+ Allocator::Free(pTable);
+ }
+
+ // Steal newHash's data.
+ pTable = newHash.pTable;
+ newHash.pTable = NULL;
+ }
+
+ struct TableType
+ {
+ UPInt EntryCount;
+ UPInt SizeMask;
+ // Entry array follows this structure
+ // in memory.
+ };
+ TableType* pTable;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C>,
+ class Entry = HashsetCachedEntry<C, HashF> >
+class HashSet : public HashSetBase<C, HashF, AltHashF, Allocator, Entry>
+{
+public:
+ typedef HashSetBase<C, HashF, AltHashF, Allocator, Entry> BaseType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, Entry> SelfType;
+
+ HashSet() { }
+ HashSet(int sizeHint) : BaseType(sizeHint) { }
+ HashSet(const SelfType& src) : BaseType(src) { }
+ ~HashSet() { }
+
+ void operator = (const SelfType& src) { BaseType::Assign(src); }
+
+ // Set a new or existing value under the key, to the value.
+ // Pass a different class of 'key' so that assignment reference object
+ // can be passed instead of the actual object.
+ template<class CRef>
+ void Set(const CRef& key)
+ {
+ BaseType::Set(key);
+ }
+
+ template<class CRef>
+ inline void Add(const CRef& key)
+ {
+ BaseType::Add(key);
+ }
+
+ // Hint the bucket count to >= n.
+ void Resize(UPInt n)
+ {
+ BaseType::SetCapacity(n);
+ }
+
+ // Size the HashSet so that it can comfortably contain the given
+ // number of elements. If the HashSet already contains more
+ // elements than newSize, then this may be a no-op.
+ void SetCapacity(UPInt newSize)
+ {
+ BaseType::SetCapacity(newSize);
+ }
+
+};
+
+// HashSet with uncached hash code; declared for convenience.
+template<class C, class HashF = FixedSizeHash<C>,
+ class AltHashF = HashF,
+ class Allocator = ContainerAllocator<C> >
+class HashSetUncached : public HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> >
+{
+public:
+
+ typedef HashSetUncached<C, HashF, AltHashF, Allocator> SelfType;
+ typedef HashSet<C, HashF, AltHashF, Allocator, HashsetEntry<C, HashF> > BaseType;
+
+ // Delegated constructors.
+ HashSetUncached() { }
+ HashSetUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashSetUncached(const SelfType& src) : BaseType(src) { }
+ ~HashSetUncached() { }
+
+ void operator = (const SelfType& src)
+ {
+ BaseType::operator = (src);
+ }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Hash hash table implementation
+
+// Node for Hash - necessary so that Hash can delegate its implementation
+// to HashSet.
+template<class C, class U, class HashF>
+struct HashNode
+{
+ typedef HashNode<C, U, HashF> SelfType;
+ typedef C FirstType;
+ typedef U SecondType;
+
+ C First;
+ U Second;
+
+ // NodeRef is used to allow passing of elements into HashSet
+ // without using a temporary object.
+ struct NodeRef
+ {
+ const C* pFirst;
+ const U* pSecond;
+
+ NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { }
+ NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { }
+
+ // Enable computation of ghash_node_hashf.
+ inline UPInt GetHash() const { return HashF()(*pFirst); }
+ // Necessary conversion to allow HashNode::operator == to work.
+ operator const C& () const { return *pFirst; }
+ };
+
+ // Note: No default constructor is necessary.
+ HashNode(const HashNode& src) : First(src.First), Second(src.Second) { }
+ HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { }
+ void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; }
+
+ template<class K>
+ bool operator == (const K& src) const { return (First == src); }
+
+ template<class K>
+ static UPInt CalcHash(const K& data) { return HashF()(data); }
+ inline UPInt GetHash() const { return HashF()(First); }
+
+ // Hash functors used with this node. A separate functor is used for alternative
+ // key lookup so that it does not need to access the '.First' element.
+ struct NodeHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return data.GetHash(); }
+ };
+ struct NodeAltHashF
+ {
+ template<class K>
+ UPInt operator()(const K& data) const { return HashNode<C,U,HashF>::CalcHash(data); }
+ };
+};
+
+
+
+// **** Extra hashset_entry types to allow NodeRef construction.
+
+// The big difference between the below types and the ones used in hash_set is that
+// these allow initializing the node with 'typename C::NodeRef& keyRef', which
+// is critical to avoid temporary node allocation on stack when using placement new.
+
+// Compact hash table Entry type that re-computes hash keys during hash traversal.
+// Good to use if the hash function is cheap or the hash value is already cached in C.
+template<class C, class HashF>
+class HashsetNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ C Value;
+
+ HashsetNodeEntry()
+ : NextInChain(-2) { }
+ HashsetNodeEntry(const HashsetNodeEntry& e)
+ : NextInChain(e.NextInChain), Value(e.Value) { }
+ HashsetNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; }
+ void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); }
+
+ void Clear()
+ {
+ Value.~C(); // placement delete
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+// Hash table Entry type that caches the Entry hash value for nodes, so that it
+// does not need to be re-computed during access.
+template<class C, class HashF>
+class HashsetCachedNodeEntry
+{
+public:
+ // Internal chaining for collisions.
+ SPInt NextInChain;
+ UPInt HashValue;
+ C Value;
+
+ HashsetCachedNodeEntry()
+ : NextInChain(-2) { }
+ HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e)
+ : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { }
+ HashsetCachedNodeEntry(const C& key, SPInt next)
+ : NextInChain(next), Value(key) { }
+ HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next)
+ : NextInChain(next), Value(keyRef) { }
+
+ bool IsEmpty() const { return NextInChain == -2; }
+ bool IsEndOfChain() const { return NextInChain == -1; }
+ UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; }
+ void SetCachedHash(UPInt hashValue) { HashValue = hashValue; }
+
+ void Clear()
+ {
+ Value.~C();
+ NextInChain = -2;
+ }
+ // Free is only used from dtor of hash; Clear is used during regular operations:
+ // assignment, hash reallocations, value reassignments, so on.
+ void Free() { Clear(); }
+};
+
+
+//-----------------------------------------------------------------------------------
+template<class C, class U,
+ class HashF = FixedSizeHash<C>,
+ class Allocator = ContainerAllocator<C>,
+ class HashNode = OVR::HashNode<C,U,HashF>,
+ class Entry = HashsetCachedNodeEntry<HashNode, typename HashNode::NodeHashF>,
+ class Container = HashSet<HashNode, typename HashNode::NodeHashF,
+ typename HashNode::NodeAltHashF, Allocator,
+ Entry> >
+class Hash
+{
+public:
+ OVR_MEMORY_REDEFINE_NEW(Hash)
+
+ // Types used for hash_set.
+ typedef U ValueType;
+ typedef Hash<C, U, HashF, Allocator, HashNode, Entry, Container> SelfType;
+
+ // Actual hash table itself, implemented as hash_set.
+ Container mHash;
+
+public:
+ Hash() { }
+ Hash(int sizeHint) : mHash(sizeHint) { }
+ Hash(const SelfType& src) : mHash(src.mHash) { }
+ ~Hash() { }
+
+ void operator = (const SelfType& src) { mHash = src.mHash; }
+
+ // Remove all entries from the Hash table.
+ inline void Clear() { mHash.Clear(); }
+ // Returns true if the Hash is empty.
+ inline bool IsEmpty() const { return mHash.IsEmpty(); }
+
+ // Access (set).
+ inline void Set(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Set(e);
+ }
+ inline void Add(const C& key, const U& value)
+ {
+ typename HashNode::NodeRef e(key, value);
+ mHash.Add(e);
+ }
+
+ // Removes an element by clearing its Entry.
+ inline void Remove(const C& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+ template<class K>
+ inline void RemoveAlt(const K& key)
+ {
+ mHash.RemoveAlt(key);
+ }
+
+ // Retrieve the value under the given key.
+ // - If there's no value under the key, then return false and leave *pvalue alone.
+ // - If there is a value, return true, and Set *Pvalue to the Entry's value.
+ // - If value == NULL, return true or false according to the presence of the key.
+ bool Get(const C& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ template<class K>
+ bool GetAlt(const K& key, U* pvalue) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ if (p)
+ {
+ if (pvalue)
+ *pvalue = p->Second;
+ return true;
+ }
+ return false;
+ }
+
+ // Retrieve the pointer to a value under the given key.
+ // - If there's no value under the key, then return NULL.
+ // - If there is a value, return the pointer.
+ inline U* Get(const C& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ inline const U* Get(const C& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ template<class K>
+ inline U* GetAlt(const K& key)
+ {
+ HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+ template<class K>
+ inline const U* GetAlt(const K& key) const
+ {
+ const HashNode* p = mHash.GetAlt(key);
+ return p ? &p->Second : 0;
+ }
+
+ // Sizing methods - delegate to Hash.
+ inline UPInt GetSize() const { return mHash.GetSize(); }
+ inline void Resize(UPInt n) { mHash.Resize(n); }
+ inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); }
+
+ // Iterator API, like STL.
+ typedef typename Container::ConstIterator ConstIterator;
+ typedef typename Container::Iterator Iterator;
+
+ inline Iterator Begin() { return mHash.Begin(); }
+ inline Iterator End() { return mHash.End(); }
+ inline ConstIterator Begin() const { return mHash.Begin(); }
+ inline ConstIterator End() const { return mHash.End(); }
+
+ Iterator Find(const C& key) { return mHash.FindAlt(key); }
+ ConstIterator Find(const C& key) const { return mHash.FindAlt(key); }
+
+ template<class K>
+ Iterator FindAlt(const K& key) { return mHash.FindAlt(key); }
+ template<class K>
+ ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); }
+};
+
+
+
+// Hash with uncached hash code; declared for convenience.
+template<class C, class U, class HashF = FixedSizeHash<C>, class Allocator = ContainerAllocator<C> >
+class HashUncached
+ : public Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>, typename HashNode<C,U,HashF>::NodeHashF> >
+{
+public:
+ typedef HashUncached<C, U, HashF, Allocator> SelfType;
+ typedef Hash<C, U, HashF, Allocator, HashNode<C,U,HashF>,
+ HashsetNodeEntry<HashNode<C,U,HashF>,
+ typename HashNode<C,U,HashF>::NodeHashF> > BaseType;
+
+ // Delegated constructors.
+ HashUncached() { }
+ HashUncached(int sizeHint) : BaseType(sizeHint) { }
+ HashUncached(const SelfType& src) : BaseType(src) { }
+ ~HashUncached() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+
+// And identity hash in which keys serve as hash value. Can be uncached,
+// since hash computation is assumed cheap.
+template<class C, class U, class Allocator = ContainerAllocator<C>, class HashF = IdentityHash<C> >
+class HashIdentity
+ : public HashUncached<C, U, HashF, Allocator>
+{
+public:
+ typedef HashIdentity<C, U, Allocator, HashF> SelfType;
+ typedef HashUncached<C, U, HashF, Allocator> BaseType;
+
+ // Delegated constructors.
+ HashIdentity() { }
+ HashIdentity(int sizeHint) : BaseType(sizeHint) { }
+ HashIdentity(const SelfType& src) : BaseType(src) { }
+ ~HashIdentity() { }
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+};
+
+
+} // OVR
+
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_KeyCodes.h b/LibOVR/Src/Kernel/OVR_KeyCodes.h
new file mode 100644
index 0000000..b5c5930
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_KeyCodes.h
@@ -0,0 +1,251 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_KeyCodes.h
+Content : Common keyboard constants
+Created : September 19, 2012
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_KeyCodes_h
+#define OVR_KeyCodes_h
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** KeyCode
+
+// KeyCode enumeration defines platform-independent keyboard key constants.
+// Note that Key_A through Key_Z are mapped to capital ascii constants.
+
+enum KeyCode
+{
+ // Key_None indicates that no key was specified.
+ Key_None = 0,
+
+ // A through Z and numbers 0 through 9.
+ Key_A = 65,
+ Key_B,
+ Key_C,
+ Key_D,
+ Key_E,
+ Key_F,
+ Key_G,
+ Key_H,
+ Key_I,
+ Key_J,
+ Key_K,
+ Key_L,
+ Key_M,
+ Key_N,
+ Key_O,
+ Key_P,
+ Key_Q,
+ Key_R,
+ Key_S,
+ Key_T,
+ Key_U,
+ Key_V,
+ Key_W,
+ Key_X,
+ Key_Y,
+ Key_Z,
+ Key_Num0 = 48,
+ Key_Num1,
+ Key_Num2,
+ Key_Num3,
+ Key_Num4,
+ Key_Num5,
+ Key_Num6,
+ Key_Num7,
+ Key_Num8,
+ Key_Num9,
+
+ // Numeric keypad.
+ Key_KP_0 = 0xa0,
+ Key_KP_1,
+ Key_KP_2,
+ Key_KP_3,
+ Key_KP_4,
+ Key_KP_5,
+ Key_KP_6,
+ Key_KP_7,
+ Key_KP_8,
+ Key_KP_9,
+ Key_KP_Multiply,
+ Key_KP_Add,
+ Key_KP_Enter,
+ Key_KP_Subtract,
+ Key_KP_Decimal,
+ Key_KP_Divide,
+
+ // Function keys.
+ Key_F1 = 0xb0,
+ Key_F2,
+ Key_F3,
+ Key_F4,
+ Key_F5,
+ Key_F6,
+ Key_F7,
+ Key_F8,
+ Key_F9,
+ Key_F10,
+ Key_F11,
+ Key_F12,
+ Key_F13,
+ Key_F14,
+ Key_F15,
+
+ // Other keys.
+ Key_Backspace = 8,
+ Key_Tab,
+ Key_Clear = 12,
+ Key_Return,
+ Key_Shift = 16,
+ Key_Control,
+ Key_Alt,
+ Key_Pause,
+ Key_CapsLock = 20, // Toggle
+ Key_Escape = 27,
+ Key_Space = 32,
+ Key_Quote = 39,
+ Key_PageUp = 0xc0,
+ Key_PageDown,
+ Key_End,
+ Key_Home,
+ Key_Left,
+ Key_Up,
+ Key_Right,
+ Key_Down,
+ Key_Insert,
+ Key_Delete,
+ Key_Help,
+
+ Key_Comma = 44,
+ Key_Minus,
+ Key_Slash = 47,
+ Key_Period,
+ Key_NumLock = 144, // Toggle
+ Key_ScrollLock = 145, // Toggle
+
+ Key_Semicolon = 59,
+ Key_Equal = 61,
+ Key_Backtick = 96, // ` and tilda~ when shifted (US keyboard)
+ Key_BracketLeft = 91,
+ Key_Backslash,
+ Key_BracketRight,
+
+ Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard
+ Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard.
+ Key_ICO_HELP = 0xE3, // Help key on ICO
+ Key_ICO_00 = 0xE4, // 00 key on ICO
+
+ Key_Meta,
+
+ // Total number of keys.
+ Key_CodeCount
+};
+
+
+//-----------------------------------------------------------------------------------
+
+class KeyModifiers
+{
+public:
+ enum
+ {
+ Key_ShiftPressed = 0x01,
+ Key_CtrlPressed = 0x02,
+ Key_AltPressed = 0x04,
+ Key_MetaPressed = 0x08,
+ Key_CapsToggled = 0x10,
+ Key_NumToggled = 0x20,
+ Key_ScrollToggled = 0x40,
+
+ Initialized_Bit = 0x80,
+ Initialized_Mask = 0xFF
+ };
+ unsigned char States;
+
+ KeyModifiers() : States(0) { }
+ KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { }
+
+ void Reset() { States = 0; }
+
+ bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; }
+ bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; }
+ bool IsAltPressed() const { return (States & Key_AltPressed) != 0; }
+ bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; }
+ bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; }
+ bool IsNumToggled() const { return (States & Key_NumToggled) != 0; }
+ bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; }
+
+ void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; }
+ void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; }
+ void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; }
+ void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; }
+ void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; }
+ void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; }
+ void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; }
+
+ bool IsInitialized() const { return (States & Initialized_Mask) != 0; }
+};
+
+
+//-----------------------------------------------------------------------------------
+
+/*
+enum PadKeyCode
+{
+ Pad_None, // Indicates absence of key code.
+ Pad_Back,
+ Pad_Start,
+ Pad_A,
+ Pad_B,
+ Pad_X,
+ Pad_Y,
+ Pad_R1, // RightShoulder;
+ Pad_L1, // LeftShoulder;
+ Pad_R2, // RightTrigger;
+ Pad_L2, // LeftTrigger;
+ Pad_Up,
+ Pad_Down,
+ Pad_Right,
+ Pad_Left,
+ Pad_Plus,
+ Pad_Minus,
+ Pad_1,
+ Pad_2,
+ Pad_H,
+ Pad_C,
+ Pad_Z,
+ Pad_O,
+ Pad_T,
+ Pad_S,
+ Pad_Select,
+ Pad_Home,
+ Pad_RT, // RightThumb;
+ Pad_LT // LeftThumb;
+};
+*/
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_List.h b/LibOVR/Src/Kernel/OVR_List.h
new file mode 100644
index 0000000..6b49f37
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_List.h
@@ -0,0 +1,336 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_List.h
+Content : Template implementation for doubly-connected linked List
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_List_h
+#define OVR_List_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** ListNode
+//
+// Base class for the elements of the intrusive linked list.
+// To store elements in the List do:
+//
+// struct MyData : ListNode<MyData>
+// {
+// . . .
+// };
+
+template<class T>
+struct ListNode
+{
+ union {
+ T* pPrev;
+ void* pVoidPrev;
+ };
+ union {
+ T* pNext;
+ void* pVoidNext;
+ };
+
+ void RemoveNode()
+ {
+ pPrev->pNext = pNext;
+ pNext->pPrev = pPrev;
+ }
+
+ // Removes us from the list and inserts pnew there instead.
+ void ReplaceNodeWith(T* pnew)
+ {
+ pPrev->pNext = pnew;
+ pNext->pPrev = pnew;
+ pnew->pPrev = pPrev;
+ pnew->pNext = pNext;
+ }
+
+ // Inserts the argument linked list node after us in the list.
+ void InsertNodeAfter(T* p)
+ {
+ p->pPrev = pNext->pPrev; // this
+ p->pNext = pNext;
+ pNext->pPrev = p;
+ pNext = p;
+ }
+ // Inserts the argument linked list node before us in the list.
+ void InsertNodeBefore(T* p)
+ {
+ p->pNext = pNext->pPrev; // this
+ p->pPrev = pPrev;
+ pPrev->pNext = p;
+ pPrev = p;
+ }
+
+ void Alloc_MoveTo(ListNode<T>* pdest)
+ {
+ pdest->pNext = pNext;
+ pdest->pPrev = pPrev;
+ pPrev->pNext = (T*)pdest;
+ pNext->pPrev = (T*)pdest;
+ }
+};
+
+
+//------------------------------------------------------------------------
+// ***** List
+//
+// Doubly linked intrusive list.
+// The data type must be derived from ListNode.
+//
+// Adding: PushFront(), PushBack().
+// Removing: Remove() - the element must be in the list!
+// Moving: BringToFront(), SendToBack() - the element must be in the list!
+//
+// Iterating:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// . . .
+// data = MyList.GetNext(data);
+// }
+//
+// Removing:
+// MyData* data = MyList.GetFirst();
+// while (!MyList.IsNull(data))
+// {
+// MyData* next = MyList.GetNext(data);
+// if (ToBeRemoved(data))
+// MyList.Remove(data);
+// data = next;
+// }
+//
+
+// List<> represents a doubly-linked list of T, where each T must derive
+// from ListNode<B>. B specifies the base class that was directly
+// derived from ListNode, and is only necessary if there is an intermediate
+// inheritance chain.
+
+template<class T, class B = T> class List
+{
+public:
+ typedef T ValueType;
+
+ List()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ void Clear()
+ {
+ Root.pNext = Root.pPrev = (ValueType*)&Root;
+ }
+
+ const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; }
+ const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; }
+ ValueType* GetFirst() { return (ValueType*)Root.pNext; }
+ ValueType* GetLast () { return (ValueType*)Root.pPrev; }
+
+ // Determine if list is empty (i.e.) points to itself.
+ // Go through void* access to avoid issues with strict-aliasing optimizing out the
+ // access after RemoveNode(), etc.
+ bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; }
+ bool IsFirst(const ValueType* p) const { return p == Root.pNext; }
+ bool IsLast (const ValueType* p) const { return p == Root.pPrev; }
+ bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; }
+
+ inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; }
+ inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; }
+ inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; }
+ inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; }
+
+ void PushFront(ValueType* p)
+ {
+ p->pNext = Root.pNext;
+ p->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = p;
+ Root.pNext = p;
+ }
+
+ void PushBack(ValueType* p)
+ {
+ p->pPrev = Root.pPrev;
+ p->pNext = (ValueType*)&Root;
+ Root.pPrev->pNext = p;
+ Root.pPrev = p;
+ }
+
+ static void Remove(ValueType* p)
+ {
+ p->pPrev->pNext = p->pNext;
+ p->pNext->pPrev = p->pPrev;
+ }
+
+ void BringToFront(ValueType* p)
+ {
+ Remove(p);
+ PushFront(p);
+ }
+
+ void SendToBack(ValueType* p)
+ {
+ Remove(p);
+ PushBack(p);
+ }
+
+ // Appends the contents of the argument list to the front of this list;
+ // items are removed from the argument list.
+ void PushListToFront(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ void PushListToBack(List<T>& src)
+ {
+ if (!src.IsEmpty())
+ {
+ ValueType* pfirst = src.GetFirst();
+ ValueType* plast = src.GetLast();
+ src.Clear();
+ plast->pNext = (ValueType*)&Root;
+ pfirst->pPrev = Root.pPrev;
+ Root.pPrev->pNext = pfirst;
+ Root.pPrev = plast;
+ }
+ }
+
+ // Removes all source list items after (and including) the 'pfirst' node from the
+ // source list and adds them to out list.
+ void PushFollowingListItemsToFront(List<T>& src, ValueType *pfirst)
+ {
+ if (pfirst != &src.Root)
+ {
+ ValueType *plast = src.Root.pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = (ValueType*)&src.Root;
+ src.Root.pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+ // Removes all source list items up to but NOT including the 'pend' node from the
+ // source list and adds them to out list.
+ void PushPrecedingListItemsToFront(List<T>& src, ValueType *ptail)
+ {
+ if (src.GetFirst() != ptail)
+ {
+ ValueType *pfirst = src.Root.pNext;
+ ValueType *plast = ptail->pPrev;
+
+ // Remove list remainder from source.
+ ptail->pPrev = (ValueType*)&src.Root;
+ src.Root.pNext = ptail;
+
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend',
+ // and adds them to out list. Note that source items MUST already be in the list.
+ void PushListItemsToFront(ValueType *pfirst, ValueType *pend)
+ {
+ if (pfirst != pend)
+ {
+ ValueType *plast = pend->pPrev;
+
+ // Remove list remainder from source.
+ pfirst->pPrev->pNext = pend;
+ pend->pPrev = pfirst->pPrev;
+ // Add the rest of the items to list.
+ plast->pNext = Root.pNext;
+ pfirst->pPrev = (ValueType*)&Root;
+ Root.pNext->pPrev = plast;
+ Root.pNext = pfirst;
+ }
+ }
+
+
+ void Alloc_MoveTo(List<T>* pdest)
+ {
+ if (IsEmpty())
+ pdest->Clear();
+ else
+ {
+ pdest->Root.pNext = Root.pNext;
+ pdest->Root.pPrev = Root.pPrev;
+
+ Root.pNext->pPrev = (ValueType*)&pdest->Root;
+ Root.pPrev->pNext = (ValueType*)&pdest->Root;
+ }
+ }
+
+
+private:
+ // Copying is prohibited
+ List(const List<T>&);
+ const List<T>& operator = (const List<T>&);
+
+ ListNode<B> Root;
+};
+
+
+//------------------------------------------------------------------------
+// ***** FreeListElements
+//
+// Remove all elements in the list and free them in the allocator
+
+template<class List, class Allocator>
+void FreeListElements(List& list, Allocator& allocator)
+{
+ typename List::ValueType* self = list.GetFirst();
+ while(!list.IsNull(self))
+ {
+ typename List::ValueType* next = list.GetNext(self);
+ allocator.Free(self);
+ self = next;
+ }
+ list.Clear();
+}
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Lockless.cpp b/LibOVR/Src/Kernel/OVR_Lockless.cpp
new file mode 100644
index 0000000..0f25805
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Lockless.cpp
@@ -0,0 +1,231 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Lockless.cpp
+Content : Test logic for lock-less classes
+Created : December 27, 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_Lockless.h"
+
+#ifdef OVR_LOCKLESS_TEST
+
+#include "OVR_Threads.h"
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+
+namespace OVR { namespace LocklessTest {
+
+
+const int TestIterations = 10000000;
+
+// Use volatile dummys to force compiler to do spinning.
+volatile int Dummy1;
+int Unused1[32];
+volatile int Dummy2;
+int Unused2[32];
+volatile int Dummy3;
+int Unused3[32];
+
+
+// Data block out of 20 consecutive integers, should be internally consistent.
+struct TestData
+{
+ enum { ItemCount = 20 };
+
+ int Data[ItemCount];
+
+
+ void Set(int val)
+ {
+ for (int i=0; i<ItemCount; i++)
+ {
+ Data[i] = val*100 + i;
+ }
+ }
+
+ int ReadAndCheckConsistency(int prevValue) const
+ {
+ int val = Data[0];
+
+ for (int i=1; i<ItemCount; i++)
+ {
+
+ if (Data[i] != (val + i))
+ {
+ // Only complain once per same-value entry
+ if (prevValue != val / 100)
+ {
+ LogText("LocklessTest Fail - corruption at %d inside block %d\n",
+ i, val/100);
+ // OVR_ASSERT(Data[i] == val + i);
+ }
+ break;
+ }
+ }
+
+ return val / 100;
+ }
+};
+
+
+
+volatile bool FirstItemWritten = false;
+LocklessUpdater<TestData> TestDataUpdater;
+
+// Use this lock to verify that testing algorithm is otherwise correct...
+Lock TestLock;
+
+
+//-------------------------------------------------------------------------------------
+
+// Consumer thread reads values from TestDataUpdater and
+// ensures that each one is internally consistent.
+
+class Consumer : public Thread
+{
+ virtual int Run()
+ {
+ LogText("LocklessTest::Consumer::Run started.\n");
+
+
+ while (!FirstItemWritten)
+ {
+ // spin until producer wrote first value...
+ }
+
+ TestData d;
+ int oldValue = 0;
+ int newValue;
+
+ do
+ {
+ {
+ //Lock::Locker scope(&TestLock);
+ d = TestDataUpdater.GetState();
+ }
+
+ newValue = d.ReadAndCheckConsistency(oldValue);
+
+ // Values should increase or stay the same!
+ if (newValue < oldValue)
+ {
+ LogText("LocklessTest Fail - %d after %d; delta = %d\n",
+ newValue, oldValue, newValue - oldValue);
+ // OVR_ASSERT(0);
+ }
+
+
+ if (oldValue != newValue)
+ {
+ oldValue = newValue;
+
+ if (oldValue % (TestIterations/30) == 0)
+ {
+ LogText("LocklessTest::Consumer - %5.2f%% done\n",
+ 100.0f * (float)oldValue/(float)TestIterations);
+ }
+ }
+
+ // Spin a while
+ for (int j = 0; j< 300; j++)
+ {
+ Dummy3 = j;
+ }
+
+
+ } while (oldValue < (TestIterations * 99 / 100));
+
+ LogText("LocklessTest::Consumer::Run exiting.\n");
+ return 0;
+ }
+
+};
+
+
+//-------------------------------------------------------------------------------------
+
+class Producer : public Thread
+{
+
+ virtual int Run()
+ {
+ LogText("LocklessTest::Producer::Run started.\n");
+
+ for (int testVal = 0; testVal < TestIterations; testVal++)
+ {
+ TestData d;
+ d.Set(testVal);
+
+ {
+ //Lock::Locker scope(&TestLock);
+ TestDataUpdater.SetState(d);
+ }
+
+ FirstItemWritten = true;
+
+ // Spin a bit
+ for(int j = 0; j < 1000; j++)
+ {
+ Dummy2 = j;
+ }
+
+ if (testVal % (TestIterations/30) == 0)
+ {
+ LogText("LocklessTest::Producer - %5.2f%% done\n",
+ 100.0f * (float)testVal/(float)TestIterations);
+ }
+ }
+
+ LogText("LocklessTest::Producer::Run exiting.\n");
+ return 0;
+ }
+};
+
+
+} // namespace LocklessTest
+
+
+
+void StartLocklessTest()
+{
+ // These threads will release themselves once done
+ Ptr<LocklessTest::Producer> producerThread = *new LocklessTest::Producer;
+ Ptr<LocklessTest::Consumer> consumerThread = *new LocklessTest::Consumer;
+
+ producerThread->Start();
+ consumerThread->Start();
+
+ /*
+ while (!producerThread->IsFinished() && consumerThread->IsFinished())
+ {
+ Thread::MSleep(500);
+ } */
+
+ // TBD: Cleanup
+}
+
+
+} // namespace OVR
+
+#endif // OVR_LOCKLESS_TEST
diff --git a/LibOVR/Src/Kernel/OVR_Lockless.h b/LibOVR/Src/Kernel/OVR_Lockless.h
new file mode 100644
index 0000000..a12f824
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Lockless.h
@@ -0,0 +1,107 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Lockless.h
+Content : Lock-less classes for producer/consumer communication
+Created : November 9, 2013
+Authors : John Carmack
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Lockless_h
+#define OVR_Lockless_h
+
+#include "OVR_Atomic.h"
+
+// Define this to compile-in Lockless test logic
+//#define OVR_LOCKLESS_TEST
+
+namespace OVR {
+
+
+// ***** LocklessUpdater
+
+// For single producer cases where you only care about the most recent update, not
+// necessarily getting every one that happens (vsync timing, SensorFusion updates).
+//
+// This is multiple consumer safe, but is currently only used with a single consumer.
+
+template<class T>
+class LocklessUpdater
+{
+public:
+ LocklessUpdater() : UpdateBegin( 0 ), UpdateEnd( 0 ) {}
+
+ T GetState() const
+ {
+ // Copy the state out, then retry with the alternate slot
+ // if we determine that our copy may have been partially
+ // stepped on by a new update.
+ T state;
+ int begin, end, final;
+
+ for(;;)
+ {
+ // We are adding 0, only using these as atomic memory barriers, so it
+ // is ok to cast off the const, allowing GetState() to remain const.
+ end = UpdateEnd.ExchangeAdd_Sync(0);
+ state = Slots[ end & 1 ];
+ begin = UpdateBegin.ExchangeAdd_Sync(0);
+ if ( begin == end ) {
+ break;
+ }
+
+ // The producer is potentially blocked while only having partially
+ // written the update, so copy out the other slot.
+ state = Slots[ (begin & 1) ^ 1 ];
+ final = UpdateBegin.ExchangeAdd_NoSync(0);
+ if ( final == begin ) {
+ break;
+ }
+
+ // The producer completed the last update and started a new one before
+ // we got it copied out, so try fetching the current buffer again.
+ }
+ return state;
+ }
+
+ void SetState( T state )
+ {
+ const int slot = UpdateBegin.ExchangeAdd_Sync(1) & 1;
+ // Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add.
+ Slots[slot ^ 1] = state;
+ UpdateEnd.ExchangeAdd_Sync(1);
+ }
+
+ mutable AtomicInt<int> UpdateBegin;
+ mutable AtomicInt<int> UpdateEnd;
+ T Slots[2];
+};
+
+
+#ifdef OVR_LOCKLESS_TEST
+void StartLocklessTest();
+#endif
+
+
+} // namespace OVR
+
+#endif // OVR_Lockless_h
+
diff --git a/LibOVR/Src/Kernel/OVR_Log.cpp b/LibOVR/Src/Kernel/OVR_Log.cpp
new file mode 100644
index 0000000..d5f8a68
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Log.cpp
@@ -0,0 +1,184 @@
+/************************************************************************************
+
+Filename : OVR_Log.cpp
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+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_Log.h"
+#include "OVR_Std.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#if defined(OVR_OS_WIN32)
+#include <windows.h>
+#elif defined(OVR_OS_ANDROID)
+#include <android/log.h>
+#endif
+
+namespace OVR {
+
+// Global Log pointer.
+Log* volatile OVR_GlobalLog = 0;
+
+//-----------------------------------------------------------------------------------
+// ***** Log Implementation
+
+Log::~Log()
+{
+ // Clear out global log
+ if (this == OVR_GlobalLog)
+ {
+ // TBD: perhaps we should ASSERT if this happens before system shutdown?
+ OVR_GlobalLog = 0;
+ }
+}
+
+void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList)
+{
+ if ((messageType & LoggingMask) == 0)
+ return;
+#ifndef OVR_BUILD_DEBUG
+ if (IsDebugMessage(messageType))
+ return;
+#endif
+
+ char buffer[MaxLogBufferMessageSize];
+ FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList);
+ DefaultLogOutput(buffer, IsDebugMessage(messageType));
+}
+
+void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...)
+{
+ va_list argList;
+ va_start(argList, pfmt);
+ LogMessageVarg(messageType, pfmt, argList);
+ va_end(argList);
+}
+
+
+void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList)
+{
+ bool addNewline = true;
+
+ switch(messageType)
+ {
+ case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break;
+ case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break;
+ case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break;
+ case Log_Text: buffer[0] = 0; addNewline = false; break;
+ case Log_DebugText: buffer[0] = 0; addNewline = false; break;
+ default:
+ buffer[0] = 0;
+ addNewline = false;
+ break;
+ }
+
+ UPInt prefixLength = OVR_strlen(buffer);
+ char *buffer2 = buffer + prefixLength;
+ OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList);
+
+ if (addNewline)
+ OVR_strcat(buffer, bufferSize, "\n");
+}
+
+
+void Log::DefaultLogOutput(const char* formattedText, bool debug)
+{
+
+#if defined(OVR_OS_WIN32)
+ // Under Win32, output regular messages to console if it exists; debug window otherwise.
+ static DWORD dummyMode;
+ static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) &&
+ (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode));
+
+ if (!hasConsole || debug)
+ {
+ ::OutputDebugStringA(formattedText);
+ }
+ else
+ {
+ fputs(formattedText, stdout);
+ }
+
+#elif defined(OVR_OS_ANDROID)
+ __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText);
+
+#else
+ fputs(formattedText, stdout);
+
+#endif
+
+ // Just in case.
+ OVR_UNUSED2(formattedText, debug);
+}
+
+
+//static
+void Log::SetGlobalLog(Log *log)
+{
+ OVR_GlobalLog = log;
+}
+//static
+Log* Log::GetGlobalLog()
+{
+// No global log by default?
+// if (!OVR_GlobalLog)
+// OVR_GlobalLog = GetDefaultLog();
+ return OVR_GlobalLog;
+}
+
+//static
+Log* Log::GetDefaultLog()
+{
+ // Create default log pointer statically so that it can be used
+ // even during startup.
+ static Log defaultLog;
+ return &defaultLog;
+}
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging functions
+
+#define OVR_LOG_FUNCTION_IMPL(Name) \
+ void Log##Name(const char* fmt, ...) \
+ { \
+ if (OVR_GlobalLog) \
+ { \
+ va_list argList; va_start(argList, fmt); \
+ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \
+ va_end(argList); \
+ } \
+ }
+
+OVR_LOG_FUNCTION_IMPL(Text)
+OVR_LOG_FUNCTION_IMPL(Error)
+
+#ifdef OVR_BUILD_DEBUG
+OVR_LOG_FUNCTION_IMPL(DebugText)
+OVR_LOG_FUNCTION_IMPL(Debug)
+OVR_LOG_FUNCTION_IMPL(Assert)
+#endif
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Log.h b/LibOVR/Src/Kernel/OVR_Log.h
new file mode 100644
index 0000000..4d9acc1
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Log.h
@@ -0,0 +1,204 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Log.h
+Content : Logging support
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Log_h
+#define OVR_Log_h
+
+#include "OVR_Types.h"
+#include <stdarg.h>
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Logging Constants
+
+// LogMaskConstants defined bit mask constants that describe what log messages
+// should be displayed.
+enum LogMaskConstants
+{
+ LogMask_Regular = 0x100,
+ LogMask_Debug = 0x200,
+ LogMask_None = 0,
+ LogMask_All = LogMask_Regular|LogMask_Debug
+};
+
+
+// LogMessageType describes the type of the log message, controls when it is
+// displayed and what prefix/suffix is given to it. Messages are subdivided into
+// regular and debug logging types. Debug logging is only generated in debug builds.
+//
+// Log_Text - General output text displayed without prefix or new-line.
+// Used in OVR libraries for general log flow messages
+// such as "Device Initialized".
+//
+// Log_Error - Error message output with "Error: %s\n", intended for
+// application/sample-level use only, in cases where an expected
+// operation failed. OVR libraries should not use this internally,
+// reporting status codes instead.
+//
+// Log_DebugText - Message without prefix or new lines; output in Debug build only.
+//
+// Log_Debug - Debug-build only message, formatted with "Debug: %s\n".
+// Intended to comment on incorrect API usage that doesn't lead
+// to crashes but can be avoided with proper use.
+// There is no Debug Error on purpose, since real errors should
+// be handled by API user.
+//
+// Log_Assert - Debug-build only message, formatted with "Assert: %s\n".
+// Intended for severe unrecoverable conditions in library
+// source code. Generated though OVR_ASSERT_MSG(c, "Text").
+
+enum LogMessageType
+{
+ // General Logging
+ Log_Text = LogMask_Regular | 0,
+ Log_Error = LogMask_Regular | 1, // "Error: %s\n".
+
+ // Debug-only messages (not generated in release build)
+ Log_DebugText = LogMask_Debug | 0,
+ Log_Debug = LogMask_Debug | 1, // "Debug: %s\n".
+ Log_Assert = LogMask_Debug | 2, // "Assert: %s\n".
+};
+
+
+// LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types
+#ifdef __GNUC__
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b)))
+#else
+# define OVR_LOG_VAARG_ATTRIBUTE(a,b)
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Log
+
+// Log defines a base class interface that can be implemented to catch both
+// debug and runtime messages.
+// Debug logging can be overridden by calling Log::SetGlobalLog.
+
+class Log
+{
+ friend class System;
+public:
+ Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { }
+ virtual ~Log();
+
+ // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated.
+ enum { MaxLogBufferMessageSize = 4096 };
+
+ unsigned GetLoggingMask() const { return LoggingMask; }
+ void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; }
+
+ // This virtual function receives all the messages,
+ // developers should override this function in order to do custom logging
+ virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList);
+
+ // Call the logging function with specific message type, with no type filtering.
+ void LogMessage(LogMessageType messageType,
+ const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4);
+
+
+ // Helper used by LogMessageVarg to format the log message, writing the resulting
+ // string into buffer. It formats text based on fmt and appends prefix/new line
+ // based on LogMessageType.
+ static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType,
+ const char* fmt, va_list argList);
+
+ // Default log output implementation used by by LogMessageVarg.
+ // Debug flag may be used to re-direct output on some platforms, but doesn't
+ // necessarily disable it in release builds; that is the job of the called.
+ static void DefaultLogOutput(const char* textBuffer, bool debug);
+
+ // Determines if the specified message type is for debugging only.
+ static bool IsDebugMessage(LogMessageType messageType)
+ {
+ return (messageType & LogMask_Debug) != 0;
+ }
+
+ // *** Global APIs
+
+ // Global Log registration APIs.
+ // - Global log is used for OVR_DEBUG messages. Set global log to null (0)
+ // to disable all logging.
+ static void SetGlobalLog(Log *log);
+ static Log* GetGlobalLog();
+
+ // Returns default log singleton instance.
+ static Log* GetDefaultLog();
+
+ // Applies logMask to the default log and returns a pointer to it.
+ // By default, only Debug logging is enabled, so to avoid SDK generating console
+ // messages in user app (those are always disabled in release build,
+ // even if the flag is set). This function is useful in System constructor.
+ static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug)
+ {
+ Log* log = GetDefaultLog();
+ log->SetLoggingMask(logMask);
+ return log;
+ }
+
+private:
+ // Logging mask described by LogMaskConstants.
+ unsigned LoggingMask;
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Global Logging Functions and Debug Macros
+
+// These functions will output text to global log with semantics described by
+// their LogMessageType.
+void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+#ifdef OVR_BUILD_DEBUG
+
+ // Debug build only logging.
+ void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+ void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2);
+
+ // Macro to do debug logging, printf-style.
+ // An extra set of set of parenthesis must be used around arguments,
+ // as in: OVR_LOG_DEBUG(("Value %d", 2)).
+ #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0)
+ #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0)
+
+ #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0)
+
+#else
+
+ // If not in debug build, macros do nothing.
+ #define OVR_DEBUG_LOG(args) ((void)0)
+ #define OVR_DEBUG_LOG_TEXT(args) ((void)0)
+ #define OVR_ASSERT_LOG(c, args) ((void)0)
+
+#endif
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Math.cpp b/LibOVR/Src/Kernel/OVR_Math.cpp
new file mode 100644
index 0000000..396d3ff
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Math.cpp
@@ -0,0 +1,91 @@
+/************************************************************************************
+
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov, Anna Yershova
+
+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_Math.h"
+#include "OVR_Log.h"
+
+#include <float.h>
+
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** Math
+
+
+// Single-precision Math constants class.
+const float Math<float>::Pi = 3.1415926f;
+const float Math<float>::TwoPi = 3.1415926f * 2;
+const float Math<float>::PiOver2 = 3.1415926f / 2.0f;
+const float Math<float>::PiOver4 = 3.1415926f / 4.0f;
+const float Math<float>::E = 2.7182818f;
+
+const float Math<float>::MaxValue = FLT_MAX;
+const float Math<float>::MinPositiveValue = FLT_MIN;
+
+const float Math<float>::RadToDegreeFactor = 360.0f / Math<float>::TwoPi;
+const float Math<float>::DegreeToRadFactor = Math<float>::TwoPi / 360.0f;
+
+const float Math<float>::Tolerance = 0.00001f;
+const float Math<float>::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems
+
+// Double-precision Math constants class.
+const double Math<double>::Pi = 3.14159265358979;
+const double Math<double>::TwoPi = 3.14159265358979 * 2;
+const double Math<double>::PiOver2 = 3.14159265358979 / 2.0;
+const double Math<double>::PiOver4 = 3.14159265358979 / 4.0;
+const double Math<double>::E = 2.71828182845905;
+
+const double Math<double>::MaxValue = DBL_MAX;
+const double Math<double>::MinPositiveValue = DBL_MIN;
+
+const double Math<double>::RadToDegreeFactor = 360.0 / Math<double>::TwoPi;
+const double Math<double>::DegreeToRadFactor = Math<double>::TwoPi / 360.0;
+
+const double Math<double>::Tolerance = 0.00001;
+const double Math<double>::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4
+
+template<>
+const Matrix4<float> Matrix4<float>::IdentityValue = Matrix4<float>(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+template<>
+const Matrix4<double> Matrix4<double>::IdentityValue = Matrix4<double>(1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+
+
+
+} // Namespace OVR
diff --git a/LibOVR/Src/Kernel/OVR_Math.h b/LibOVR/Src/Kernel/OVR_Math.h
new file mode 100644
index 0000000..4aa42b0
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Math.h
@@ -0,0 +1,2526 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Math.h
+Content : Implementation of 3D primitives such as vectors, matrices.
+Created : September 4, 2012
+Authors : Andrew Reisse, Michael Antonov, Steve LaValle,
+ Anna Yershova, Max Katsev, Dov Katz
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Math_h
+#define OVR_Math_h
+
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "OVR_Types.h"
+#include "OVR_RefCount.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Constants for 3D world/axis definitions.
+
+// Definitions of axes for coordinate and rotation conversions.
+enum Axis
+{
+ Axis_X = 0, Axis_Y = 1, Axis_Z = 2
+};
+
+// RotateDirection describes the rotation direction around an axis, interpreted as follows:
+// CW - Clockwise while looking "down" from positive axis towards the origin.
+// CCW - Counter-clockwise while looking from the positive axis towards the origin,
+// which is in the negative axis direction.
+// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate
+// system defines Y up, X right, and Z back (pointing out from the screen). In this
+// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane.
+enum RotateDirection
+{
+ Rotate_CCW = 1,
+ Rotate_CW = -1
+};
+
+// Constants for right handed and left handed coordinate systems
+enum HandedSystem
+{
+ Handed_R = 1, Handed_L = -1
+};
+
+// AxisDirection describes which way the coordinate axis points. Used by WorldAxes.
+enum AxisDirection
+{
+ Axis_Up = 2,
+ Axis_Down = -2,
+ Axis_Right = 1,
+ Axis_Left = -1,
+ Axis_In = 3,
+ Axis_Out = -3
+};
+
+struct WorldAxes
+{
+ AxisDirection XAxis, YAxis, ZAxis;
+
+ WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z)
+ : XAxis(x), YAxis(y), ZAxis(z)
+ { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));}
+};
+
+} // namespace OVR
+
+
+//------------------------------------------------------------------------------------//
+// ***** C Compatibility Types
+
+// These declarations are used to support conversion between C types used in
+// LibOVR C interfaces and their C++ versions. As an example, they allow passing
+// Vector3f into a function that expects ovrVector3f.
+
+typedef struct ovrQuatf_ ovrQuatf;
+typedef struct ovrQuatd_ ovrQuatd;
+typedef struct ovrSizei_ ovrSizei;
+typedef struct ovrSizef_ ovrSizef;
+typedef struct ovrRecti_ ovrRecti;
+typedef struct ovrVector2i_ ovrVector2i;
+typedef struct ovrVector2f_ ovrVector2f;
+typedef struct ovrVector3f_ ovrVector3f;
+typedef struct ovrVector3d_ ovrVector3d;
+typedef struct ovrMatrix3d_ ovrMatrix3d;
+typedef struct ovrMatrix4f_ ovrMatrix4f;
+typedef struct ovrPosef_ ovrPosef;
+typedef struct ovrPosed_ ovrPosed;
+typedef struct ovrPoseStatef_ ovrPoseStatef;
+typedef struct ovrPoseStated_ ovrPoseStated;
+
+namespace OVR {
+
+// Forward-declare our templates.
+template<class T> class Quat;
+template<class T> class Size;
+template<class T> class Rect;
+template<class T> class Vector2;
+template<class T> class Vector3;
+template<class T> class Matrix3;
+template<class T> class Matrix4;
+template<class T> class Transform;
+template<class T> class PoseState;
+
+// CompatibleTypes::Type is used to lookup a compatible C-version of a C++ class.
+template<class C>
+struct CompatibleTypes
+{
+ // Declaration here seems necessary for MSVC; specializations are
+ // used instead.
+ typedef float Type;
+};
+
+// Specializations providing CompatibleTypes::Type value.
+template<> struct CompatibleTypes<Quat<float> > { typedef ovrQuatf Type; };
+template<> struct CompatibleTypes<Quat<double> > { typedef ovrQuatd Type; };
+template<> struct CompatibleTypes<Matrix3<double> > { typedef ovrMatrix3d Type; };
+template<> struct CompatibleTypes<Matrix4<float> > { typedef ovrMatrix4f Type; };
+template<> struct CompatibleTypes<Size<int> > { typedef ovrSizei Type; };
+template<> struct CompatibleTypes<Size<float> > { typedef ovrSizef Type; };
+template<> struct CompatibleTypes<Rect<int> > { typedef ovrRecti Type; };
+template<> struct CompatibleTypes<Vector2<int> > { typedef ovrVector2i Type; };
+template<> struct CompatibleTypes<Vector2<float> > { typedef ovrVector2f Type; };
+template<> struct CompatibleTypes<Vector3<float> > { typedef ovrVector3f Type; };
+template<> struct CompatibleTypes<Vector3<double> > { typedef ovrVector3d Type; };
+
+template<> struct CompatibleTypes<Transform<float> > { typedef ovrPosef Type; };
+template<> struct CompatibleTypes<PoseState<float> > { typedef ovrPoseStatef Type; };
+
+template<> struct CompatibleTypes<Transform<double> > { typedef ovrPosed Type; };
+template<> struct CompatibleTypes<PoseState<double> > { typedef ovrPoseStated Type; };
+
+//------------------------------------------------------------------------------------//
+// ***** Math
+//
+// Math class contains constants and functions. This class is a template specialized
+// per type, with Math<float> and Math<double> being distinct.
+template<class Type>
+class Math
+{
+public:
+ // By default, support explicit conversion to float. This allows Vector2<int> to
+ // compile, for example.
+ typedef float OtherFloatType;
+};
+
+// Single-precision Math constants class.
+template<>
+class Math<float>
+{
+public:
+ static const float Pi;
+ static const float TwoPi;
+ static const float PiOver2;
+ static const float PiOver4;
+ static const float E;
+
+ static const float MaxValue; // Largest positive float Value
+ static const float MinPositiveValue; // Smallest possible positive value
+
+ static const float RadToDegreeFactor;
+ static const float DegreeToRadFactor;
+
+ static const float Tolerance; // 0.00001f;
+ static const float SingularityRadius; // 0.0000001f for Gimbal lock numerical problems
+
+ // Used to support direct conversions in template classes.
+ typedef double OtherFloatType;
+};
+
+// Double-precision Math constants class.
+template<>
+class Math<double>
+{
+public:
+ static const double Pi;
+ static const double TwoPi;
+ static const double PiOver2;
+ static const double PiOver4;
+ static const double E;
+
+ static const double MaxValue; // Largest positive double Value
+ static const double MinPositiveValue; // Smallest possible positive value
+
+ static const double RadToDegreeFactor;
+ static const double DegreeToRadFactor;
+
+ static const double Tolerance; // 0.00001;
+ static const double SingularityRadius; // 0.000000000001 for Gimbal lock numerical problems
+
+ typedef float OtherFloatType;
+};
+
+
+typedef Math<float> Mathf;
+typedef Math<double> Mathd;
+
+// Conversion functions between degrees and radians
+template<class T>
+T RadToDegree(T rads) { return rads * Math<T>::RadToDegreeFactor; }
+template<class T>
+T DegreeToRad(T rads) { return rads * Math<T>::DegreeToRadFactor; }
+
+// Numerically stable acos function
+template<class T>
+T Acos(T val) {
+ if (val > T(1)) return T(0);
+ else if (val < T(-1)) return Math<T>::Pi;
+ else return acos(val);
+};
+
+// Numerically stable asin function
+template<class T>
+T Asin(T val) {
+ if (val > T(1)) return Math<T>::PiOver2;
+ else if (val < T(-1)) return Math<T>::PiOver2 * T(3);
+ else return asin(val);
+};
+
+#ifdef OVR_CC_MSVC
+inline int isnan(double x) { return _isnan(x); };
+#endif
+
+template<class T>
+class Quat;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Vector2<>
+
+// Vector2f (Vector2d) represents a 2-dimensional vector or point in space,
+// consisting of coordinates x and y
+
+template<class T>
+class Vector2
+{
+public:
+ T x, y;
+
+ Vector2() : x(0), y(0) { }
+ Vector2(T x_, T y_) : x(x_), y(y_) { }
+ explicit Vector2(T s) : x(s), y(s) { }
+ explicit Vector2(const Vector2<typename Math<T>::OtherFloatType> &src)
+ : x((T)src.x), y((T)src.y) { }
+
+
+ // C-interop support.
+ typedef typename CompatibleTypes<Vector2<T> >::Type CompatibleType;
+
+ Vector2(const CompatibleType& s) : x(s.x), y(s.y) { }
+
+ operator const CompatibleType& () const
+ {
+ OVR_COMPILER_ASSERT(sizeof(Vector2<T>) == sizeof(CompatibleType));
+ return reinterpret_cast<const CompatibleType&>(*this);
+ }
+
+
+ bool operator== (const Vector2& b) const { return x == b.x && y == b.y; }
+ bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; }
+
+ Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); }
+ Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; }
+ Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); }
+ Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; }
+ Vector2 operator- () const { return Vector2(-x, -y); }
+
+ // Scalar multiplication/division scales vector.
+ Vector2 operator* (T s) const { return Vector2(x*s, y*s); }
+ Vector2& operator*= (T s) { x *= s; y *= s; return *this; }
+
+ Vector2 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector2(x*rcp, y*rcp); }
+ Vector2& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp;
+ return *this; }
+
+ static Vector2 Min(const Vector2& a, const Vector2& b) { return Vector2((a.x < b.x) ? a.x : b.x,
+ (a.y < b.y) ? a.y : b.y); }
+ static Vector2 Max(const Vector2& a, const Vector2& b) { return Vector2((a.x > b.x) ? a.x : b.x,
+ (a.y > b.y) ? a.y : b.y); }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance);
+ }
+
+ // Entrywise product of two vectors
+ Vector2 EntrywiseMultiply(const Vector2& b) const { return Vector2(x * b.x, y * b.y);}
+
+
+ // Multiply and divide operators do entry-wise math. Used Dot() for dot product.
+ Vector2 operator* (const Vector2& b) const { return Vector2(x * b.x, y * b.y); }
+ Vector2 operator/ (const Vector2& b) const { return Vector2(x / b.x, y / b.y); }
+
+ // Dot product
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T Dot(const Vector2& b) const { return x*b.x + y*b.y; }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector2& b) const
+ {
+ T div = LengthSq()*b.LengthSq();
+ OVR_ASSERT(div != T(0));
+ T result = Acos((this->Dot(b))/sqrt(div));
+ return result;
+ }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y); }
+
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns squared distance between two points represented by vectors.
+ T DistanceSq(Vector2& b) const { return (*this - b).LengthSq(); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector2& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+
+ // Normalize, convention vector length to 1.
+ void Normalize()
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ *this /= l;
+ }
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector2 Normalized() const
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ return *this / l;
+ }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector2 ProjectTo(const Vector2& b) const
+ {
+ T l2 = b.LengthSq();
+ OVR_ASSERT(l2 != T(0));
+ return b * ( Dot(b) / l2 );
+ }
+};
+
+
+typedef Vector2<float> Vector2f;
+typedef Vector2<double> Vector2d;
+typedef Vector2<int> Vector2i;
+
+//-------------------------------------------------------------------------------------
+// ***** Vector3<> - 3D vector of {x, y, z}
+
+//
+// Vector3f (Vector3d) represents a 3-dimensional vector or point in space,
+// consisting of coordinates x, y and z.
+
+template<class T>
+class Vector3
+{
+public:
+ T x, y, z;
+
+ Vector3() : x(0), y(0), z(0) { }
+ Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { }
+ explicit Vector3(T s) : x(s), y(s), z(s) { }
+ explicit Vector3(const Vector3<typename Math<T>::OtherFloatType> &src)
+ : x((T)src.x), y((T)src.y), z((T)src.z) { }
+
+
+ // C-interop support.
+ typedef typename CompatibleTypes<Vector3<T> >::Type CompatibleType;
+
+ Vector3(const CompatibleType& s) : x(s.x), y(s.y), z(s.z) { }
+
+ operator const CompatibleType& () const
+ {
+ OVR_COMPILER_ASSERT(sizeof(Vector3<T>) == sizeof(CompatibleType));
+ return reinterpret_cast<const CompatibleType&>(*this);
+ }
+
+ bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; }
+ bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; }
+
+ Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
+ Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; }
+ Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
+ Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; }
+ Vector3 operator- () const { return Vector3(-x, -y, -z); }
+
+ // Scalar multiplication/division scales vector.
+ Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); }
+ Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; }
+
+ Vector3 operator/ (T s) const { T rcp = T(1)/s;
+ return Vector3(x*rcp, y*rcp, z*rcp); }
+ Vector3& operator/= (T s) { T rcp = T(1)/s;
+ x *= rcp; y *= rcp; z *= rcp;
+ return *this; }
+
+ static Vector3 Min(const Vector3& a, const Vector3& b)
+ {
+ return Vector3((a.x < b.x) ? a.x : b.x,
+ (a.y < b.y) ? a.y : b.y,
+ (a.z < b.z) ? a.z : b.z);
+ }
+ static Vector3 Max(const Vector3& a, const Vector3& b)
+ {
+ return Vector3((a.x > b.x) ? a.x : b.x,
+ (a.y > b.y) ? a.y : b.y,
+ (a.z > b.z) ? a.z : b.z);
+ }
+
+ // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
+ bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance)
+ {
+ return (fabs(b.x-x) < tolerance) &&
+ (fabs(b.y-y) < tolerance) &&
+ (fabs(b.z-z) < tolerance);
+ }
+
+ T& operator[] (int idx)
+ {
+ OVR_ASSERT(0 <= idx && idx < 3);
+ return *(&x + idx);
+ }
+
+ const T& operator[] (int idx) const
+ {
+ OVR_ASSERT(0 <= idx && idx < 3);
+ return *(&x + idx);
+ }
+
+ // Entrywise product of two vectors
+ Vector3 EntrywiseMultiply(const Vector3& b) const { return Vector3(x * b.x,
+ y * b.y,
+ z * b.z);}
+
+ // Multiply and divide operators do entry-wise math
+ Vector3 operator* (const Vector3& b) const { return Vector3(x * b.x,
+ y * b.y,
+ z * b.z); }
+
+ Vector3 operator/ (const Vector3& b) const { return Vector3(x / b.x,
+ y / b.y,
+ z / b.z); }
+
+
+ // Dot product
+ // Used to calculate angle q between two vectors among other things,
+ // as (A dot B) = |a||b|cos(q).
+ T Dot(const Vector3& b) const { return x*b.x + y*b.y + z*b.z; }
+
+ // Compute cross product, which generates a normal vector.
+ // Direction vector can be determined by right-hand rule: Pointing index finder in
+ // direction a and middle finger in direction b, thumb will point in a.Cross(b).
+ Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y,
+ z*b.x - x*b.z,
+ x*b.y - y*b.x); }
+
+ // Returns the angle from this vector to b, in radians.
+ T Angle(const Vector3& b) const
+ {
+ T div = LengthSq()*b.LengthSq();
+ OVR_ASSERT(div != T(0));
+ T result = Acos((this->Dot(b))/sqrt(div));
+ return result;
+ }
+
+ // Return Length of the vector squared.
+ T LengthSq() const { return (x * x + y * y + z * z); }
+
+ // Return vector length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Returns squared distance between two points represented by vectors.
+ T DistanceSq(Vector3 const& b) const { return (*this - b).LengthSq(); }
+
+ // Returns distance between two points represented by vectors.
+ T Distance(Vector3 const& b) const { return (*this - b).Length(); }
+
+ // Determine if this a unit vector.
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+
+ // Normalize, convention vector length to 1.
+ void Normalize()
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ *this /= l;
+ }
+
+ // Returns normalized (unit) version of the vector without modifying itself.
+ Vector3 Normalized() const
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ return *this / l;
+ }
+
+ // Linearly interpolates from this vector to another.
+ // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
+ Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; }
+
+ // Projects this vector onto the argument; in other words,
+ // A.Project(B) returns projection of vector A onto B.
+ Vector3 ProjectTo(const Vector3& b) const
+ {
+ T l2 = b.LengthSq();
+ OVR_ASSERT(l2 != T(0));
+ return b * ( Dot(b) / l2 );
+ }
+
+ // Projects this vector onto a plane defined by a normal vector
+ Vector3 ProjectToPlane(const Vector3& normal) const { return *this - this->ProjectTo(normal); }
+};
+
+
+typedef Vector3<float> Vector3f;
+typedef Vector3<double> Vector3d;
+typedef Vector3<SInt32> Vector3i;
+
+
+// JDC: this was defined in Render_Device.h, I moved it here, but it
+// needs to be fleshed out like the other Vector types.
+//
+// A vector with a dummy w component for alignment in uniform buffers (and for float colors).
+// The w component is not used in any calculations.
+
+struct Vector4f : public Vector3f
+{
+ float w;
+
+ Vector4f() : w(1) {}
+ Vector4f(const Vector3f& v) : Vector3f(v), w(1) {}
+ Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {}
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Size
+
+// Size class represents 2D size with Width, Height components.
+// Used to describe distentions of render targets, etc.
+
+template<class T>
+class Size
+{
+public:
+ T w, h;
+
+ Size() : w(0), h(0) { }
+ Size(T w_, T h_) : w(w_), h(h_) { }
+ explicit Size(T s) : w(s), h(s) { }
+ explicit Size(const Size<typename Math<T>::OtherFloatType> &src)
+ : w((T)src.w), h((T)src.h) { }
+
+ // C-interop support.
+ typedef typename CompatibleTypes<Size<T> >::Type CompatibleType;
+
+ Size(const CompatibleType& s) : w(s.w), h(s.h) { }
+
+ operator const CompatibleType& () const
+ {
+ OVR_COMPILER_ASSERT(sizeof(Size<T>) == sizeof(CompatibleType));
+ return reinterpret_cast<const CompatibleType&>(*this);
+ }
+
+ bool operator== (const Size& b) const { return w == b.w && h == b.h; }
+ bool operator!= (const Size& b) const { return w != b.w || h != b.h; }
+
+ Size operator+ (const Size& b) const { return Size(w + b.w, h + b.h); }
+ Size& operator+= (const Size& b) { w += b.w; h += b.h; return *this; }
+ Size operator- (const Size& b) const { return Size(w - b.w, h - b.h); }
+ Size& operator-= (const Size& b) { w -= b.w; h -= b.h; return *this; }
+ Size operator- () const { return Size(-w, -h); }
+ Size operator* (const Size& b) const { return Size(w * b.w, h * b.h); }
+ Size& operator*= (const Size& b) { w *= b.w; h *= b.h; return *this; }
+ Size operator/ (const Size& b) const { return Size(w / b.w, h / b.h); }
+ Size& operator/= (const Size& b) { w /= b.w; h /= b.h; return *this; }
+
+ // Scalar multiplication/division scales both components.
+ Size operator* (T s) const { return Size(w*s, h*s); }
+ Size& operator*= (T s) { w *= s; h *= s; return *this; }
+ Size operator/ (T s) const { return Size(w/s, h/s); }
+ Size& operator/= (T s) { w /= s; h /= s; return *this; }
+
+ static Size Min(const Size& a, const Size& b) { return Size((a.w < b.w) ? a.w : b.w,
+ (a.h < b.h) ? a.h : b.h); }
+ static Size Max(const Size& a, const Size& b) { return Size((a.w > b.w) ? a.w : b.w,
+ (a.h > b.h) ? a.h : b.h); }
+
+
+ T Area() const { return w * h; }
+
+ inline Vector2<T> ToVector() const { return Vector2<T>(w, h); }
+};
+
+
+typedef Size<int> Sizei;
+typedef Size<unsigned> Sizeu;
+typedef Size<float> Sizef;
+typedef Size<double> Sized;
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Rect
+
+// Rect describes a rectangular area for rendering, that includes position and size.
+template<class T>
+class Rect
+{
+public:
+ T x, y;
+ T w, h;
+
+ Rect() { }
+ Rect(T x1, T y1, T w1, T h1) : x(x1), y(y1), w(w1), h(h1) { }
+ Rect(const Vector2<T>& pos, const Size<T>& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) { }
+ Rect(const Size<T>& sz) : x(0), y(0), w(sz.w), h(sz.h) { }
+
+ // C-interop support.
+ typedef typename CompatibleTypes<Rect<T> >::Type CompatibleType;
+
+ Rect(const CompatibleType& s) : x(s.Pos.x), y(s.Pos.y), w(s.Size.w), h(s.Size.h) { }
+
+ operator const CompatibleType& () const
+ {
+ OVR_COMPILER_ASSERT(sizeof(Rect<T>) == sizeof(CompatibleType));
+ return reinterpret_cast<const CompatibleType&>(*this);
+ }
+
+ Vector2<T> GetPos() const { return Vector2<T>(x, y); }
+ Size<T> GetSize() const { return Size<T>(w, h); }
+ void SetPos(const Vector2<T>& pos) { x = pos.x; y = pos.y; }
+ void SetSize(const Size<T>& sz) { w = sz.w; h = sz.h; }
+
+ bool operator == (const Rect& vp) const
+ { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); }
+ bool operator != (const Rect& vp) const
+ { return !operator == (vp); }
+};
+
+typedef Rect<int> Recti;
+
+
+//-------------------------------------------------------------------------------------//
+// ***** Quat
+//
+// Quatf represents a quaternion class used for rotations.
+//
+// Quaternion multiplications are done in right-to-left order, to match the
+// behavior of matrices.
+
+
+template<class T>
+class Quat
+{
+public:
+ // w + Xi + Yj + Zk
+ T x, y, z, w;
+
+ Quat() : x(0), y(0), z(0), w(1) { }
+ Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) { }
+ explicit Quat(const Quat<typename Math<T>::OtherFloatType> &src)
+ : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) { }
+
+ // C-interop support.
+ Quat(const typename CompatibleTypes<Quat<T> >::Type& s) : x(s.x), y(s.y), z(s.z), w(s.w) { }
+
+ operator typename CompatibleTypes<Quat<T> >::Type () const
+ {
+ typename CompatibleTypes<Quat<T> >::Type result;
+ result.x = x;
+ result.y = y;
+ result.z = z;
+ result.w = w;
+ return result;
+ }
+
+ // Constructs quaternion for rotation around the axis by an angle.
+ Quat(const Vector3<T>& axis, T angle)
+ {
+ // Make sure we don't divide by zero.
+ if (axis.LengthSq() == 0)
+ {
+ // Assert if the axis is zero, but the angle isn't
+ OVR_ASSERT(angle == 0);
+ x = 0; y = 0; z = 0; w = 1;
+ return;
+ }
+
+ Vector3<T> unitAxis = axis.Normalized();
+ T sinHalfAngle = sin(angle * T(0.5));
+
+ w = cos(angle * T(0.5));
+ x = unitAxis.x * sinHalfAngle;
+ y = unitAxis.y * sinHalfAngle;
+ z = unitAxis.z * sinHalfAngle;
+ }
+
+ // Constructs quaternion for rotation around one of the coordinate axis by an angle.
+ Quat(Axis A, T angle, RotateDirection d = Rotate_CCW, HandedSystem s = Handed_R)
+ {
+ T sinHalfAngle = s * d *sin(angle * T(0.5));
+ T v[3];
+ v[0] = v[1] = v[2] = T(0);
+ v[A] = sinHalfAngle;
+
+ w = cos(angle * T(0.5));
+ x = v[0];
+ y = v[1];
+ z = v[2];
+ }
+
+ // Compute axis and angle from quaternion
+ void GetAxisAngle(Vector3<T>* axis, T* angle) const
+ {
+ if ( x*x + y*y + z*z > Math<T>::Tolerance * Math<T>::Tolerance ) {
+ *axis = Vector3<T>(x, y, z).Normalized();
+ *angle = 2 * Acos(w);
+ if (*angle > Math<T>::Pi) // Reduce the magnitude of the angle, if necessary
+ {
+ *angle = Math<T>::TwoPi - *angle;
+ *axis = *axis * (-1);
+ }
+ }
+ else
+ {
+ *axis = Vector3<T>(1, 0, 0);
+ *angle= 0;
+ }
+ }
+
+ // Constructs the quaternion from a rotation matrix
+ explicit Quat(const Matrix4<T>& m)
+ {
+ T trace = m.M[0][0] + m.M[1][1] + m.M[2][2];
+
+ // In almost all cases, the first part is executed.
+ // However, if the trace is not positive, the other
+ // cases arise.
+ if (trace > T(0))
+ {
+ T s = sqrt(trace + T(1)) * T(2); // s=4*qw
+ w = T(0.25) * s;
+ x = (m.M[2][1] - m.M[1][2]) / s;
+ y = (m.M[0][2] - m.M[2][0]) / s;
+ z = (m.M[1][0] - m.M[0][1]) / s;
+ }
+ else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2]))
+ {
+ T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2);
+ w = (m.M[2][1] - m.M[1][2]) / s;
+ x = T(0.25) * s;
+ y = (m.M[0][1] + m.M[1][0]) / s;
+ z = (m.M[2][0] + m.M[0][2]) / s;
+ }
+ else if (m.M[1][1] > m.M[2][2])
+ {
+ T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy
+ w = (m.M[0][2] - m.M[2][0]) / s;
+ x = (m.M[0][1] + m.M[1][0]) / s;
+ y = T(0.25) * s;
+ z = (m.M[1][2] + m.M[2][1]) / s;
+ }
+ else
+ {
+ T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz
+ w = (m.M[1][0] - m.M[0][1]) / s;
+ x = (m.M[0][2] + m.M[2][0]) / s;
+ y = (m.M[1][2] + m.M[2][1]) / s;
+ z = T(0.25) * s;
+ }
+ }
+
+ // Constructs the quaternion from a rotation matrix
+ explicit Quat(const Matrix3<T>& m)
+ {
+ T trace = m.M[0][0] + m.M[1][1] + m.M[2][2];
+
+ // In almost all cases, the first part is executed.
+ // However, if the trace is not positive, the other
+ // cases arise.
+ if (trace > T(0))
+ {
+ T s = sqrt(trace + T(1)) * T(2); // s=4*qw
+ w = T(0.25) * s;
+ x = (m.M[2][1] - m.M[1][2]) / s;
+ y = (m.M[0][2] - m.M[2][0]) / s;
+ z = (m.M[1][0] - m.M[0][1]) / s;
+ }
+ else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2]))
+ {
+ T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2);
+ w = (m.M[2][1] - m.M[1][2]) / s;
+ x = T(0.25) * s;
+ y = (m.M[0][1] + m.M[1][0]) / s;
+ z = (m.M[2][0] + m.M[0][2]) / s;
+ }
+ else if (m.M[1][1] > m.M[2][2])
+ {
+ T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy
+ w = (m.M[0][2] - m.M[2][0]) / s;
+ x = (m.M[0][1] + m.M[1][0]) / s;
+ y = T(0.25) * s;
+ z = (m.M[1][2] + m.M[2][1]) / s;
+ }
+ else
+ {
+ T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz
+ w = (m.M[1][0] - m.M[0][1]) / s;
+ x = (m.M[0][2] + m.M[2][0]) / s;
+ y = (m.M[1][2] + m.M[2][1]) / s;
+ z = T(0.25) * s;
+ }
+ }
+
+ bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; }
+ bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; }
+
+ Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); }
+ Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; }
+ Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); }
+ Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; }
+
+ Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); }
+ Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; }
+ Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); }
+ Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; }
+
+
+ // Get Imaginary part vector
+ Vector3<T> Imag() const { return Vector3<T>(x,y,z); }
+
+ // Get quaternion length.
+ T Length() const { return sqrt(LengthSq()); }
+
+ // Get quaternion length squared.
+ T LengthSq() const { return (x * x + y * y + z * z + w * w); }
+
+ // Simple Euclidean distance in R^4 (not SLERP distance, but at least respects Haar measure)
+ T Distance(const Quat& q) const
+ {
+ T d1 = (*this - q).Length();
+ T d2 = (*this + q).Length(); // Antipodal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+
+ T DistanceSq(const Quat& q) const
+ {
+ T d1 = (*this - q).LengthSq();
+ T d2 = (*this + q).LengthSq(); // Antipodal point check
+ return (d1 < d2) ? d1 : d2;
+ }
+
+ T Dot(const Quat& q) const
+ {
+ return x * q.x + y * q.y + z * q.z + w * q.w;
+ }
+
+ // Angle between two quaternions in radians
+ T Angle(const Quat& q) const
+ {
+ return 2 * Acos(Alg::Abs(Dot(q)));
+ }
+
+ // Normalize
+ bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
+
+ void Normalize()
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ *this /= l;
+ }
+
+ Quat Normalized() const
+ {
+ T l = Length();
+ OVR_ASSERT(l != T(0));
+ return *this / l;
+ }
+
+ // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized.
+ Quat Conj() const { return Quat(-x, -y, -z, w); }
+
+ // Quaternion multiplication. Combines quaternion rotations, performing the one on the
+ // right hand side first.
+ Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y,
+ w * b.y - x * b.z + y * b.w + z * b.x,
+ w * b.z + x * b.y - y * b.x + z * b.w,
+ w * b.w - x * b.x - y * b.y - z * b.z); }
+
+ //
+ // this^p normalized; same as rotating by this p times.
+ Quat PowNormalized(T p) const
+ {
+ Vector3<T> v;
+ T a;
+ GetAxisAngle(&v, &a);
+ return Quat(v, a * p);
+ }
+
+ // Normalized linear interpolation of quaternions
+ Quat Nlerp(const Quat& other, T a)
+ {
+ T sign = (Dot(other) >= 0) ? 1 : -1;
+ return (*this * sign * a + other * (1-a)).Normalized();
+ }
+
+ // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise,
+ // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1.
+ Vector3<T> Rotate(const Vector3<T>& v) const
+ {
+ return ((*this * Quat<T>(v.x, v.y, v.z, T(0))) * Inverted()).Imag();
+ }
+
+ // Inversed quaternion rotates in the opposite direction.
+ Quat Inverted() const
+ {
+ return Quat(-x, -y, -z, w);
+ }
+
+ // Sets this quaternion to the one rotates in the opposite direction.
+ void Invert()
+ {
+ *this = Quat(-x, -y, -z, w);
+ }
+
+ // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void GetEulerAngles(T *a, T *b, T *c) const
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ T Q[3] = { x, y, z }; //Quaternion components x,y,z
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Q33 = Q[A3]*Q[A3];
+
+ T psign = T(-1);
+ // Determine whether even permutation
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3))
+ psign = T(1);
+
+ T s2 = psign * T(2) * (psign*w*Q[A2] + Q[A1]*Q[A3]);
+
+ if (s2 < T(-1) + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = T(0);
+ *b = -S*D*Math<T>::PiOver2;
+ *c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33 );
+ }
+ else if (s2 > T(1) - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = T(0);
+ *b = S*D*Math<T>::PiOver2;
+ *c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]),
+ ww + Q22 - Q11 - Q33);
+ }
+ else
+ {
+ *a = -S*D*atan2(T(-2)*(w*Q[A1] - psign*Q[A2]*Q[A3]),
+ ww + Q33 - Q11 - Q22);
+ *b = S*D*asin(s2);
+ *c = S*D*atan2(T(2)*(w*Q[A3] - psign*Q[A1]*Q[A2]),
+ ww + Q11 - Q22 - Q33);
+ }
+ return;
+ }
+
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D>
+ void GetEulerAngles(T *a, T *b, T *c) const
+ { GetEulerAngles<A1, A2, A3, D, Handed_R>(a, b, c); }
+
+ template <Axis A1, Axis A2, Axis A3>
+ void GetEulerAngles(T *a, T *b, T *c) const
+ { GetEulerAngles<A1, A2, A3, Rotate_CCW, Handed_R>(a, b, c); }
+
+
+ // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of
+ // axis rotations and the specified coordinate system. Right-handed coordinate system
+ // is the default, with CCW rotations while looking in the negative axis direction.
+ // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // Rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void GetEulerAnglesABA(T *a, T *b, T *c) const
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ T Q[3] = {x, y, z}; // Quaternion components
+
+ // Determine the missing axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ T ww = w*w;
+ T Q11 = Q[A1]*Q[A1];
+ T Q22 = Q[A2]*Q[A2];
+ T Qmm = Q[m]*Q[m];
+
+ T psign = T(-1);
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ {
+ psign = T(1);
+ }
+
+ T c2 = ww + Q11 - Q22 - Qmm;
+ if (c2 < T(-1) + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = T(0);
+ *b = S*D*Math<T>::Pi;
+ *c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else if (c2 > T(1) - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = T(0);
+ *b = T(0);
+ *c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]),
+ ww + Q22 - Q11 - Qmm);
+ }
+ else
+ {
+ *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] -psign*Q[A1]*Q[m]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2],
+ w*Q[A2] + psign*Q[A1]*Q[m]);
+ }
+ return;
+ }
+};
+
+typedef Quat<float> Quatf;
+typedef Quat<double> Quatd;
+
+//-------------------------------------------------------------------------------------
+// ***** Pose
+
+// Position and orientation combined.
+
+template<class T>
+class Transform
+{
+public:
+
+ typedef typename CompatibleTypes<Transform<T> >::Type CompatibleType;
+
+ Transform() { }
+ Transform(const Quat<T>& orientation, const Vector3<T>& pos)
+ : Rotation(orientation), Translation(pos) { }
+ Transform(const Transform& s)
+ : Rotation(s.Rotation), Translation(s.Translation) { }
+ Transform(const CompatibleType& s)
+ : Rotation(s.Orientation), Translation(s.Position) { }
+ explicit Transform(const Transform<typename Math<T>::OtherFloatType> &s)
+ : Rotation(s.Rotation), Translation(s.Translation) { }
+
+ operator typename CompatibleTypes<Transform<T> >::Type () const
+ {
+ typename CompatibleTypes<Transform<T> >::Type result;
+ result.Orientation = Rotation;
+ result.Position = Translation;
+ return result;
+ }
+
+ Quat<T> Rotation;
+ Vector3<T> Translation;
+
+ Vector3<T> Rotate(const Vector3<T>& v) const
+ {
+ return Rotation.Rotate(v);
+ }
+
+ Vector3<T> Translate(const Vector3<T>& v) const
+ {
+ return v + Translation;
+ }
+
+ Vector3<T> Apply(const Vector3<T>& v) const
+ {
+ return Translate(Rotate(v));
+ }
+
+ Transform operator*(const Transform& other) const
+ {
+ return Transform(Rotation * other.Rotation, Apply(other.Translation));
+ }
+
+ PoseState<T> operator*(const PoseState<T>& poseState) const
+ {
+ PoseState<T> result;
+ result.Pose = (*this) * poseState.Pose;
+ result.LinearVelocity = this->Rotate(poseState.LinearVelocity);
+ result.LinearAcceleration = this->Rotate(poseState.LinearAcceleration);
+ result.AngularVelocity = this->Rotate(poseState.AngularVelocity);
+ result.AngularAcceleration = this->Rotate(poseState.AngularAcceleration);
+ return result;
+ }
+
+ Transform Inverted() const
+ {
+ Quat<T> inv = Rotation.Inverted();
+ return Transform(inv, inv.Rotate(-Translation));
+ }
+};
+
+typedef Transform<float> Transformf;
+typedef Transform<double> Transformd;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix4
+//
+// Matrix4 is a 4x4 matrix used for 3d transformations and projections.
+// Translation stored in the last column.
+// The matrix is stored in row-major order in memory, meaning that values
+// of the first row are stored before the next one.
+//
+// The arrangement of the matrix is chosen to be in Right-Handed
+// coordinate system and counterclockwise rotations when looking down
+// the axis
+//
+// Transformation Order:
+// - Transformations are applied from right to left, so the expression
+// M1 * M2 * M3 * V means that the vector V is transformed by M3 first,
+// followed by M2 and M1.
+//
+// Coordinate system: Right Handed
+//
+// Rotations: Counterclockwise when looking down the axis. All angles are in radians.
+//
+// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector.
+// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector.
+// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector.
+// | 30 31 32 33 |
+//
+// The basis vectors are first three columns.
+
+template<class T>
+class Matrix4
+{
+ static const Matrix4 IdentityValue;
+
+public:
+ T M[4][4];
+
+ enum NoInitType { NoInit };
+
+ // Construct with no memory initialization.
+ Matrix4(NoInitType) { }
+
+ // By default, we construct identity matrix.
+ Matrix4()
+ {
+ SetIdentity();
+ }
+
+ Matrix4(T m11, T m12, T m13, T m14,
+ T m21, T m22, T m23, T m24,
+ T m31, T m32, T m33, T m34,
+ T m41, T m42, T m43, T m44)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34;
+ M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44;
+ }
+
+ Matrix4(T m11, T m12, T m13,
+ T m21, T m22, T m23,
+ T m31, T m32, T m33)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0;
+ M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1;
+ }
+
+ explicit Matrix4(const Quat<T>& q)
+ {
+ T ww = q.w*q.w;
+ T xx = q.x*q.x;
+ T yy = q.y*q.y;
+ T zz = q.z*q.z;
+
+ M[0][0] = ww + xx - yy - zz; M[0][1] = 2 * (q.x*q.y - q.w*q.z); M[0][2] = 2 * (q.x*q.z + q.w*q.y); M[0][3] = 0;
+ M[1][0] = 2 * (q.x*q.y + q.w*q.z); M[1][1] = ww - xx + yy - zz; M[1][2] = 2 * (q.y*q.z - q.w*q.x); M[1][3] = 0;
+ M[2][0] = 2 * (q.x*q.z - q.w*q.y); M[2][1] = 2 * (q.y*q.z + q.w*q.x); M[2][2] = ww - xx - yy + zz; M[2][3] = 0;
+ M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1;
+ }
+
+ explicit Matrix4(const Transform<T>& p)
+ {
+ Matrix4 result(p.Rotation);
+ result.SetTranslation(p.Translation);
+ *this = result;
+ }
+
+ // C-interop support
+ explicit Matrix4(const Matrix4<typename Math<T>::OtherFloatType> &src)
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ M[i][j] = (T)src.M[i][j];
+ }
+
+ // C-interop support.
+ Matrix4(const typename CompatibleTypes<Matrix4<T> >::Type& s)
+ {
+ OVR_COMPILER_ASSERT(sizeof(s) == sizeof(Matrix4));
+ memcpy(M, s.M, sizeof(M));
+ }
+
+ operator typename CompatibleTypes<Matrix4<T> >::Type () const
+ {
+ typename CompatibleTypes<Matrix4<T> >::Type result;
+ OVR_COMPILER_ASSERT(sizeof(result) == sizeof(Matrix4));
+ memcpy(result.M, M, sizeof(M));
+ return result;
+ }
+
+ void ToString(char* dest, UPInt destsize) const
+ {
+ UPInt pos = 0;
+ for (int r=0; r<4; r++)
+ for (int c=0; c<4; c++)
+ pos += OVR_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]);
+ }
+
+ static Matrix4 FromString(const char* src)
+ {
+ Matrix4 result;
+ for (int r=0; r<4; r++)
+ for (int c=0; c<4; c++)
+ {
+ result.M[r][c] = (T)atof(src);
+ while (src && *src != ' ')
+ src++;
+ while (src && *src == ' ')
+ src++;
+ }
+ return result;
+ }
+
+ static const Matrix4& Identity() { return IdentityValue; }
+
+ void SetIdentity()
+ {
+ M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1;
+ M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0;
+ M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0;
+ M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0;
+ }
+
+ bool operator== (const Matrix4& b) const
+ {
+ bool isEqual = true;
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ isEqual &= (M[i][j] == b.M[i][j]);
+
+ return isEqual;
+ }
+
+ Matrix4 operator+ (const Matrix4& b) const
+ {
+ Matrix4 result(*this);
+ result += b;
+ return result;
+ }
+
+ Matrix4& operator+= (const Matrix4& b)
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ M[i][j] += b.M[i][j];
+ return *this;
+ }
+
+ Matrix4 operator- (const Matrix4& b) const
+ {
+ Matrix4 result(*this);
+ result -= b;
+ return result;
+ }
+
+ Matrix4& operator-= (const Matrix4& b)
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ M[i][j] -= b.M[i][j];
+ return *this;
+ }
+
+ // Multiplies two matrices into destination with minimum copying.
+ static Matrix4& Multiply(Matrix4* d, const Matrix4& a, const Matrix4& b)
+ {
+ OVR_ASSERT((d != &a) && (d != &b));
+ int i = 0;
+ do {
+ d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0];
+ d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1];
+ d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2];
+ d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3];
+ } while((++i) < 4);
+
+ return *d;
+ }
+
+ Matrix4 operator* (const Matrix4& b) const
+ {
+ Matrix4 result(Matrix4::NoInit);
+ Multiply(&result, *this, b);
+ return result;
+ }
+
+ Matrix4& operator*= (const Matrix4& b)
+ {
+ return Multiply(this, Matrix4(*this), b);
+ }
+
+ Matrix4 operator* (T s) const
+ {
+ Matrix4 result(*this);
+ result *= s;
+ return result;
+ }
+
+ Matrix4& operator*= (T s)
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ M[i][j] *= s;
+ return *this;
+ }
+
+
+ Matrix4 operator/ (T s) const
+ {
+ Matrix4 result(*this);
+ result /= s;
+ return result;
+ }
+
+ Matrix4& operator/= (T s)
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ M[i][j] /= s;
+ return *this;
+ }
+
+ Vector3<T> Transform(const Vector3<T>& v) const
+ {
+ return Vector3<T>(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3],
+ M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3],
+ M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]);
+ }
+
+ Matrix4 Transposed() const
+ {
+ return Matrix4(M[0][0], M[1][0], M[2][0], M[3][0],
+ M[0][1], M[1][1], M[2][1], M[3][1],
+ M[0][2], M[1][2], M[2][2], M[3][2],
+ M[0][3], M[1][3], M[2][3], M[3][3]);
+ }
+
+ void Transpose()
+ {
+ *this = Transposed();
+ }
+
+
+ T SubDet (const UPInt* rows, const UPInt* cols) const
+ {
+ return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]])
+ - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]])
+ + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]);
+ }
+
+ T Cofactor(UPInt I, UPInt J) const
+ {
+ const UPInt indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
+ return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]);
+ }
+
+ T Determinant() const
+ {
+ return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3);
+ }
+
+ Matrix4 Adjugated() const
+ {
+ return Matrix4(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0),
+ Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1),
+ Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2),
+ Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3));
+ }
+
+ Matrix4 Inverted() const
+ {
+ T det = Determinant();
+ assert(det != 0);
+ return Adjugated() * (1.0f/det);
+ }
+
+ void Invert()
+ {
+ *this = Inverted();
+ }
+
+ // This is more efficient than general inverse, but ONLY works
+ // correctly if it is a homogeneous transform matrix (rot + trans)
+ Matrix4 InvertedHomogeneousTransform() const
+ {
+ // Make the inverse rotation matrix
+ Matrix4 rinv = this->Transposed();
+ rinv.M[3][0] = rinv.M[3][1] = rinv.M[3][2] = 0.0f;
+ // Make the inverse translation matrix
+ Vector3<T> tvinv(-M[0][3],-M[1][3],-M[2][3]);
+ Matrix4 tinv = Matrix4::Translation(tvinv);
+ return rinv * tinv; // "untranslate", then "unrotate"
+ }
+
+ // This is more efficient than general inverse, but ONLY works
+ // correctly if it is a homogeneous transform matrix (rot + trans)
+ void InvertHomogeneousTransform()
+ {
+ *this = InvertedHomogeneousTransform();
+ }
+
+ // Matrix to Euler Angles conversion
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A3
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, Axis A3, RotateDirection D, HandedSystem S>
+ void ToEulerAngles(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3));
+
+ T psign = -1;
+ if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation
+ psign = 1;
+
+ T pm = psign*M[A1][A3];
+ if (pm < -1.0f + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = 0;
+ *b = -S*D*Math<T>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else if (pm > 1.0f - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = 0;
+ *b = S*D*Math<T>::PiOver2;
+ *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] );
+ *b = S*D*asin(pm);
+ *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] );
+ }
+
+ return;
+ }
+
+ // Matrix to Euler Angles conversion
+ // a,b,c, are the YawPitchRoll angles to be returned
+ // rotation a around axis A1
+ // is followed by rotation b around axis A2
+ // is followed by rotation c around axis A1
+ // rotations are CCW or CW (D) in LH or RH coordinate system (S)
+ template <Axis A1, Axis A2, RotateDirection D, HandedSystem S>
+ void ToEulerAnglesABA(T *a, T *b, T *c)
+ {
+ OVR_COMPILER_ASSERT(A1 != A2);
+
+ // Determine the axis that was not supplied
+ int m = 3 - A1 - A2;
+
+ T psign = -1;
+ if ((A1 + 1) % 3 == A2) // Determine whether even permutation
+ psign = 1.0f;
+
+ T c2 = M[A1][A1];
+ if (c2 < -1 + Math<T>::SingularityRadius)
+ { // South pole singularity
+ *a = 0;
+ *b = S*D*Math<T>::Pi;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else if (c2 > 1.0f - Math<T>::SingularityRadius)
+ { // North pole singularity
+ *a = 0;
+ *b = 0;
+ *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
+ }
+ else
+ { // Normal case (nonsingular)
+ *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]);
+ *b = S*D*acos(c2);
+ *c = S*D*atan2( M[A1][A2],psign*M[A1][m]);
+ }
+ return;
+ }
+
+ // Creates a matrix that converts the vertices from one coordinate system
+ // to another.
+ static Matrix4 AxisConversion(const WorldAxes& to, const WorldAxes& from)
+ {
+ // Holds axis values from the 'to' structure
+ int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis };
+
+ // The inverse of the toArray
+ int inv[4];
+ inv[0] = inv[abs(to.XAxis)] = 0;
+ inv[abs(to.YAxis)] = 1;
+ inv[abs(to.ZAxis)] = 2;
+
+ Matrix4 m(0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0);
+
+ // Only three values in the matrix need to be changed to 1 or -1.
+ m.M[inv[abs(from.XAxis)]][0] = T(from.XAxis/toArray[inv[abs(from.XAxis)]]);
+ m.M[inv[abs(from.YAxis)]][1] = T(from.YAxis/toArray[inv[abs(from.YAxis)]]);
+ m.M[inv[abs(from.ZAxis)]][2] = T(from.ZAxis/toArray[inv[abs(from.ZAxis)]]);
+ return m;
+ }
+
+
+ // Creates a matrix for translation by vector
+ static Matrix4 Translation(const Vector3<T>& v)
+ {
+ Matrix4 t;
+ t.M[0][3] = v.x;
+ t.M[1][3] = v.y;
+ t.M[2][3] = v.z;
+ return t;
+ }
+
+ // Creates a matrix for translation by vector
+ static Matrix4 Translation(T x, T y, T z = 0.0f)
+ {
+ Matrix4 t;
+ t.M[0][3] = x;
+ t.M[1][3] = y;
+ t.M[2][3] = z;
+ return t;
+ }
+
+ // Sets the translation part
+ void SetTranslation(const Vector3<T>& v)
+ {
+ M[0][3] = v.x;
+ M[1][3] = v.y;
+ M[2][3] = v.z;
+ }
+
+ Vector3<T> GetTranslation() const
+ {
+ return Vector3<T>( M[0][3], M[1][3], M[2][3] );
+ }
+
+ // Creates a matrix for scaling by vector
+ static Matrix4 Scaling(const Vector3<T>& v)
+ {
+ Matrix4 t;
+ t.M[0][0] = v.x;
+ t.M[1][1] = v.y;
+ t.M[2][2] = v.z;
+ return t;
+ }
+
+ // Creates a matrix for scaling by vector
+ static Matrix4 Scaling(T x, T y, T z)
+ {
+ Matrix4 t;
+ t.M[0][0] = x;
+ t.M[1][1] = y;
+ t.M[2][2] = z;
+ return t;
+ }
+
+ // Creates a matrix for scaling by constant
+ static Matrix4 Scaling(T s)
+ {
+ Matrix4 t;
+ t.M[0][0] = s;
+ t.M[1][1] = s;
+ t.M[2][2] = s;
+ return t;
+ }
+
+ // Simple L1 distance in R^12
+ T Distance(const Matrix4& m2) const
+ {
+ T d = fabs(M[0][0] - m2.M[0][0]) + fabs(M[0][1] - m2.M[0][1]);
+ d += fabs(M[0][2] - m2.M[0][2]) + fabs(M[0][3] - m2.M[0][3]);
+ d += fabs(M[1][0] - m2.M[1][0]) + fabs(M[1][1] - m2.M[1][1]);
+ d += fabs(M[1][2] - m2.M[1][2]) + fabs(M[1][3] - m2.M[1][3]);
+ d += fabs(M[2][0] - m2.M[2][0]) + fabs(M[2][1] - m2.M[2][1]);
+ d += fabs(M[2][2] - m2.M[2][2]) + fabs(M[2][3] - m2.M[2][3]);
+ d += fabs(M[3][0] - m2.M[3][0]) + fabs(M[3][1] - m2.M[3][1]);
+ d += fabs(M[3][2] - m2.M[3][2]) + fabs(M[3][3] - m2.M[3][3]);
+ return d;
+ }
+
+ // Creates a rotation matrix rotating around the X axis by 'angle' radians.
+ // Just for quick testing. Not for final API. Need to remove case.
+ static Matrix4 RotationAxis(Axis A, T angle, RotateDirection d, HandedSystem s)
+ {
+ T sina = s * d *sin(angle);
+ T cosa = cos(angle);
+
+ switch(A)
+ {
+ case Axis_X:
+ return Matrix4(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ case Axis_Y:
+ return Matrix4(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ case Axis_Z:
+ return Matrix4(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+ }
+
+
+ // Creates a rotation matrix rotating around the X axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4 RotationX(T angle)
+ {
+ T sina = sin(angle);
+ T cosa = cos(angle);
+ return Matrix4(1, 0, 0,
+ 0, cosa, -sina,
+ 0, sina, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Y axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4 RotationY(T angle)
+ {
+ T sina = sin(angle);
+ T cosa = cos(angle);
+ return Matrix4(cosa, 0, sina,
+ 0, 1, 0,
+ -sina, 0, cosa);
+ }
+
+ // Creates a rotation matrix rotating around the Z axis by 'angle' radians.
+ // Rotation direction is depends on the coordinate system:
+ // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
+ // while looking in the negative axis direction. This is the
+ // same as looking down from positive axis values towards origin.
+ // LHS: Positive angle values rotate clock-wise (CW), while looking in the
+ // negative axis direction.
+ static Matrix4 RotationZ(T angle)
+ {
+ T sina = sin(angle);
+ T cosa = cos(angle);
+ return Matrix4(cosa, -sina, 0,
+ sina, cosa, 0,
+ 0, 0, 1);
+ }
+
+ // LookAtRH creates a View transformation matrix for right-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector. The resulting matrix should be used with PerspectiveRH
+ // projection.
+ static Matrix4 LookAtRH(const Vector3<T>& eye, const Vector3<T>& at, const Vector3<T>& up)
+ {
+ Vector3<T> z = (eye - at).Normalized(); // Forward
+ Vector3<T> x = up.Cross(z).Normalized(); // Right
+ Vector3<T> y = z.Cross(x);
+
+ Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)),
+ y.x, y.y, y.z, -(y.Dot(eye)),
+ z.x, z.y, z.z, -(z.Dot(eye)),
+ 0, 0, 0, 1 );
+ return m;
+ }
+
+ // LookAtLH creates a View transformation matrix for left-handed coordinate system.
+ // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up'
+ // specifying the up vector.
+ static Matrix4 LookAtLH(const Vector3<T>& eye, const Vector3<T>& at, const Vector3<T>& up)
+ {
+ Vector3<T> z = (at - eye).Normalized(); // Forward
+ Vector3<T> x = up.Cross(z).Normalized(); // Right
+ Vector3<T> y = z.Cross(x);
+
+ Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)),
+ y.x, y.y, y.z, -(y.Dot(eye)),
+ z.x, z.y, z.z, -(z.Dot(eye)),
+ 0, 0, 0, 1 );
+ return m;
+ }
+
+ // PerspectiveRH creates a right-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ // Even though RHS usually looks in the direction of negative Z, positive values
+ // are expected for znear and zfar.
+ static Matrix4 PerspectiveRH(T yfov, T aspect, T znear, T zfar)
+ {
+ Matrix4 m;
+ T tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1 / (aspect * tanHalfFov);
+ m.M[1][1] = 1 / tanHalfFov;
+ m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = 1;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ return m;
+ }
+
+ // PerspectiveRH creates a left-handed perspective projection matrix that can be
+ // used with the Oculus sample renderer.
+ // yfov - Specifies vertical field of view in radians.
+ // aspect - Screen aspect ration, which is usually width/height for square pixels.
+ // Note that xfov = yfov * aspect.
+ // znear - Absolute value of near Z clipping clipping range.
+ // zfar - Absolute value of far Z clipping clipping range (larger then near).
+ static Matrix4 PerspectiveLH(T yfov, T aspect, T znear, T zfar)
+ {
+ Matrix4 m;
+ T tanHalfFov = tan(yfov * 0.5f);
+
+ m.M[0][0] = 1.0 / (aspect * tanHalfFov);
+ m.M[1][1] = 1.0 / tanHalfFov;
+ m.M[2][2] = zfar / (znear - zfar);
+ // m.M[2][2] = zfar / (zfar - znear);
+ m.M[3][2] = -1.0;
+ m.M[2][3] = (zfar * znear) / (znear - zfar);
+ m.M[3][3] = 0.0;
+
+ // Note: Post-projection matrix result assumes Left-Handed coordinate system,
+ // with Y up, X right and Z forward. This supports positive z-buffer values.
+ // This is the case even for RHS cooridnate input.
+ return m;
+ }
+
+ static Matrix4 Ortho2D(T w, T h)
+ {
+ Matrix4 m;
+ m.M[0][0] = 2.0/w;
+ m.M[1][1] = -2.0/h;
+ m.M[0][3] = -1.0;
+ m.M[1][3] = 1.0;
+ m.M[2][2] = 0;
+ return m;
+ }
+};
+
+typedef Matrix4<float> Matrix4f;
+typedef Matrix4<double> Matrix4d;
+
+//-------------------------------------------------------------------------------------
+// ***** Matrix3
+//
+// Matrix3 is a 3x3 matrix used for representing a rotation matrix.
+// The matrix is stored in row-major order in memory, meaning that values
+// of the first row are stored before the next one.
+//
+// The arrangement of the matrix is chosen to be in Right-Handed
+// coordinate system and counterclockwise rotations when looking down
+// the axis
+//
+// Transformation Order:
+// - Transformations are applied from right to left, so the expression
+// M1 * M2 * M3 * V means that the vector V is transformed by M3 first,
+// followed by M2 and M1.
+//
+// Coordinate system: Right Handed
+//
+// Rotations: Counterclockwise when looking down the axis. All angles are in radians.
+
+template<typename T>
+class SymMat3;
+
+template<class T>
+class Matrix3
+{
+ static const Matrix3 IdentityValue;
+
+public:
+ T M[3][3];
+
+ enum NoInitType { NoInit };
+
+ // Construct with no memory initialization.
+ Matrix3(NoInitType) { }
+
+ // By default, we construct identity matrix.
+ Matrix3()
+ {
+ SetIdentity();
+ }
+
+ Matrix3(T m11, T m12, T m13,
+ T m21, T m22, T m23,
+ T m31, T m32, T m33)
+ {
+ M[0][0] = m11; M[0][1] = m12; M[0][2] = m13;
+ M[1][0] = m21; M[1][1] = m22; M[1][2] = m23;
+ M[2][0] = m31; M[2][1] = m32; M[2][2] = m33;
+ }
+
+ /*
+ explicit Matrix3(const Quat<T>& q)
+ {
+ T ww = q.w*q.w;
+ T xx = q.x*q.x;
+ T yy = q.y*q.y;
+ T zz = q.z*q.z;
+
+ M[0][0] = ww + xx - yy - zz; M[0][1] = 2 * (q.x*q.y - q.w*q.z); M[0][2] = 2 * (q.x*q.z + q.w*q.y);
+ M[1][0] = 2 * (q.x*q.y + q.w*q.z); M[1][1] = ww - xx + yy - zz; M[1][2] = 2 * (q.y*q.z - q.w*q.x);
+ M[2][0] = 2 * (q.x*q.z - q.w*q.y); M[2][1] = 2 * (q.y*q.z + q.w*q.x); M[2][2] = ww - xx - yy + zz;
+ }
+ */
+
+ explicit Matrix3(const Quat<T>& q)
+ {
+ const T tx = q.x+q.x, ty = q.y+q.y, tz = q.z+q.z;
+ const T twx = q.w*tx, twy = q.w*ty, twz = q.w*tz;
+ const T txx = q.x*tx, txy = q.x*ty, txz = q.x*tz;
+ const T tyy = q.y*ty, tyz = q.y*tz, tzz = q.z*tz;
+ M[0][0] = T(1) - (tyy + tzz); M[0][1] = txy - twz; M[0][2] = txz + twy;
+ M[1][0] = txy + twz; M[1][1] = T(1) - (txx + tzz); M[1][2] = tyz - twx;
+ M[2][0] = txz - twy; M[2][1] = tyz + twx; M[2][2] = T(1) - (txx + tyy);
+ }
+
+ inline explicit Matrix3(T s)
+ {
+ M[0][0] = M[1][1] = M[2][2] = s;
+ M[0][1] = M[0][2] = M[1][0] = M[1][2] = M[2][0] = M[2][1] = 0;
+ }
+
+ explicit Matrix3(const Transform<T>& p)
+ {
+ Matrix3 result(p.Rotation);
+ result.SetTranslation(p.Translation);
+ *this = result;
+ }
+
+ // C-interop support
+ explicit Matrix3(const Matrix4<typename Math<T>::OtherFloatType> &src)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] = (T)src.M[i][j];
+ }
+
+ // C-interop support.
+ Matrix3(const typename CompatibleTypes<Matrix3<T> >::Type& s)
+ {
+ OVR_COMPILER_ASSERT(sizeof(s) == sizeof(Matrix3));
+ memcpy(M, s.M, sizeof(M));
+ }
+
+ operator typename CompatibleTypes<Matrix3<T> >::Type () const
+ {
+ typename CompatibleTypes<Matrix3<T> >::Type result;
+ OVR_COMPILER_ASSERT(sizeof(result) == sizeof(Matrix3));
+ memcpy(result.M, M, sizeof(M));
+ return result;
+ }
+
+ void ToString(char* dest, UPInt destsize) const
+ {
+ UPInt pos = 0;
+ for (int r=0; r<3; r++)
+ for (int c=0; c<3; c++)
+ pos += OVR_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]);
+ }
+
+ static Matrix3 FromString(const char* src)
+ {
+ Matrix3 result;
+ for (int r=0; r<3; r++)
+ for (int c=0; c<3; c++)
+ {
+ result.M[r][c] = (T)atof(src);
+ while (src && *src != ' ')
+ src++;
+ while (src && *src == ' ')
+ src++;
+ }
+ return result;
+ }
+
+ static const Matrix3& Identity() { return IdentityValue; }
+
+ void SetIdentity()
+ {
+ M[0][0] = M[1][1] = M[2][2] = 1;
+ M[0][1] = M[1][0] = M[2][0] = 0;
+ M[0][2] = M[1][2] = M[2][1] = 0;
+ }
+
+ bool operator== (const Matrix3& b) const
+ {
+ bool isEqual = true;
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ isEqual &= (M[i][j] == b.M[i][j]);
+
+ return isEqual;
+ }
+
+ Matrix3 operator+ (const Matrix3& b) const
+ {
+ Matrix4<T> result(*this);
+ result += b;
+ return result;
+ }
+
+ Matrix3& operator+= (const Matrix3& b)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] += b.M[i][j];
+ return *this;
+ }
+
+ void operator= (const Matrix3& b)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] = b.M[i][j];
+ return;
+ }
+
+ void operator= (const SymMat3<T>& b)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] = 0;
+
+ M[0][0] = b.v[0];
+ M[0][1] = b.v[1];
+ M[0][2] = b.v[2];
+ M[1][1] = b.v[3];
+ M[1][2] = b.v[4];
+ M[2][2] = b.v[5];
+
+ return;
+ }
+
+ Matrix3 operator- (const Matrix3& b) const
+ {
+ Matrix3 result(*this);
+ result -= b;
+ return result;
+ }
+
+ Matrix3& operator-= (const Matrix3& b)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] -= b.M[i][j];
+ return *this;
+ }
+
+ // Multiplies two matrices into destination with minimum copying.
+ static Matrix3& Multiply(Matrix3* d, const Matrix3& a, const Matrix3& b)
+ {
+ OVR_ASSERT((d != &a) && (d != &b));
+ int i = 0;
+ do {
+ d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0];
+ d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1];
+ d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2];
+ } while((++i) < 3);
+
+ return *d;
+ }
+
+ Matrix3 operator* (const Matrix3& b) const
+ {
+ Matrix3 result(Matrix3::NoInit);
+ Multiply(&result, *this, b);
+ return result;
+ }
+
+ Matrix3& operator*= (const Matrix3& b)
+ {
+ return Multiply(this, Matrix3(*this), b);
+ }
+
+ Matrix3 operator* (T s) const
+ {
+ Matrix3 result(*this);
+ result *= s;
+ return result;
+ }
+
+ Matrix3& operator*= (T s)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] *= s;
+ return *this;
+ }
+
+ Vector3<T> operator* (const Vector3<T> &b) const
+ {
+ Vector3<T> result;
+ result.x = M[0][0]*b.x + M[0][1]*b.y + M[0][2]*b.z;
+ result.y = M[1][0]*b.x + M[1][1]*b.y + M[1][2]*b.z;
+ result.z = M[2][0]*b.x + M[2][1]*b.y + M[2][2]*b.z;
+
+ return result;
+ }
+
+ Matrix3 operator/ (T s) const
+ {
+ Matrix3 result(*this);
+ result /= s;
+ return result;
+ }
+
+ Matrix3& operator/= (T s)
+ {
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ M[i][j] /= s;
+ return *this;
+ }
+
+ Vector3<T> Transform(const Vector3<T>& v) const
+ {
+ return Vector3<T>(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z,
+ M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z,
+ M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z);
+ }
+
+ Matrix3 Transposed() const
+ {
+ return Matrix3(M[0][0], M[1][0], M[2][0],
+ M[0][1], M[1][1], M[2][1],
+ M[0][2], M[1][2], M[2][2]);
+ }
+
+ void Transpose()
+ {
+ *this = Transposed();
+ }
+
+
+ T SubDet (const UPInt* rows, const UPInt* cols) const
+ {
+ return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]])
+ - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]])
+ + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]);
+ }
+
+ // M += a*b.t()
+ inline void Rank1Add(const Vector3<T> &a, const Vector3<T> &b)
+ {
+ M[0][0] += a.x*b.x; M[0][1] += a.x*b.y; M[0][2] += a.x*b.z;
+ M[1][0] += a.y*b.x; M[1][1] += a.y*b.y; M[1][2] += a.y*b.z;
+ M[2][0] += a.z*b.x; M[2][1] += a.z*b.y; M[2][2] += a.z*b.z;
+ }
+
+ // M -= a*b.t()
+ inline void Rank1Sub(const Vector3<T> &a, const Vector3<T> &b)
+ {
+ M[0][0] -= a.x*b.x; M[0][1] -= a.x*b.y; M[0][2] -= a.x*b.z;
+ M[1][0] -= a.y*b.x; M[1][1] -= a.y*b.y; M[1][2] -= a.y*b.z;
+ M[2][0] -= a.z*b.x; M[2][1] -= a.z*b.y; M[2][2] -= a.z*b.z;
+ }
+
+ inline Vector3<T> Col(int c) const
+ {
+ return Vector3<T>(M[0][c], M[1][c], M[2][c]);
+ }
+
+ inline Vector3<T> Row(int r) const
+ {
+ return Vector3<T>(M[r][0], M[r][1], M[r][2]);
+ }
+
+ inline T Determinant() const
+ {
+ const Matrix3<T>& m = *this;
+ T d;
+
+ d = m.M[0][0] * (m.M[1][1]*m.M[2][2] - m.M[1][2] * m.M[2][1]);
+ d -= m.M[0][1] * (m.M[1][0]*m.M[2][2] - m.M[1][2] * m.M[2][0]);
+ d += m.M[0][2] * (m.M[1][0]*m.M[2][1] - m.M[1][1] * m.M[2][0]);
+
+ return d;
+ }
+
+ inline Matrix3<T> Inverse() const
+ {
+ Matrix3<T> a;
+ const Matrix3<T>& m = *this;
+ T d = Determinant();
+
+ assert(d != 0);
+ T s = T(1)/d;
+
+ a.M[0][0] = s * (m.M[1][1] * m.M[2][2] - m.M[1][2] * m.M[2][1]);
+ a.M[1][0] = s * (m.M[1][2] * m.M[2][0] - m.M[1][0] * m.M[2][2]);
+ a.M[2][0] = s * (m.M[1][0] * m.M[2][1] - m.M[1][1] * m.M[2][0]);
+
+ a.M[0][1] = s * (m.M[0][2] * m.M[2][1] - m.M[0][1] * m.M[2][2]);
+ a.M[1][1] = s * (m.M[0][0] * m.M[2][2] - m.M[0][2] * m.M[2][0]);
+ a.M[2][1] = s * (m.M[0][1] * m.M[2][0] - m.M[0][0] * m.M[2][1]);
+
+ a.M[0][2] = s * (m.M[0][1] * m.M[1][2] - m.M[0][2] * m.M[1][1]);
+ a.M[1][2] = s * (m.M[0][2] * m.M[1][0] - m.M[0][0] * m.M[1][2]);
+ a.M[2][2] = s * (m.M[0][0] * m.M[1][1] - m.M[0][1] * m.M[1][0]);
+
+ return a;
+ }
+
+};
+
+typedef Matrix3<float> Matrix3f;
+typedef Matrix3<double> Matrix3d;
+
+//-------------------------------------------------------------------------------------
+
+template<typename T>
+class SymMat3
+{
+private:
+ typedef SymMat3<T> this_type;
+
+public:
+ typedef T Value_t;
+ // Upper symmetric
+ T v[6]; // _00 _01 _02 _11 _12 _22
+
+ inline SymMat3() {}
+
+ inline explicit SymMat3(T s)
+ {
+ v[0] = v[3] = v[5] = s;
+ v[1] = v[2] = v[4] = 0;
+ }
+
+ inline explicit SymMat3(T a00, T a01, T a02, T a11, T a12, T a22)
+ {
+ v[0] = a00; v[1] = a01; v[2] = a02;
+ v[3] = a11; v[4] = a12;
+ v[5] = a22;
+ }
+
+ static inline int Index(unsigned int i, unsigned int j)
+ {
+ return (i <= j) ? (3*i - i*(i+1)/2 + j) : (3*j - j*(j+1)/2 + i);
+ }
+
+ inline T operator()(int i, int j) const { return v[Index(i,j)]; }
+
+ inline T &operator()(int i, int j) { return v[Index(i,j)]; }
+
+ template<typename U>
+ inline SymMat3<U> CastTo() const
+ {
+ return SymMat3<U>(static_cast<U>(v[0]), static_cast<U>(v[1]), static_cast<U>(v[2]),
+ static_cast<U>(v[3]), static_cast<U>(v[4]), static_cast<U>(v[5]));
+ }
+
+ inline this_type& operator+=(const this_type& b)
+ {
+ v[0]+=b.v[0];
+ v[1]+=b.v[1];
+ v[2]+=b.v[2];
+ v[3]+=b.v[3];
+ v[4]+=b.v[4];
+ v[5]+=b.v[5];
+ return *this;
+ }
+
+ inline this_type& operator-=(const this_type& b)
+ {
+ v[0]-=b.v[0];
+ v[1]-=b.v[1];
+ v[2]-=b.v[2];
+ v[3]-=b.v[3];
+ v[4]-=b.v[4];
+ v[5]-=b.v[5];
+
+ return *this;
+ }
+
+ inline this_type& operator*=(T s)
+ {
+ v[0]*=s;
+ v[1]*=s;
+ v[2]*=s;
+ v[3]*=s;
+ v[4]*=s;
+ v[5]*=s;
+
+ return *this;
+ }
+
+ inline SymMat3 operator*(T s) const
+ {
+ SymMat3 d;
+ d.v[0] = v[0]*s;
+ d.v[1] = v[1]*s;
+ d.v[2] = v[2]*s;
+ d.v[3] = v[3]*s;
+ d.v[4] = v[4]*s;
+ d.v[5] = v[5]*s;
+
+ return d;
+ }
+
+ // Multiplies two matrices into destination with minimum copying.
+ static SymMat3& Multiply(SymMat3* d, const SymMat3& a, const SymMat3& b)
+ {
+ // _00 _01 _02 _11 _12 _22
+
+ d->v[0] = a.v[0] * b.v[0];
+ d->v[1] = a.v[0] * b.v[1] + a.v[1] * b.v[3];
+ d->v[2] = a.v[0] * b.v[2] + a.v[1] * b.v[4];
+
+ d->v[3] = a.v[3] * b.v[3];
+ d->v[4] = a.v[3] * b.v[4] + a.v[4] * b.v[5];
+
+ d->v[5] = a.v[5] * b.v[5];
+
+ return *d;
+ }
+
+ inline T Determinant() const
+ {
+ const this_type& m = *this;
+ T d;
+
+ d = m(0,0) * (m(1,1)*m(2,2) - m(1,2) * m(2,1));
+ d -= m(0,1) * (m(1,0)*m(2,2) - m(1,2) * m(2,0));
+ d += m(0,2) * (m(1,0)*m(2,1) - m(1,1) * m(2,0));
+
+ return d;
+ }
+
+ inline this_type Inverse() const
+ {
+ this_type a;
+ const this_type& m = *this;
+ T d = Determinant();
+
+ assert(d != 0);
+ T s = T(1)/d;
+
+ a(0,0) = s * (m(1,1) * m(2,2) - m(1,2) * m(2,1));
+
+ a(0,1) = s * (m(0,2) * m(2,1) - m(0,1) * m(2,2));
+ a(1,1) = s * (m(0,0) * m(2,2) - m(0,2) * m(2,0));
+
+ a(0,2) = s * (m(0,1) * m(1,2) - m(0,2) * m(1,1));
+ a(1,2) = s * (m(0,2) * m(1,0) - m(0,0) * m(1,2));
+ a(2,2) = s * (m(0,0) * m(1,1) - m(0,1) * m(1,0));
+
+ return a;
+ }
+
+ inline T Trace() const { return v[0] + v[3] + v[5]; }
+
+ // M = a*a.t()
+ inline void Rank1(const Vector3<T> &a)
+ {
+ v[0] = a.x*a.x; v[1] = a.x*a.y; v[2] = a.x*a.z;
+ v[3] = a.y*a.y; v[4] = a.y*a.z;
+ v[5] = a.z*a.z;
+ }
+
+ // M += a*a.t()
+ inline void Rank1Add(const Vector3<T> &a)
+ {
+ v[0] += a.x*a.x; v[1] += a.x*a.y; v[2] += a.x*a.z;
+ v[3] += a.y*a.y; v[4] += a.y*a.z;
+ v[5] += a.z*a.z;
+ }
+
+ // M -= a*a.t()
+ inline void Rank1Sub(const Vector3<T> &a)
+ {
+ v[0] -= a.x*a.x; v[1] -= a.x*a.y; v[2] -= a.x*a.z;
+ v[3] -= a.y*a.y; v[4] -= a.y*a.z;
+ v[5] -= a.z*a.z;
+ }
+};
+
+typedef SymMat3<float> SymMat3f;
+typedef SymMat3<double> SymMat3d;
+
+template<typename T>
+inline Matrix3<T> operator*(const SymMat3<T>& a, const SymMat3<T>& b)
+{
+ #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c))
+ return Matrix3<T>(
+ AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2),
+ AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2),
+ AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2));
+ #undef AJB_ARBC
+}
+
+template<typename T>
+inline Matrix3<T> operator*(const Matrix3<T>& a, const SymMat3<T>& b)
+{
+ #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c))
+ return Matrix3<T>(
+ AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2),
+ AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2),
+ AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2));
+ #undef AJB_ARBC
+}
+
+//-------------------------------------------------------------------------------------
+// ***** Angle
+
+// Cleanly representing the algebra of 2D rotations.
+// The operations maintain the angle between -Pi and Pi, the same range as atan2.
+
+template<class T>
+class Angle
+{
+public:
+ enum AngularUnits
+ {
+ Radians = 0,
+ Degrees = 1
+ };
+
+ Angle() : a(0) {}
+
+ // Fix the range to be between -Pi and Pi
+ Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math<T>::DegreeToRadFactor) { FixRange(); }
+
+ T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math<T>::RadToDegreeFactor; }
+ void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math<T>::DegreeToRadFactor; FixRange(); }
+ int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; }
+ T Abs() const { return (a > 0) ? a : -a; }
+
+ bool operator== (const Angle& b) const { return a == b.a; }
+ bool operator!= (const Angle& b) const { return a != b.a; }
+// bool operator< (const Angle& b) const { return a < a.b; }
+// bool operator> (const Angle& b) const { return a > a.b; }
+// bool operator<= (const Angle& b) const { return a <= a.b; }
+// bool operator>= (const Angle& b) const { return a >= a.b; }
+// bool operator= (const T& x) { a = x; FixRange(); }
+
+ // These operations assume a is already between -Pi and Pi.
+ Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; }
+ Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; }
+ Angle operator+ (const Angle& b) const { Angle res = *this; res += b; return res; }
+ Angle operator+ (const T& x) const { Angle res = *this; res += x; return res; }
+ Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; }
+ Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; }
+ Angle operator- (const Angle& b) const { Angle res = *this; res -= b; return res; }
+ Angle operator- (const T& x) const { Angle res = *this; res -= x; return res; }
+
+ T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math<T>::Pi) ? c : Math<T>::TwoPi - c; }
+
+private:
+
+ // The stored angle, which should be maintained between -Pi and Pi
+ T a;
+
+ // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side
+ inline void FastFixRange()
+ {
+ if (a < -Math<T>::Pi)
+ a += Math<T>::TwoPi;
+ else if (a > Math<T>::Pi)
+ a -= Math<T>::TwoPi;
+ }
+
+ // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method
+ inline void FixRange()
+ {
+ // do nothing if the value is already in the correct range, since fmod call is expensive
+ if (a >= -Math<T>::Pi && a <= Math<T>::Pi)
+ return;
+ a = fmod(a,Math<T>::TwoPi);
+ if (a < -Math<T>::Pi)
+ a += Math<T>::TwoPi;
+ else if (a > Math<T>::Pi)
+ a -= Math<T>::TwoPi;
+ }
+};
+
+
+typedef Angle<float> Anglef;
+typedef Angle<double> Angled;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Plane
+
+// Consists of a normal vector and distance from the origin where the plane is located.
+
+template<class T>
+class Plane : public RefCountBase<Plane<T> >
+{
+public:
+ Vector3<T> N;
+ T D;
+
+ Plane() : D(0) {}
+
+ // Normals must already be normalized
+ Plane(const Vector3<T>& n, T d) : N(n), D(d) {}
+ Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {}
+
+ // construct from a point on the plane and the normal
+ Plane(const Vector3<T>& p, const Vector3<T>& n) : N(n), D(-(p * n)) {}
+
+ // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane).
+ T TestSide(const Vector3<T>& p) const
+ {
+ return (N.Dot(p)) + D;
+ }
+
+ Plane<T> Flipped() const
+ {
+ return Plane(-N, -D);
+ }
+
+ void Flip()
+ {
+ N = -N;
+ D = -D;
+ }
+
+ bool operator==(const Plane<T>& rhs) const
+ {
+ return (this->D == rhs.D && this->N == rhs.N);
+ }
+};
+
+typedef Plane<float> Planef;
+
+} // Namespace OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.cpp b/LibOVR/Src/Kernel/OVR_RefCount.cpp
new file mode 100644
index 0000000..c6301ed
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_RefCount.cpp
@@ -0,0 +1,111 @@
+/************************************************************************************
+
+Filename : OVR_RefCount.cpp
+Content : Reference counting implementation
+Created : September 19, 2012
+Notes :
+
+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_RefCount.h"
+#include "OVR_Atomic.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+#ifdef OVR_CC_ARM
+void* ReturnArg0(void* p)
+{
+ return p;
+}
+#endif
+
+// ***** Reference Count Base implementation
+
+RefCountImplCore::~RefCountImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+RefCountNTSImplCore::~RefCountNTSImplCore()
+{
+ // RefCount can be either 1 or 0 here.
+ // 0 if Release() was properly called.
+ // 1 if the object was declared on stack or as an aggregate.
+ OVR_ASSERT(RefCount <= 1);
+}
+
+#ifdef OVR_BUILD_DEBUG
+void RefCountNTSImplCore::reportInvalidDelete(void *pmem)
+{
+ OVR_DEBUG_LOG(
+ ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem));
+ OVR_ASSERT(0);
+}
+#endif
+
+
+// *** Thread-Safe RefCountImpl
+
+void RefCountImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** Thread-Safe RefCountVImpl w/virtual AddRef/Release
+
+void RefCountVImpl::AddRef()
+{
+ AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, 1);
+}
+void RefCountVImpl::Release()
+{
+ if ((AtomicOps<int>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ delete this;
+}
+
+// *** NON-Thread-Safe RefCountImpl
+
+void RefCountNTSImpl::Release() const
+{
+ RefCount--;
+ if (RefCount == 0)
+ delete this;
+}
+
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_RefCount.h b/LibOVR/Src/Kernel/OVR_RefCount.h
new file mode 100644
index 0000000..775e24c
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_RefCount.h
@@ -0,0 +1,564 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_RefCount.h
+Content : Reference counting implementation headers
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_RefCount_h
+#define OVR_RefCount_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Reference Counting
+
+// There are three types of reference counting base classes:
+//
+// RefCountBase - Provides thread-safe reference counting (Default).
+// RefCountBaseNTS - Non Thread Safe version of reference counting.
+
+
+// ***** Declared classes
+
+template<class C>
+class RefCountBase;
+template<class C>
+class RefCountBaseNTS;
+
+class RefCountImpl;
+class RefCountNTSImpl;
+
+
+//-----------------------------------------------------------------------------------
+// ***** Implementation For Reference Counting
+
+// RefCountImplCore holds RefCount value and defines a few utility
+// functions shared by all implementations.
+
+class RefCountImplCore
+{
+protected:
+ volatile int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ inline static void checkInvalidDelete(RefCountImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ inline static void checkInvalidDelete(RefCountImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountImplCore &) { }
+};
+
+class RefCountNTSImplCore
+{
+protected:
+ mutable int RefCount;
+
+public:
+ // RefCountImpl constructor always initializes RefCount to 1 by default.
+ OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { }
+
+ // Need virtual destructor
+ // This: 1. Makes sure the right destructor's called.
+ // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem()
+ virtual ~RefCountNTSImplCore();
+
+ // Debug method only.
+ int GetRefCount() const { return RefCount; }
+
+ // This logic is used to detect invalid 'delete' calls of reference counted
+ // objects. Direct delete calls are not allowed on them unless they come in
+ // internally from Release.
+#ifdef OVR_BUILD_DEBUG
+ static void OVR_CDECL reportInvalidDelete(void *pmem);
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem)
+ {
+ if (pmem->RefCount != 0)
+ reportInvalidDelete(pmem);
+ }
+#else
+ OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { }
+#endif
+
+ // Base class ref-count content should not be copied.
+ void operator = (const RefCountNTSImplCore &) { }
+};
+
+
+
+// RefCountImpl provides Thread-Safe implementation of reference counting, so
+// it should be used by default in most places.
+
+class RefCountImpl : public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ void AddRef();
+ void Release();
+};
+
+// RefCountVImpl provides Thread-Safe implementation of reference counting, plus,
+// virtual AddRef and Release.
+
+class RefCountVImpl : virtual public RefCountImplCore
+{
+public:
+ // Thread-Safe Ref-Count Implementation.
+ virtual void AddRef();
+ virtual void Release();
+};
+
+
+// RefCountImplNTS provides Non-Thread-Safe implementation of reference counting,
+// which is slightly more efficient since it doesn't use atomics.
+
+class RefCountNTSImpl : public RefCountNTSImplCore
+{
+public:
+ OVR_FORCE_INLINE void AddRef() const { RefCount++; }
+ void Release() const;
+};
+
+
+
+// RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking
+// to the reference counting implementation. Base must be one of the RefCountImpl classes.
+
+template<class Base>
+class RefCountBaseStatImpl : public Base
+{
+public:
+ RefCountBaseStatImpl() { }
+
+ // *** Override New and Delete
+
+ // DOM-IGNORE-BEGIN
+ // Undef new temporarily if it is being redefined
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+#ifdef OVR_BUILD_DEBUG
+ // Custom check used to detect incorrect calls of 'delete' on ref-counted objects.
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \
+ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0)
+#else
+ #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p)
+#endif
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE)
+
+#undef OVR_REFCOUNTALLOC_CHECK_DELETE
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+ // OVR_BUILD_DEFINE_NEW
+ // DOM-IGNORE-END
+};
+
+
+template<class Base>
+class RefCountBaseStatVImpl : virtual public Base
+{
+public:
+ RefCountBaseStatVImpl() { }
+
+ // *** Override New and Delete
+
+ // DOM-IGNORE-BEGIN
+ // Undef new temporarily if it is being redefined
+#ifdef OVR_DEFINE_NEW
+#undef new
+#endif
+
+#define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p)
+
+ // Redefine all new & delete operators.
+ OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE)
+
+#undef OVR_REFCOUNTALLOC_CHECK_DELETE
+
+#ifdef OVR_DEFINE_NEW
+#define new OVR_DEFINE_NEW
+#endif
+ // OVR_BUILD_DEFINE_NEW
+ // DOM-IGNORE-END
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// *** End user RefCountBase<> classes
+
+
+// RefCountBase is a base class for classes that require thread-safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBase : public RefCountBaseStatImpl<RefCountImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl<RefCountImpl>() { }
+};
+
+// RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release
+
+template<class C>
+class RefCountBaseV : virtual public RefCountBaseStatVImpl<RefCountVImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatVImpl<RefCountVImpl>() { }
+};
+
+
+// RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference
+// counting; it also overrides the new and delete operators to use MemoryHeap.
+// This class should only be used if all pointers to it are known to be assigned,
+// destroyed and manipulated within one thread.
+//
+// Reference counted objects start out with RefCount value of 1. Further lifetime
+// management is done through the AddRef() and Release() methods, typically
+// hidden by Ptr<>.
+
+template<class C>
+class RefCountBaseNTS : public RefCountBaseStatImpl<RefCountNTSImpl>
+{
+public:
+ // Constructor.
+ OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl<RefCountNTSImpl>() { }
+};
+
+//-----------------------------------------------------------------------------------
+// ***** Pickable template pointer
+enum PickType { PickValue };
+
+template <typename T>
+class Pickable
+{
+public:
+ Pickable() : pV(NULL) {}
+ explicit Pickable(T* p) : pV(p) {}
+ Pickable(T* p, PickType) : pV(p)
+ {
+ OVR_ASSERT(pV);
+ if (pV)
+ pV->AddRef();
+ }
+ template <typename OT>
+ Pickable(const Pickable<OT>& other) : pV(other.GetPtr()) {}
+
+public:
+ Pickable& operator =(const Pickable& other)
+ {
+ OVR_ASSERT(pV == NULL);
+ pV = other.pV;
+ // Extra check.
+ //other.pV = NULL;
+ return *this;
+ }
+
+public:
+ T* GetPtr() const { return pV; }
+ T* operator->() const
+ {
+ return pV;
+ }
+ T& operator*() const
+ {
+ OVR_ASSERT(pV);
+ return *pV;
+ }
+
+private:
+ T* pV;
+};
+
+template <typename T>
+OVR_FORCE_INLINE
+Pickable<T> MakePickable(T* p)
+{
+ return Pickable<T>(p);
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Ref-Counted template pointer
+
+// Automatically AddRefs and Releases interfaces
+
+void* ReturnArg0(void* p);
+
+template<class C>
+class Ptr
+{
+#ifdef OVR_CC_ARM
+ static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); }
+#endif
+
+protected:
+ C *pObject;
+
+public:
+
+ // Constructors
+ OVR_FORCE_INLINE Ptr() : pObject(0)
+ { }
+#ifdef OVR_CC_ARM
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj))
+#else
+ OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj)
+#endif
+ { }
+ OVR_FORCE_INLINE Ptr(Pickable<C> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(Ptr<C>& other, PickType) : pObject(other.pObject)
+ {
+ other.pObject = NULL;
+ // No AddRef() on purpose.
+ }
+ OVR_FORCE_INLINE Ptr(C *pobj)
+ {
+ if (pobj) pobj->AddRef();
+ pObject = pobj;
+ }
+ OVR_FORCE_INLINE Ptr(const Ptr<C> &src)
+ {
+ if (src.pObject) src.pObject->AddRef();
+ pObject = src.pObject;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ pObject = src;
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr(Pickable<R> v) : pObject(v.GetPtr())
+ {
+ // No AddRef() on purpose.
+ }
+
+ // Destructor
+ OVR_FORCE_INLINE ~Ptr()
+ {
+ if (pObject) pObject->Release();
+ }
+
+ // Compares
+ OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; }
+ OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; }
+
+ OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; }
+ OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; }
+
+
+ OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; }
+
+ // Assignment
+ template<class R>
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE const Ptr<C>& operator = (const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE const Ptr<C>& operator = (C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<C> src)
+ {
+ return Pick(src);
+ }
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& operator = (Pickable<R> src)
+ {
+ return Pick(src);
+ }
+
+ // Set Assignment
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<R> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+ // Specialization
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(const Ptr<C> &src)
+ {
+ if (src) src->AddRef();
+ if (pObject) pObject->Release();
+ pObject = src;
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C *psrc)
+ {
+ if (psrc) psrc->AddRef();
+ if (pObject) pObject->Release();
+ pObject = psrc;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(C &src)
+ {
+ if (pObject) pObject->Release();
+ pObject = &src;
+ return *this;
+ }
+ OVR_FORCE_INLINE Ptr<C>& SetPtr(Pickable<C> src)
+ {
+ return Pick(src);
+ }
+
+ // Nulls ref-counted pointer without decrement
+ OVR_FORCE_INLINE void NullWithoutRelease()
+ {
+ pObject = 0;
+ }
+
+ // Clears the pointer to the object
+ OVR_FORCE_INLINE void Clear()
+ {
+ if (pObject) pObject->Release();
+ pObject = 0;
+ }
+
+ // Obtain pointer reference directly, for D3D interfaces
+ OVR_FORCE_INLINE C*& GetRawRef() { return pObject; }
+
+ // Access Operators
+ OVR_FORCE_INLINE C* GetPtr() const { return pObject; }
+ OVR_FORCE_INLINE C& operator * () const { return *pObject; }
+ OVR_FORCE_INLINE C* operator -> () const { return pObject; }
+ // Conversion
+ OVR_FORCE_INLINE operator C* () const { return pObject; }
+
+ // Pickers.
+
+ // Pick a value.
+ OVR_FORCE_INLINE Ptr<C>& Pick(Ptr<C>& other)
+ {
+ if (&other != this)
+ {
+ if (pObject) pObject->Release();
+ pObject = other.pObject;
+ other.pObject = 0;
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<C> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ template<class R>
+ OVR_FORCE_INLINE Ptr<C>& Pick(Pickable<R> v)
+ {
+ if (v.GetPtr() != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = v.GetPtr();
+ }
+
+ return *this;
+ }
+
+ OVR_FORCE_INLINE Ptr<C>& Pick(C* p)
+ {
+ if (p != pObject)
+ {
+ if (pObject) pObject->Release();
+ pObject = p;
+ }
+
+ return *this;
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Std.cpp b/LibOVR/Src/Kernel/OVR_Std.cpp
new file mode 100644
index 0000000..6b5be18
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Std.cpp
@@ -0,0 +1,1036 @@
+/************************************************************************************
+
+Filename : OVR_Std.cpp
+Content : Standard C function implementation
+Created : September 19, 2012
+Notes :
+
+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_Std.h"
+#include "OVR_Alg.h"
+
+// localeconv() call in OVR_strtod()
+#include <locale.h>
+
+namespace OVR {
+
+// Source for functions not available on all platforms is included here.
+
+// Case insensitive compare implemented in platform-specific way.
+int OVR_CDECL OVR_stricmp(const char* a, const char* b)
+{
+#if defined(OVR_OS_WIN32)
+ #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_stricmp(a, b);
+ #else
+ return ::stricmp(a, b);
+ #endif
+
+#else
+ return strcasecmp(a, b);
+#endif
+}
+
+int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_strnicmp(a, b, count);
+#else
+ return ::strnicmp(a, b, count);
+#endif
+
+#else
+ return strncasecmp(a, b, count);
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscpy_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscpy(dest, src);
+ return dest;
+#else
+ UPInt l = OVR_wcslen(src) + 1; // incl term null
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcsncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ UPInt srclen = OVR_wcslen(src);
+ UPInt l = Alg::Min(srclen, count);
+ l = (l < destsize) ? l : destsize;
+ memcpy(dest, src, l * sizeof(wchar_t));
+ if (count > srclen)
+ {
+ UPInt remLen = Alg::Min(destsize - l, (count - srclen));
+ memset(&dest[l], 0, sizeof(wchar_t)*remLen);
+ }
+ else if (l < destsize)
+ dest[l] = 0;
+ return dest;
+#endif
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ wcscat_s(dest, destsize, src);
+ return dest;
+#elif defined(OVR_OS_WIN32)
+ OVR_UNUSED(destsize);
+ wcscat(dest, src);
+ return dest;
+#else
+ UPInt dstlen = OVR_wcslen(dest); // do not incl term null
+ UPInt srclen = OVR_wcslen(src) + 1; // incl term null
+ UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen;
+ memcpy(dest + dstlen, src, copylen * sizeof(wchar_t));
+ return dest;
+#endif
+}
+
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str)
+{
+#if defined(OVR_OS_WIN32)
+ return wcslen(str);
+#else
+ UPInt i = 0;
+ while(str[i] != '\0')
+ ++i;
+ return i;
+#endif
+}
+
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscmp(a, b);
+#else
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = *pa;
+ wchar_t cb = *pb;
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#endif
+}
+
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicmp(a, b);
+#else
+ return ::wcsicmp(a, b);
+#endif
+#elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE)
+ // not supported, use custom implementation
+ const wchar_t *pa = a, *pb = b;
+ while (*pa && *pb)
+ {
+ wchar_t ca = OVR_towlower(*pa);
+ wchar_t cb = OVR_towlower(*pb);
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return 1;
+ pa++;
+ pb++;
+ }
+ if (*pa)
+ return 1;
+ else if (*pb)
+ return -1;
+ else
+ return 0;
+#else
+ return wcscasecmp(a, b);
+#endif
+}
+
+// This function is not inline because of dependency on <locale.h>
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr)
+{
+#if !defined(OVR_OS_ANDROID)
+ const char s = *localeconv()->decimal_point;
+
+ if (s != '.')
+ {
+ char buffer[347 + 1];
+
+ OVR_strcpy(buffer, sizeof(buffer), string);
+
+ for (char* c = buffer; *c != '\0'; ++c)
+ {
+ if (*c == '.')
+ {
+ *c = s;
+ break;
+ }
+ }
+
+ return strtod(buffer, tailptr);
+ }
+#endif
+
+ return strtod(string, tailptr);
+}
+
+
+#ifndef OVR_NO_WCTYPE
+
+//// Use this class to generate Unicode bitsets. For example:
+////
+//// UnicodeBitSet bitSet;
+//// for(unsigned i = 0; i < 65536; ++i)
+//// {
+//// if (iswalpha(i))
+//// bitSet.Set(i);
+//// }
+//// bitSet.Dump();
+////
+////---------------------------------------------------------------
+//class UnicodeBitSet
+//{
+//public:
+// UnicodeBitSet()
+// {
+// memset(Offsets, 0, sizeof(Offsets));
+// memset(Bits, 0, sizeof(Bits));
+// }
+//
+// void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); }
+//
+// void Dump()
+// {
+// unsigned i, j;
+// unsigned offsetCount = 0;
+// for(i = 0; i < 256; ++i)
+// {
+// if (isNull(i)) Offsets[i] = 0;
+// else
+// if (isFull(i)) Offsets[i] = 1;
+// else Offsets[i] = UInt16(offsetCount++ * 16 + 256);
+// }
+// for(i = 0; i < 16; ++i)
+// {
+// for(j = 0; j < 16; ++j)
+// {
+// printf("%5u,", Offsets[i*16+j]);
+// }
+// printf("\n");
+// }
+// for(i = 0; i < 256; ++i)
+// {
+// if (Offsets[i] > 255)
+// {
+// for(j = 0; j < 16; j++)
+// {
+// printf("%5u,", Bits[i][j]);
+// }
+// printf("\n");
+// }
+// }
+// }
+//
+//private:
+// bool isNull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0) return false;
+// return true;
+// }
+//
+// bool isFull(unsigned n) const
+// {
+// const UInt16* p = Bits[n];
+// for(unsigned i = 0; i < 16; ++i)
+// if (p[i] != 0xFFFF) return false;
+// return true;
+// }
+//
+// UInt16 Offsets[256];
+// UInt16 Bits[256][16];
+//};
+
+
+const UInt16 UnicodeAlnumBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3,
+34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+ 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0,
+ 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeAlphaBits[] = {
+ 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+ 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+ 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720,
+ 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832,
+ 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407,
+65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+ 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15,
+65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+ 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7,
+ 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168,
+ 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3,
+34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0,
+40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0,
+57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0,
+57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12,
+65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0,
+ 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0,
+65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127,
+65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31,
+65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0,
+ 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+ 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+ 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047,
+65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+ 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+ 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+
+const UInt16 UnicodeDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384,
+ 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464,
+ 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0,
+ 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0,
+ 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeSpaceBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const UInt16 UnicodeXDigitBits[] = {
+ 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272,
+ 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+// Uncomment if necessary
+//const UInt16 UnicodeCntrlBits[] = {
+// 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0,
+// 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336,
+//65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0,
+//32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584};
+//
+//const UInt16 UnicodeGraphBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePrintBits[] = {
+// 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464,
+// 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624,
+// 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736,
+// 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+// 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848,
+// 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535,
+//65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0,
+// 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15,
+//65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831,
+// 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31,
+// 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191,
+//16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0,
+//65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3,
+//34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0,
+//40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0,
+//57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0,
+//57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28,
+//65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0,
+//65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0,
+//65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175,
+//65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023,
+//65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535,
+//32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31,
+//65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,
+//65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0,
+// 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023,
+//65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156,
+// 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0,
+//64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095,
+//65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0,
+//65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0,
+//65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535,
+//65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095,
+// 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959,
+//63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0};
+//
+//const UInt16 UnicodePunctBits[] = {
+// 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384,
+// 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0,
+// 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560,
+// 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128,
+// 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24,
+// 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+//16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+// 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0,
+// 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048,
+// 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeLowerBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+// 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432,
+// 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407,
+//43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565,
+//43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15,
+// 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554,
+// 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0,
+//43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682,
+// 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316,
+// 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0,
+//50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+//
+//const UInt16 UnicodeUpperBits[] = {
+// 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+// 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+// 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+//21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974,
+//21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0,
+//65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277,
+// 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+//21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+//65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936,
+//14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+// MA: March 19, 2010
+// Modified ToUpper and ToLower tables to match values expected by AS3 tests.
+// ToLower modifications:
+// 304 -> 105
+// 1024 -> 1104 *
+// 1037 -> 1117 *
+// UoUpper modifications:
+// 255 -> 376
+// 305 -> 73
+// 383 -> 83
+// 1104 -> 1024 *
+// 1117 -> 1037 *
+// Entries marked with a '*' don't make complete sense based on Unicode manual, although
+// they match AS3.
+
+
+static const UInt16 UnicodeToUpperBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368,
+ 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416,
+ 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407,
+43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3.
+43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0,
+ 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3.
+ 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0,
+43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682,
+ 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0,
+ 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const UInt16 UnicodeToLowerBits[] = {
+ 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384,
+ 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432,
+ 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0,
+21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3.
+21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0,
+65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277,
+ 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0,
+21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341,
+65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840,
+ 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0,
+ 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+struct GUnicodePairType
+{
+ UInt16 Key, Value;
+};
+
+static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key)
+{
+ return a.Key < key;
+}
+
+static const GUnicodePairType UnicodeToUpperTable[] = {
+{ 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71},
+{ 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78},
+{ 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85},
+{ 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193},
+{ 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200},
+{ 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207},
+{ 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214},
+{ 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222},
+{ 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266},
+{ 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280},
+{ 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294},
+{ 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310},
+{ 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325},
+{ 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340},
+{ 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354},
+{ 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368},
+{ 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386},
+{ 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418},
+{ 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440},
+{ 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465},
+{ 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478},
+{ 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492},
+{ 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512},
+{ 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526},
+{ 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393},
+{ 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406},
+{ 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434},
+{ 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914},
+{ 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921},
+{ 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928},
+{ 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935},
+{ 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911},
+{ 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006},
+{ 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046},
+{ 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053},
+{ 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060},
+{ 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067},
+{ 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027},
+{ 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034},
+{ 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124},
+{ 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138},
+{ 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152},
+{ 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180},
+{ 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194},
+{ 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208},
+{ 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227},
+{ 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244},
+{ 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258},
+{ 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330},
+{ 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337},
+{ 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344},
+{ 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351},
+{ 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358},
+{ 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365},
+{ 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690},
+{ 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704},
+{ 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718},
+{ 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732},
+{ 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746},
+{ 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760},
+{ 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774},
+{ 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788},
+{ 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802},
+{ 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816},
+{ 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840},
+{ 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854},
+{ 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868},
+{ 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882},
+{ 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896},
+{ 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910},
+{ 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924},
+{ 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948},
+{ 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963},
+{ 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980},
+{ 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995},
+{ 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010},
+{ 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031},
+{ 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046},
+{ 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139},
+{ 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186},
+{ 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169},
+{ 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549},
+{ 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556},
+{ 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401},
+{ 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408},
+{ 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415},
+{ 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422},
+{ 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318},
+{65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325},
+{65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332},
+{65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}};
+
+static const GUnicodePairType UnicodeToLowerTable[] = {
+{ 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103},
+{ 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110},
+{ 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117},
+{ 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225},
+{ 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232},
+{ 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239},
+{ 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246},
+{ 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254},
+{ 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269},
+{ 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283},
+{ 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297},
+{ 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314},
+{ 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328},
+{ 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343},
+{ 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357},
+{ 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371},
+{ 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595},
+{ 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396},
+{ 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617},
+{ 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419},
+{ 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650},
+{ 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454},
+{ 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470},
+{ 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485},
+{ 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501},
+{ 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519},
+{ 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533},
+{ 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973},
+{ 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950},
+{ 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957},
+{ 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965},
+{ 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995},
+{ 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105},
+{ 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112},
+{ 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072},
+{ 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079},
+{ 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086},
+{ 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093},
+{ 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100},
+{ 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127},
+{ 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141},
+{ 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169},
+{ 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183},
+{ 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197},
+{ 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211},
+{ 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233},
+{ 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247},
+{ 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263},
+{ 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379},
+{ 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386},
+{ 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393},
+{ 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400},
+{ 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407},
+{ 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414},
+{ 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310},
+{ 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317},
+{ 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324},
+{ 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331},
+{ 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338},
+{ 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687},
+{ 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701},
+{ 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715},
+{ 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729},
+{ 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743},
+{ 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757},
+{ 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771},
+{ 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785},
+{ 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799},
+{ 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813},
+{ 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827},
+{ 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851},
+{ 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865},
+{ 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879},
+{ 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893},
+{ 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907},
+{ 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921},
+{ 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938},
+{ 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953},
+{ 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970},
+{ 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985},
+{ 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000},
+{ 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019},
+{ 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036},
+{ 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049},
+{ 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054},
+{ 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056},
+{ 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563},
+{ 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570},
+{ 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425},
+{ 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432},
+{ 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439},
+{ 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446},
+{ 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348},
+{65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355},
+{65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362},
+{65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369},
+{65338,65370}, {65535, 0}};
+
+int OVR_CDECL OVR_towupper(wchar_t charCode)
+{
+ // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits.
+ if (UnicodeCharIs(UnicodeToUpperBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToUpperTable,
+ 0,
+ sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToUpperTable[idx].Value;
+ }
+ return charCode;
+}
+
+int OVR_CDECL OVR_towlower(wchar_t charCode)
+{
+ // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits.
+ if (UnicodeCharIs(UnicodeToLowerBits, charCode))
+ {
+ // To protect from memory overrun in case the character is not found
+ // we use one extra fake element in the table {65536, 0}.
+ UPInt idx = Alg::LowerBoundSliced(
+ UnicodeToLowerTable,
+ 0,
+ sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1,
+ (UInt16)charCode,
+ CmpUnicodeKey);
+ return UnicodeToLowerTable[idx].Value;
+ }
+ return charCode;
+}
+
+#endif //OVR_NO_WCTYPE
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_Std.h b/LibOVR/Src/Kernel/OVR_Std.h
new file mode 100644
index 0000000..c11f853
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Std.h
@@ -0,0 +1,514 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Std.h
+Content : Standard C function interface
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Std_h
+#define OVR_Std_h
+
+#include "OVR_Types.h"
+#include <stdarg.h> // for va_list args
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+#define OVR_MSVC_SAFESTRING
+#include <errno.h>
+#endif
+
+// Wide-char funcs
+#include <wchar.h>
+#include <wctype.h>
+
+namespace OVR {
+
+#if defined(OVR_OS_WIN32)
+inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ _itoa_s(val, dest, destsize, radix);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return itoa(val, dest, radix);
+#endif
+}
+#else // OVR_OS_WIN32
+inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix)
+{
+ if (val == 0)
+ {
+ if (len > 1)
+ {
+ dest[0] = '0';
+ dest[1] = '\0';
+ }
+ return dest;
+ }
+
+ int cur = val;
+ unsigned int i = 0;
+ unsigned int sign = 0;
+
+ if (val < 0)
+ {
+ val = -val;
+ sign = 1;
+ }
+
+ while ((val != 0) && (i < (len - 1 - sign)))
+ {
+ cur = val % radix;
+ val /= radix;
+
+ if (radix == 16)
+ {
+ switch(cur)
+ {
+ case 10:
+ dest[i] = 'a';
+ break;
+ case 11:
+ dest[i] = 'b';
+ break;
+ case 12:
+ dest[i] = 'c';
+ break;
+ case 13:
+ dest[i] = 'd';
+ break;
+ case 14:
+ dest[i] = 'e';
+ break;
+ case 15:
+ dest[i] = 'f';
+ break;
+ default:
+ dest[i] = (char)('0' + cur);
+ break;
+ }
+ }
+ else
+ {
+ dest[i] = (char)('0' + cur);
+ }
+ ++i;
+ }
+
+ if (sign)
+ {
+ dest[i++] = '-';
+ }
+
+ for (unsigned int j = 0; j < i / 2; ++j)
+ {
+ char tmp = dest[j];
+ dest[j] = dest[i - 1 - j];
+ dest[i - 1 - j] = tmp;
+ }
+ dest[i] = '\0';
+
+ return dest;
+}
+
+#endif
+
+
+// String functions
+
+inline UPInt OVR_CDECL OVR_strlen(const char* str)
+{
+ return strlen(str);
+}
+
+inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcpy_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcpy(dest, src);
+#endif
+}
+
+inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strncpy_s(dest, destsize, src, count);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strncpy(dest, src, count);
+#endif
+}
+
+inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src)
+{
+#if defined(OVR_MSVC_SAFESTRING)
+ strcat_s(dest, destsize, src);
+ return dest;
+#else
+ OVR_UNUSED(destsize);
+ return strcat(dest, src);
+#endif
+}
+
+inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src)
+{
+ return strcmp(dest, src);
+}
+
+inline const char* OVR_CDECL OVR_strchr(const char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline char* OVR_CDECL OVR_strchr(char* str, char c)
+{
+ return strchr(str, c);
+}
+
+inline const char* OVR_strrchr(const char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c)
+{
+ for (SPInt i = (SPInt)size - 1; i >= 0; i--)
+ {
+ if (str[i] == c)
+ return str + i;
+ }
+ return 0;
+}
+
+inline char* OVR_CDECL OVR_strrchr(char* str, char c)
+{
+ UPInt len = OVR_strlen(str);
+ for (UPInt i=len; i>0; i--)
+ if (str[i]==c)
+ return str+i;
+ return 0;
+}
+
+
+double OVR_CDECL OVR_strtod(const char* string, char** tailptr);
+
+inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix)
+{
+ return strtol(string, tailptr, radix);
+}
+
+inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix)
+{
+ return strtoul(string, tailptr, radix);
+}
+
+inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size)
+{
+ return strncmp(ws1, ws2, size);
+}
+
+inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoui64(nptr, endptr, base);
+#else
+ return strtoull(nptr, endptr, base);
+#endif
+}
+
+inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _strtoi64(nptr, endptr, base);
+#else
+ return strtoll(nptr, endptr, base);
+#endif
+}
+
+
+inline SInt64 OVR_CDECL OVR_atoq(const char* string)
+{
+#if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE)
+ return _atoi64(string);
+#else
+ return atoll(string);
+#endif
+}
+
+inline UInt64 OVR_CDECL OVR_atouq(const char* string)
+{
+ return OVR_strtouq(string, NULL, 10);
+}
+
+
+// Implemented in GStd.cpp in platform-specific manner.
+int OVR_CDECL OVR_stricmp(const char* dest, const char* src);
+int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count);
+
+inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...)
+{
+ va_list argList;
+ va_start(argList,format);
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ OVR_ASSERT(ret != -1);
+ #else
+ OVR_UNUSED(destsize);
+ ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character
+ OVR_ASSERT(ret != -1);
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ va_end(argList);
+ return ret;
+}
+
+inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ #if defined(OVR_MSVC_SAFESTRING)
+ dest[0] = '\0';
+ int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList);
+ if (rv == -1)
+ {
+ dest[destsize - 1] = '\0';
+ ret = destsize - 1;
+ }
+ else
+ ret = (UPInt)rv;
+ #else
+ OVR_UNUSED(destsize);
+ int rv = _vsnprintf(dest, destsize - 1, format, argList);
+ OVR_ASSERT(rv != -1);
+ ret = (UPInt)rv;
+ dest[destsize-1] = 0;
+ #endif
+#else
+ OVR_UNUSED(destsize);
+ ret = (UPInt)vsprintf(dest, format, argList);
+ OVR_ASSERT(ret < destsize);
+#endif
+ return ret;
+}
+
+// Returns the number of characters in the formatted string.
+inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList)
+{
+ UPInt ret;
+#if defined(OVR_CC_MSVC)
+ ret = (UPInt) _vscprintf(format, argList);
+#else
+ ret = (UPInt) vsnprintf(NULL, 0, format, argList);
+#endif
+ return ret;
+}
+
+
+wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src);
+wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count);
+wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src);
+UPInt OVR_CDECL OVR_wcslen(const wchar_t* str);
+int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b);
+int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b);
+
+inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32)
+#if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400)
+ return ::_wcsicoll(a, b);
+#else
+ return ::wcsicoll(a, b);
+#endif
+#else
+ // not supported, use regular wcsicmp
+ return OVR_wcsicmp(a, b);
+#endif
+}
+
+inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b)
+{
+#if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX)
+ return wcscoll(a, b);
+#else
+ // not supported, use regular wcscmp
+ return OVR_wcscmp(a, b);
+#endif
+}
+
+#ifndef OVR_NO_WCTYPE
+
+inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode)
+{
+ unsigned offset = table[charCode >> 8];
+ if (offset == 0) return 0;
+ if (offset == 1) return 1;
+ return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0;
+}
+
+extern const UInt16 UnicodeAlnumBits[];
+extern const UInt16 UnicodeAlphaBits[];
+extern const UInt16 UnicodeDigitBits[];
+extern const UInt16 UnicodeSpaceBits[];
+extern const UInt16 UnicodeXDigitBits[];
+
+// Uncomment if necessary
+//extern const UInt16 UnicodeCntrlBits[];
+//extern const UInt16 UnicodeGraphBits[];
+//extern const UInt16 UnicodeLowerBits[];
+//extern const UInt16 UnicodePrintBits[];
+//extern const UInt16 UnicodePunctBits[];
+//extern const UInt16 UnicodeUpperBits[];
+
+inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); }
+inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); }
+inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); }
+inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); }
+inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); }
+
+// Uncomment if necessary
+//inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); }
+//inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); }
+//inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); }
+//inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); }
+//inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); }
+//inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); }
+
+int OVR_CDECL OVR_towupper(wchar_t charCode);
+int OVR_CDECL OVR_towlower(wchar_t charCode);
+
+#else // OVR_NO_WCTYPE
+
+inline int OVR_CDECL OVR_iswspace(wchar_t c)
+{
+ return iswspace(c);
+}
+
+inline int OVR_CDECL OVR_iswdigit(wchar_t c)
+{
+ return iswdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswxdigit(wchar_t c)
+{
+ return iswxdigit(c);
+}
+
+inline int OVR_CDECL OVR_iswalpha(wchar_t c)
+{
+ return iswalpha(c);
+}
+
+inline int OVR_CDECL OVR_iswalnum(wchar_t c)
+{
+ return iswalnum(c);
+}
+
+inline wchar_t OVR_CDECL OVR_towlower(wchar_t c)
+{
+ return (wchar_t)towlower(c);
+}
+
+inline wchar_t OVR_towupper(wchar_t c)
+{
+ return (wchar_t)towupper(c);
+}
+
+#endif // OVR_NO_WCTYPE
+
+// ASCII versions of tolower and toupper. Don't use "char"
+inline int OVR_CDECL OVR_tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
+}
+
+inline int OVR_CDECL OVR_toupper(int c)
+{
+ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
+}
+
+
+
+inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return OVR_strtod(buffer, &tp);
+#else
+ return wcstod(string, tailptr);
+#endif
+}
+
+inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix)
+{
+#if defined(OVR_OS_OTHER)
+ OVR_UNUSED(tailptr);
+ char buffer[64];
+ char* tp = NULL;
+ UPInt max = OVR_wcslen(string);
+ if (max > 63) max = 63;
+ unsigned char c = 0;
+ for (UPInt i=0; i < max; i++)
+ {
+ c = (unsigned char)string[i];
+ buffer[i] = ((c) < 128 ? (char)c : '!');
+ }
+ buffer[max] = 0;
+ return strtol(buffer, &tp, radix);
+#else
+ return wcstol(string, tailptr, radix);
+#endif
+}
+
+} // OVR
+
+#endif // OVR_Std_h
diff --git a/LibOVR/Src/Kernel/OVR_String.cpp b/LibOVR/Src/Kernel/OVR_String.cpp
new file mode 100644
index 0000000..75b7c0e
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String.cpp
@@ -0,0 +1,768 @@
+/************************************************************************************
+
+Filename : OVR_String.cpp
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+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_String.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef OVR_OS_QNX
+# include <strings.h>
+#endif
+
+namespace OVR {
+
+#define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift)
+
+String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} };
+
+
+String::String()
+{
+ pData = &NullData;
+ pData->AddRef();
+};
+
+String::String(const char* pdata)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size = pdata ? OVR_strlen(pdata) : 0;
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+String::String(const char* pdata1, const char* pdata2, const char* pdata3)
+{
+ // Obtain length in bytes; it doesn't matter if _data is UTF8.
+ UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0;
+ UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0;
+ UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0;
+
+ DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0,
+ pdata1, size1, pdata2, size2);
+ memcpy(pdataDesc->Data + size1 + size2, pdata3, size3);
+ pData = pdataDesc;
+}
+
+String::String(const char* pdata, UPInt size)
+{
+ OVR_ASSERT((size == 0) || (pdata != 0));
+ pData = AllocDataCopy1(size, 0, pdata, size);
+};
+
+
+String::String(const InitStruct& src, UPInt size)
+{
+ pData = AllocData(size, 0);
+ src.InitString(GetData()->Data, size);
+}
+
+String::String(const String& src)
+{
+ pData = src.GetData();
+ pData->AddRef();
+}
+
+String::String(const StringBuffer& src)
+{
+ pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize());
+}
+
+String::String(const wchar_t* data)
+{
+ pData = &NullData;
+ pData->AddRef();
+ // Simplified logic for wchar_t constructor.
+ if (data)
+ *this = data;
+}
+
+
+String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize)
+{
+ String::DataDesc* pdesc;
+
+ if (size == 0)
+ {
+ pdesc = &NullData;
+ pdesc->AddRef();
+ return pdesc;
+ }
+
+ pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size);
+ pdesc->Data[size] = 0;
+ pdesc->RefCount = 1;
+ pdesc->Size = size | lengthIsSize;
+ return pdesc;
+}
+
+
+String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata, copySize);
+ return pdesc;
+}
+
+String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2)
+{
+ String::DataDesc* pdesc = AllocData(size, lengthIsSize);
+ memcpy(pdesc->Data, pdata1, copySize1);
+ memcpy(pdesc->Data + copySize1, pdata2, copySize2);
+ return pdesc;
+}
+
+
+UPInt String::GetLength() const
+{
+ // Optimize length accesses for non-UTF8 character strings.
+ DataDesc* pdata = GetData();
+ UPInt length, size = pdata->GetSize();
+
+ if (pdata->LengthIsSize())
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size);
+
+ if (length == size)
+ pdata->Size |= String_LengthIsSize;
+
+ return length;
+}
+
+
+//static UInt32 String_CharSearch(const char* buf, )
+
+
+UInt32 String::GetCharAt(UPInt index) const
+{
+ SPInt i = (SPInt) index;
+ DataDesc* pdata = GetData();
+ const char* buf = pdata->Data;
+ UInt32 c;
+
+ if (pdata->LengthIsSize())
+ {
+ OVR_ASSERT(index < pdata->GetSize());
+ buf += i;
+ return UTF8Util::DecodeNextChar_Advance0(&buf);
+ }
+
+ c = UTF8Util::GetCharAt(index, buf, pdata->GetSize());
+ return c;
+}
+
+UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const
+{
+ DataDesc* pdata = GetData();
+ SPInt i = (SPInt) index;
+ const char* buf = pdata->Data;
+ const char* end = buf + pdata->GetSize();
+ UInt32 c;
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ i--;
+
+ if (buf >= end)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(i == 0);
+ return c;
+ }
+ } while (i >= 0);
+
+ *offset = buf;
+
+ return c;
+}
+
+UInt32 String::GetNextChar(const char** offset) const
+{
+ return UTF8Util::DecodeNextChar(offset);
+}
+
+
+
+void String::AppendChar(UInt32 ch)
+{
+ DataDesc* pdata = GetData();
+ UPInt size = pdata->GetSize();
+ char buff[8];
+ SPInt encodeSize = 0;
+
+ // Converts ch into UTF8 string and fills it into buff.
+ UTF8Util::EncodeChar(buff, &encodeSize, ch);
+ OVR_ASSERT(encodeSize >= 0);
+
+ SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0,
+ pdata->Data, size, buff, (UPInt)encodeSize));
+ pdata->Release();
+}
+
+
+void String::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len);
+
+ DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0,
+ pdata->Data, oldSize);
+ UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len);
+
+ SetData(pnewData);
+ pdata->Release();
+}
+
+
+void String::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+
+ SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0,
+ pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz));
+ pdata->Release();
+}
+
+void String::AssignString(const InitStruct& src, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ DataDesc* pnewData = AllocData(size, 0);
+ src.InitString(pnewData->Data, size);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+void String::AssignString(const char* putf8str, UPInt size)
+{
+ DataDesc* poldData = GetData();
+ SetData(AllocDataCopy1(size, 0, putf8str, size));
+ poldData->Release();
+}
+
+void String::operator = (const char* pstr)
+{
+ AssignString(pstr, pstr ? OVR_strlen(pstr) : 0);
+}
+
+void String::operator = (const wchar_t* pwstr)
+{
+ DataDesc* poldData = GetData();
+ UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0;
+
+ DataDesc* pnewData = AllocData(size, 0);
+ UTF8Util::EncodeString(pnewData->Data, pwstr);
+ SetData(pnewData);
+ poldData->Release();
+}
+
+
+void String::operator = (const String& src)
+{
+ DataDesc* psdata = src.GetData();
+ DataDesc* pdata = GetData();
+
+ SetData(psdata);
+ psdata->AddRef();
+ pdata->Release();
+}
+
+
+void String::operator = (const StringBuffer& src)
+{
+ DataDesc* polddata = GetData();
+ SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()));
+ polddata->Release();
+}
+
+void String::operator += (const String& src)
+{
+ DataDesc *pourData = GetData(),
+ *psrcData = src.GetData();
+ UPInt ourSize = pourData->GetSize(),
+ srcSize = psrcData->GetSize();
+ UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag();
+
+ SetData(AllocDataCopy2(ourSize + srcSize, lflag,
+ pourData->Data, ourSize, psrcData->Data, srcSize));
+ pourData->Release();
+}
+
+
+String String::operator + (const char* str) const
+{
+ String tmp1(*this);
+ tmp1 += (str ? str : "");
+ return tmp1;
+}
+
+String String::operator + (const String& src) const
+{
+ String tmp1(*this);
+ tmp1 += src;
+ return tmp1;
+}
+
+void String::Remove(UPInt posAt, SPInt removeLength)
+{
+ DataDesc* pdata = GetData();
+ UPInt oldSize = pdata->GetSize();
+ // Length indicates the number of characters to remove.
+ UPInt length = GetLength();
+
+ // If index is past the string, nothing to remove.
+ if (posAt >= length)
+ return;
+ // Otherwise, cap removeLength to the length of the string.
+ if ((posAt + removeLength) > length)
+ removeLength = length - posAt;
+
+ // Get the byte position of the UTF8 char at position posAt.
+ SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize);
+ SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos);
+
+ SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(),
+ pdata->Data, bytePos,
+ pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize)));
+ pdata->Release();
+}
+
+
+String String::Substring(UPInt start, UPInt end) const
+{
+ UPInt length = GetLength();
+ if ((start >= length) || (start >= end))
+ return String();
+
+ DataDesc* pdata = GetData();
+
+ // If size matches, we know the exact index range.
+ if (pdata->LengthIsSize())
+ return String(pdata->Data + start, end - start);
+
+ // Get position of starting character.
+ SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize());
+ SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart);
+ return String(pdata->Data + byteStart, (UPInt)byteSize);
+}
+
+void String::Clear()
+{
+ NullData.AddRef();
+ GetData()->Release();
+ SetData(&NullData);
+}
+
+
+String String::ToUpper() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+String String::ToLower() const
+{
+ UInt32 c;
+ const char* psource = GetData()->Data;
+ const char* pend = psource + GetData()->GetSize();
+ String str;
+ SPInt bufferOffset = 0;
+ char buffer[512];
+
+ while(psource < pend)
+ {
+ do {
+ c = UTF8Util::DecodeNextChar_Advance0(&psource);
+ UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c)));
+ } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8)));
+
+ // Append string a piece at a time.
+ str.AppendString(buffer, bufferOffset);
+ bufferOffset = 0;
+ }
+
+ return str;
+}
+
+
+
+String& String::Insert(const char* substr, UPInt posAt, SPInt strSize)
+{
+ DataDesc* poldData = GetData();
+ UPInt oldSize = poldData->GetSize();
+ UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize;
+ UPInt byteIndex = (poldData->LengthIsSize()) ?
+ posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+
+ DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0,
+ poldData->Data, byteIndex, substr, insertSize);
+ memcpy(pnewData->Data + byteIndex + insertSize,
+ poldData->Data + byteIndex, oldSize - byteIndex);
+ SetData(pnewData);
+ poldData->Release();
+ return *this;
+}
+
+/*
+String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len)
+{
+ for (SPInt i = 0; i < len; ++i)
+ {
+ UPInt charw = InsertCharAt(substr[i], posAt);
+ posAt += charw;
+ }
+ return *this;
+}
+*/
+
+UPInt String::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt index = 0;
+ UTF8Util::EncodeChar(buf, &index, c);
+ OVR_ASSERT(index >= 0);
+ buf[(UPInt)index] = 0;
+
+ Insert(buf, posAt, index);
+ return (UPInt)index;
+}
+
+
+int String::CompareNoCase(const char* a, const char* b)
+{
+ return OVR_stricmp(a, b);
+}
+
+int String::CompareNoCase(const char* a, const char* b, SPInt len)
+{
+ if (len)
+ {
+ SPInt f,l;
+ SPInt slen = len;
+ const char *s = b;
+ do {
+ f = (SPInt)OVR_tolower((int)(*(a++)));
+ l = (SPInt)OVR_tolower((int)(*(b++)));
+ } while (--len && f && (f == l) && *b != 0);
+
+ if (f == l && (len != 0 || *b != 0))
+ {
+ f = (SPInt)slen;
+ l = (SPInt)OVR_strlen(s);
+ return int(f - l);
+ }
+
+ return int(f - l);
+ }
+ else
+ return (0-(int)OVR_strlen(b));
+}
+
+// ***** Implement hash static functions
+
+// Hash function
+UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ (unsigned) pdata[size];
+ }
+
+ return h;
+}
+
+// Hash function, case-insensitive
+UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed)
+{
+ const UByte* pdata = (const UByte*) pdataIn;
+ UPInt h = seed;
+ while (size > 0)
+ {
+ size--;
+ h = ((h << 5) + h) ^ OVR_tolower(pdata[size]);
+ }
+
+ // Alternative: "sdbm" hash function, suggested at same web page above.
+ // h = 0;
+ // for bytes { h = (h << 16) + (h << 6) - hash + *p; }
+ return h;
+}
+
+
+
+// ***** String Buffer used for Building Strings
+
+
+#define OVR_SBUFF_DEFAULT_GROW_SIZE 512
+// Constructors / Destructor.
+StringBuffer::StringBuffer()
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+}
+
+StringBuffer::StringBuffer(UPInt growSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ SetGrowSize(growSize);
+}
+
+StringBuffer::StringBuffer(const char* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(data);
+}
+
+StringBuffer::StringBuffer(const char* data, UPInt dataSize)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(data, dataSize);
+}
+
+StringBuffer::StringBuffer(const String& src)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+}
+
+StringBuffer::StringBuffer(const StringBuffer& src)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ AppendString(src.ToCStr(), src.GetSize());
+}
+
+StringBuffer::StringBuffer(const wchar_t* data)
+ : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
+{
+ *this = data;
+}
+
+StringBuffer::~StringBuffer()
+{
+ if (pData)
+ OVR_FREE(pData);
+}
+void StringBuffer::SetGrowSize(UPInt growSize)
+{
+ if (growSize <= 16)
+ GrowSize = 16;
+ else
+ {
+ UByte bits = Alg::UpperBit(UInt32(growSize-1));
+ UPInt size = 1<<bits;
+ GrowSize = size == growSize ? growSize : size;
+ }
+}
+
+UPInt StringBuffer::GetLength() const
+{
+ UPInt length, size = GetSize();
+ if (LengthIsSize)
+ return size;
+
+ length = (UPInt)UTF8Util::GetLength(pData, (UPInt)GetSize());
+
+ if (length == GetSize())
+ LengthIsSize = true;
+ return length;
+}
+
+void StringBuffer::Reserve(UPInt _size)
+{
+ if (_size >= BufferSize) // >= because of trailing zero! (!AB)
+ {
+ BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1);
+ if (!pData)
+ pData = (char*)OVR_ALLOC(BufferSize);
+ else
+ pData = (char*)OVR_REALLOC(pData, BufferSize);
+ }
+}
+void StringBuffer::Resize(UPInt _size)
+{
+ Reserve(_size);
+ LengthIsSize = false;
+ Size = _size;
+ if (pData)
+ pData[Size] = 0;
+}
+
+void StringBuffer::Clear()
+{
+ Resize(0);
+ /*
+ if (pData != pEmptyNullData)
+ {
+ OVR_FREE(pHeap, pData);
+ pData = pEmptyNullData;
+ Size = BufferSize = 0;
+ LengthIsSize = false;
+ }
+ */
+}
+// Appends a character
+void StringBuffer::AppendChar(UInt32 ch)
+{
+ char buff[8];
+ UPInt origSize = GetSize();
+
+ // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes
+ // in the UTF8 string.
+ SPInt srcSize = 0;
+ UTF8Util::EncodeChar(buff, &srcSize, ch);
+ OVR_ASSERT(srcSize >= 0);
+
+ UPInt size = origSize + srcSize;
+ Resize(size);
+ memcpy(pData + origSize, buff, srcSize);
+}
+
+// Append a string
+void StringBuffer::AppendString(const wchar_t* pstr, SPInt len)
+{
+ if (!pstr)
+ return;
+
+ SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len);
+ UPInt origSize = GetSize();
+ UPInt size = srcSize + origSize;
+
+ Resize(size);
+ UTF8Util::EncodeString(pData + origSize, pstr, len);
+}
+
+void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz)
+{
+ if (!putf8str || !utf8StrSz)
+ return;
+ if (utf8StrSz == -1)
+ utf8StrSz = (SPInt)OVR_strlen(putf8str);
+
+ UPInt origSize = GetSize();
+ UPInt size = utf8StrSz + origSize;
+
+ Resize(size);
+ memcpy(pData + origSize, putf8str, utf8StrSz);
+}
+
+
+void StringBuffer::operator = (const char* pstr)
+{
+ pstr = pstr ? pstr : "";
+ UPInt size = OVR_strlen(pstr);
+ Resize(size);
+ memcpy(pData, pstr, size);
+}
+
+void StringBuffer::operator = (const wchar_t* pstr)
+{
+ pstr = pstr ? pstr : L"";
+ UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr);
+ Resize(size);
+ UTF8Util::EncodeString(pData, pstr);
+}
+
+void StringBuffer::operator = (const String& src)
+{
+ Resize(src.GetSize());
+ memcpy(pData, src.ToCStr(), src.GetSize());
+}
+
+void StringBuffer::operator = (const StringBuffer& src)
+{
+ Clear();
+ AppendString(src.ToCStr(), src.GetSize());
+}
+
+
+// Inserts substr at posAt
+void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len)
+{
+ UPInt oldSize = Size;
+ UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len;
+ UPInt byteIndex = LengthIsSize ? posAt :
+ (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size);
+
+ OVR_ASSERT(byteIndex <= oldSize);
+ Reserve(oldSize + insertSize);
+
+ memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1);
+ memcpy (pData + byteIndex, substr, insertSize);
+ LengthIsSize = false;
+ Size = oldSize + insertSize;
+ pData[Size] = 0;
+}
+
+// Inserts character at posAt
+UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt)
+{
+ char buf[8];
+ SPInt len = 0;
+ UTF8Util::EncodeChar(buf, &len, c);
+ OVR_ASSERT(len >= 0);
+ buf[(UPInt)len] = 0;
+
+ Insert(buf, posAt, len);
+ return (UPInt)len;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String.h b/LibOVR/Src/Kernel/OVR_String.h
new file mode 100644
index 0000000..0866968
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String.h
@@ -0,0 +1,657 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_String.h
+Content : String UTF8 string implementation with copy-on-write semantics
+ (thread-safe for assignment but not modification).
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_String_h
+#define OVR_String_h
+
+#include "OVR_Types.h"
+#include "OVR_Allocator.h"
+#include "OVR_UTF8Util.h"
+#include "OVR_Atomic.h"
+#include "OVR_Std.h"
+#include "OVR_Alg.h"
+
+namespace OVR {
+
+// ***** Classes
+
+class String;
+class StringBuffer;
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Class
+
+// String is UTF8 based string class with copy-on-write implementation
+// for assignment.
+
+class String
+{
+protected:
+
+ enum FlagConstants
+ {
+ //Flag_GetLength = 0x7FFFFFFF,
+ // This flag is set if GetLength() == GetSize() for a string.
+ // Avoid extra scanning is Substring and indexing logic.
+ Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1)
+ };
+
+
+ // Internal structure to hold string data
+ struct DataDesc
+ {
+ // Number of bytes. Will be the same as the number of chars if the characters
+ // are ascii, may not be equal to number of chars in case string data is UTF8.
+ UPInt Size;
+ volatile SInt32 RefCount;
+ char Data[1];
+
+ void AddRef()
+ {
+ AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, 1);
+ }
+ // Decrement ref count. This needs to be thread-safe, since
+ // a different thread could have also decremented the ref count.
+ // For example, if u start off with a ref count = 2. Now if u
+ // decrement the ref count and check against 0 in different
+ // statements, a different thread can also decrement the ref count
+ // in between our decrement and checking against 0 and will find
+ // the ref count = 0 and delete the object. This will lead to a crash
+ // when context switches to our thread and we'll be trying to delete
+ // an already deleted object. Hence decrementing the ref count and
+ // checking against 0 needs to made an atomic operation.
+ void Release()
+ {
+ if ((AtomicOps<SInt32>::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0)
+ OVR_FREE(this);
+ }
+
+ static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; }
+ UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; }
+ UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); }
+ bool LengthIsSize() const { return GetLengthFlag() != 0; }
+ };
+
+ // Heap type of the string is encoded in the lower bits.
+ enum HeapType
+ {
+ HT_Global = 0, // Heap is global.
+ HT_Local = 1, // SF::String_loc: Heap is determined based on string's address.
+ HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class.
+ HT_Mask = 3
+ };
+
+ union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ };
+ typedef union {
+ DataDesc* pData;
+ UPInt HeapTypeBits;
+ } DataDescUnion;
+
+ inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); }
+
+ inline DataDesc* GetData() const
+ {
+ DataDescUnion u;
+ u.pData = pData;
+ u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask);
+ return u.pData;
+ }
+
+ inline void SetData(DataDesc* pdesc)
+ {
+ HeapType ht = GetHeapType();
+ pData = pdesc;
+ OVR_ASSERT((HeapTypeBits & HT_Mask) == 0);
+ HeapTypeBits |= ht;
+ }
+
+
+ DataDesc* AllocData(UPInt size, UPInt lengthIsSize);
+ DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize,
+ const char* pdata, UPInt copySize);
+ DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize,
+ const char* pdata1, UPInt copySize1,
+ const char* pdata2, UPInt copySize2);
+
+ // Special constructor to avoid data initalization when used in derived class.
+ struct NoConstructor { };
+ String(const NoConstructor&) { }
+
+public:
+
+ // For initializing string with dynamic buffer
+ struct InitStruct
+ {
+ virtual ~InitStruct() { }
+ virtual void InitString(char* pbuffer, UPInt size) const = 0;
+ };
+
+
+ // Constructors / Destructors.
+ String();
+ String(const char* data);
+ String(const char* data1, const char* pdata2, const char* pdata3 = 0);
+ String(const char* data, UPInt buflen);
+ String(const String& src);
+ String(const StringBuffer& src);
+ String(const InitStruct& src, UPInt size);
+ explicit String(const wchar_t* data);
+
+ // Destructor (Captain Obvious guarantees!)
+ ~String()
+ {
+ GetData()->Release();
+ }
+
+ // Declaration of NullString
+ static DataDesc NullData;
+
+
+ // *** General Functions
+
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return GetData()->Data; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return GetData()->Data; }
+
+ // Returns number of bytes
+ UPInt GetSize() const { return GetData()->GetSize() ; }
+ // Tells whether or not the string is empty
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ void AssignString(const InitStruct& src, UPInt size);
+ // Assigns string with known size.
+ void AssignString(const char* putf8str, UPInt size);
+
+ // Resize the string to the new size
+// void Resize(UPInt _size);
+
+ // Removes the character at posAt
+ void Remove(UPInt posAt, SPInt len = 1);
+
+ // Returns a String that's a substring of this.
+ // -start is the index of the first UTF8 character you want to include.
+ // -end is the index one past the last UTF8 character you want to include.
+ String Substring(UPInt start, UPInt end) const;
+
+ // Case-conversion
+ String ToUpper() const;
+ String ToLower() const;
+
+ // Inserts substr at posAt
+ String& Insert (const char* substr, UPInt posAt, SPInt len = -1);
+
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Inserts substr at posAt, which is an index of a character (not byte).
+ // Of size is specified, it is in bytes.
+// String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1);
+
+ // Get Byte index of the character at position = index
+ UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); }
+
+ // Utility: case-insensitive string compare. stricmp() & strnicmp() are not
+ // ANSI or POSIX, do not seem to appear in Linux.
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b);
+ static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len);
+
+ // Hash function, case-insensitive
+ static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+ // Hash function, case-sensitive
+ static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381);
+
+
+ // ***** File path parsing helper functions.
+ // Implemented in OVR_String_FilePath.cpp.
+
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+ static bool HasAbsolutePath(const char* path);
+ static bool HasExtension(const char* path);
+ static bool HasProtocol(const char* path);
+
+ bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); }
+ bool HasExtension() const { return HasExtension(ToCStr()); }
+ bool HasProtocol() const { return HasProtocol(ToCStr()); }
+
+ String GetProtocol() const; // Returns protocol, if any, with trailing '://'.
+ String GetPath() const; // Returns path with trailing '/'.
+ String GetFilename() const; // Returns filename, including extension.
+ String GetExtension() const; // Returns extension with a dot.
+
+ void StripProtocol(); // Strips front protocol, if any, from the string.
+ void StripExtension(); // Strips off trailing extension.
+
+
+ // Operators
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+ void operator = (const StringBuffer& src);
+
+ // Addition
+ void operator += (const String& src);
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ String operator + (const char* str) const;
+ String operator + (const String& src) const;
+
+ // Comparison
+ bool operator == (const String& str) const
+ {
+ return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0);
+ }
+
+ bool operator != (const String& str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator == (const char* str) const
+ {
+ return OVR_strcmp(GetData()->Data, str) == 0;
+ }
+
+ bool operator != (const char* str) const
+ {
+ return !operator == (str);
+ }
+
+ bool operator < (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) < 0;
+ }
+
+ bool operator < (const String& str) const
+ {
+ return *this < str.GetData()->Data;
+ }
+
+ bool operator > (const char* pstr) const
+ {
+ return OVR_strcmp(GetData()->Data, pstr) > 0;
+ }
+
+ bool operator > (const String& str) const
+ {
+ return *this > str.GetData()->Data;
+ }
+
+ int CompareNoCase(const char* pstr) const
+ {
+ return CompareNoCase(GetData()->Data, pstr);
+ }
+ int CompareNoCase(const String& str) const
+ {
+ return CompareNoCase(GetData()->Data, str.ToCStr());
+ }
+
+ // Accesses raw bytes
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(index >= 0 && (UPInt)index < GetSize());
+ return GetData()->Data[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return GetData()->Data[index];
+ }
+
+
+ // Case insensitive keys are used to look up insensitive string in hash tables
+ // for SWF files with version before SWF 7.
+ struct NoCaseKey
+ {
+ const String* pStr;
+ NoCaseKey(const String &str) : pStr(&str){};
+ };
+
+ bool operator == (const NoCaseKey& strKey) const
+ {
+ return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+ bool operator != (const NoCaseKey& strKey) const
+ {
+ return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0);
+ }
+
+ // Hash functor used for strings.
+ struct HashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunction((const char*)data, size);
+ }
+ };
+ // Case-insensitive hash functor used for strings. Supports additional
+ // lookup based on NoCaseKey.
+ struct NoCaseHashFunctor
+ {
+ UPInt operator()(const String& data) const
+ {
+ UPInt size = data.GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data, size);
+ }
+ UPInt operator()(const NoCaseKey& data) const
+ {
+ UPInt size = data.pStr->GetSize();
+ return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size);
+ }
+ };
+
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** String Buffer used for Building Strings
+
+class StringBuffer
+{
+ char* pData;
+ UPInt Size;
+ UPInt BufferSize;
+ UPInt GrowSize;
+ mutable bool LengthIsSize;
+
+public:
+
+ // Constructors / Destructor.
+ StringBuffer();
+ explicit StringBuffer(UPInt growSize);
+ StringBuffer(const char* data);
+ StringBuffer(const char* data, UPInt buflen);
+ StringBuffer(const String& src);
+ StringBuffer(const StringBuffer& src);
+ explicit StringBuffer(const wchar_t* data);
+ ~StringBuffer();
+
+
+ // Modify grow size used for growing/shrinking the buffer.
+ UPInt GetGrowSize() const { return GrowSize; }
+ void SetGrowSize(UPInt growSize);
+
+
+ // *** General Functions
+ // Does not release memory, just sets Size to 0
+ void Clear();
+
+ // For casting to a pointer to char.
+ operator const char*() const { return (pData) ? pData : ""; }
+ // Pointer to raw buffer.
+ const char* ToCStr() const { return (pData) ? pData : ""; }
+
+ // Returns number of bytes.
+ UPInt GetSize() const { return Size ; }
+ // Tells whether or not the string is empty.
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // Returns number of characters
+ UPInt GetLength() const;
+
+ // Returns character at the specified index
+ UInt32 GetCharAt(UPInt index) const;
+ UInt32 GetFirstCharAt(UPInt index, const char** offset) const;
+ UInt32 GetNextChar(const char** offset) const;
+
+
+ // Resize the string to the new size
+ void Resize(UPInt _size);
+ void Reserve(UPInt _size);
+
+ // Appends a character
+ void AppendChar(UInt32 ch);
+
+ // Append a string
+ void AppendString(const wchar_t* pstr, SPInt len = -1);
+ void AppendString(const char* putf8str, SPInt utf8StrSz = -1);
+ void AppendFormat(const char* format, ...);
+
+ // Assigned a string with dynamic data (copied through initializer).
+ //void AssignString(const InitStruct& src, UPInt size);
+
+ // Inserts substr at posAt
+ void Insert (const char* substr, UPInt posAt, SPInt len = -1);
+ // Inserts character at posAt
+ UPInt InsertCharAt(UInt32 c, UPInt posAt);
+
+ // Assignment
+ void operator = (const char* str);
+ void operator = (const wchar_t* str);
+ void operator = (const String& src);
+ void operator = (const StringBuffer& src);
+
+ // Addition
+ void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); }
+ void operator += (const char* psrc) { AppendString(psrc); }
+ void operator += (const wchar_t* psrc) { AppendString(psrc); }
+ void operator += (char ch) { AppendChar(ch); }
+ //String operator + (const char* str) const ;
+ //String operator + (const String& src) const ;
+
+ // Accesses raw bytes
+ char& operator [] (int index)
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ char& operator [] (UPInt index)
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+
+ const char& operator [] (int index) const
+ {
+ OVR_ASSERT(((UPInt)index) < GetSize());
+ return pData[index];
+ }
+ const char& operator [] (UPInt index) const
+ {
+ OVR_ASSERT(index < GetSize());
+ return pData[index];
+ }
+};
+
+
+//
+// Wrapper for string data. The data must have a guaranteed
+// lifespan throughout the usage of the wrapper. Not intended for
+// cached usage. Not thread safe.
+//
+class StringDataPtr
+{
+public:
+ StringDataPtr() : pStr(NULL), Size(0) {}
+ StringDataPtr(const StringDataPtr& p)
+ : pStr(p.pStr), Size(p.Size) {}
+ StringDataPtr(const char* pstr, UPInt sz)
+ : pStr(pstr), Size(sz) {}
+ StringDataPtr(const char* pstr)
+ : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {}
+ explicit StringDataPtr(const String& str)
+ : pStr(str.ToCStr()), Size(str.GetSize()) {}
+ template <typename T, int N>
+ StringDataPtr(const T (&v)[N])
+ : pStr(v), Size(N) {}
+
+public:
+ const char* ToCStr() const { return pStr; }
+ UPInt GetSize() const { return Size; }
+ bool IsEmpty() const { return GetSize() == 0; }
+
+ // value is a prefix of this string
+ // Character's values are not compared.
+ bool IsPrefix(const StringDataPtr& value) const
+ {
+ return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize();
+ }
+ // value is a suffix of this string
+ // Character's values are not compared.
+ bool IsSuffix(const StringDataPtr& value) const
+ {
+ return ToCStr() <= value.ToCStr() && (End()) == (value.End());
+ }
+
+ // Find first character.
+ // init_ind - initial index.
+ SPInt FindChar(char c, UPInt init_ind = 0) const
+ {
+ for (UPInt i = init_ind; i < GetSize(); ++i)
+ if (pStr[i] == c)
+ return static_cast<SPInt>(i);
+
+ return -1;
+ }
+
+ // Find last character.
+ // init_ind - initial index.
+ SPInt FindLastChar(char c, UPInt init_ind = ~0) const
+ {
+ if (init_ind == (UPInt)~0 || init_ind > GetSize())
+ init_ind = GetSize();
+ else
+ ++init_ind;
+
+ for (UPInt i = init_ind; i > 0; --i)
+ if (pStr[i - 1] == c)
+ return static_cast<SPInt>(i - 1);
+
+ return -1;
+ }
+
+ // Create new object and trim size bytes from the left.
+ StringDataPtr GetTrimLeft(UPInt size) const
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr() + size, GetSize() - size);
+ }
+ // Create new object and trim size bytes from the right.
+ StringDataPtr GetTrimRight(UPInt size) const
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+
+ return StringDataPtr(ToCStr(), GetSize() - size);
+ }
+
+ // Create new object, which contains next token.
+ // Useful for parsing.
+ StringDataPtr GetNextToken(char separator = ':') const
+ {
+ UPInt cur_pos = 0;
+ const char* cur_str = ToCStr();
+
+ for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos)
+ {
+ if (cur_str[cur_pos] == separator)
+ {
+ break;
+ }
+ }
+
+ return StringDataPtr(ToCStr(), cur_pos);
+ }
+
+ // Trim size bytes from the left.
+ StringDataPtr& TrimLeft(UPInt size)
+ {
+ // Limit trim size to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ pStr += size;
+ Size -= size;
+
+ return *this;
+ }
+ // Trim size bytes from the right.
+ StringDataPtr& TrimRight(UPInt size)
+ {
+ // Limit trim to the size of the string.
+ size = Alg::PMin(GetSize(), size);
+ Size -= size;
+
+ return *this;
+ }
+
+ const char* Begin() const { return ToCStr(); }
+ const char* End() const { return ToCStr() + GetSize(); }
+
+ // Hash functor used string data pointers
+ struct HashFunctor
+ {
+ UPInt operator()(const StringDataPtr& data) const
+ {
+ return String::BernsteinHashFunction(data.ToCStr(), data.GetSize());
+ }
+ };
+
+ bool operator== (const StringDataPtr& data) const
+ {
+ return (OVR_strncmp(pStr, data.pStr, data.Size) == 0);
+ }
+
+protected:
+ const char* pStr;
+ UPInt Size;
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_StringHash.h b/LibOVR/Src/Kernel/OVR_StringHash.h
new file mode 100644
index 0000000..baa80a7
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_StringHash.h
@@ -0,0 +1,100 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_StringHash.h
+Content : String hash table used when optional case-insensitive
+ lookup is required.
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_StringHash_h
+#define OVR_StringHash_h
+
+#include "OVR_String.h"
+#include "OVR_Hash.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// *** StringHash
+
+// This is a custom string hash table that supports case-insensitive
+// searches through special functions such as GetCaseInsensitive, etc.
+// This class is used for Flash labels, exports and other case-insensitive tables.
+
+template<class U, class Allocator = ContainerAllocator<U> >
+class StringHash : public Hash<String, U, String::NoCaseHashFunctor, Allocator>
+{
+public:
+ typedef U ValueType;
+ typedef StringHash<U, Allocator> SelfType;
+ typedef Hash<String, U, String::NoCaseHashFunctor, Allocator> BaseType;
+
+public:
+
+ void operator = (const SelfType& src) { BaseType::operator = (src); }
+
+ bool GetCaseInsensitive(const String& key, U* pvalue) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey, pvalue);
+ }
+ // Pointer-returning get variety.
+ const U* GetCaseInsensitive(const String& key) const
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+ U* GetCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::GetAlt(ikey);
+ }
+
+
+ typedef typename BaseType::Iterator base_iterator;
+
+ base_iterator FindCaseInsensitive(const String& key)
+ {
+ String::NoCaseKey ikey(key);
+ return BaseType::FindAlt(ikey);
+ }
+
+ // Set just uses a find and assigns value if found. The key is not modified;
+ // this behavior is identical to Flash string variable assignment.
+ void SetCaseInsensitive(const String& key, const U& value)
+ {
+ base_iterator it = FindCaseInsensitive(key);
+ if (it != BaseType::End())
+ {
+ it->Second = value;
+ }
+ else
+ {
+ BaseType::Add(key, value);
+ }
+ }
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp
new file mode 100644
index 0000000..e196dd7
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp
@@ -0,0 +1,53 @@
+/************************************************************************************
+
+Filename : OVR_String_FormatUtil.cpp
+Content : String format functions.
+Created : February 27, 2013
+Notes :
+
+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_String.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+void StringBuffer::AppendFormat(const char* format, ...)
+{
+ va_list argList;
+
+ va_start(argList, format);
+ UPInt size = OVR_vscprintf(format, argList);
+ va_end(argList);
+
+ char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1));
+
+ va_start(argList, format);
+ UPInt result = OVR_vsprintf(buffer, size+1, format, argList);
+ OVR_UNUSED1(result);
+ va_end(argList);
+ OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf"));
+
+ AppendString(buffer);
+
+ OVR_FREE(buffer);
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp
new file mode 100644
index 0000000..02abe15
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp
@@ -0,0 +1,211 @@
+/************************************************************************************
+
+Filename : OVR_String_PathUtil.cpp
+Content : String filename/url helper function
+Created : September 19, 2012
+Notes :
+
+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_String.h"
+#include "OVR_UTF8Util.h"
+
+namespace OVR {
+
+//--------------------------------------------------------------------
+// ***** Path-Scanner helper function
+
+// Scans file path finding filename start and extension start, fills in their addess.
+void ScanFilePath(const char* url, const char** pfilename, const char** pext)
+{
+ const char* urlStart = url;
+ const char *filename = 0;
+ const char *lastDot = 0;
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ while (charVal != 0)
+ {
+ if ((charVal == '/') || (charVal == '\\'))
+ {
+ filename = url;
+ lastDot = 0;
+ }
+ else if (charVal == '.')
+ {
+ lastDot = url - 1;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ if (pfilename)
+ {
+ // It was a naked filename
+ if (urlStart && (*urlStart != '.') && *urlStart)
+ *pfilename = urlStart;
+ else
+ *pfilename = filename;
+ }
+
+ if (pext)
+ {
+ *pext = lastDot;
+ }
+}
+
+// Scans till the end of protocol. Returns first character past protocol,
+// 0 if not found.
+// - protocol: 'file://', 'http://'
+const char* ScanPathProtocol(const char* url)
+{
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+ UInt32 charVal2;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal2 = UTF8Util::DecodeNextChar(&url);
+ charVal = UTF8Util::DecodeNextChar(&url);
+ if ((charVal == '/') && (charVal2 == '\\'))
+ return url;
+ }
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+ return 0;
+}
+
+
+//--------------------------------------------------------------------
+// ***** String Path API implementation
+
+bool String::HasAbsolutePath(const char* url)
+{
+ // Absolute paths can star with:
+ // - protocols: 'file://', 'http://'
+ // - windows drive: 'c:\'
+ // - UNC share name: '\\share'
+ // - unix root '/'
+
+ // On the other hand, relative paths are:
+ // - directory: 'directory/file'
+ // - this directory: './file'
+ // - parent directory: '../file'
+ //
+ // For now, we don't parse '.' or '..' out, but instead let it be concatenated
+ // to string and let the OS figure it out. This, however, is not good for file
+ // name matching in library/etc, so it should be improved.
+
+ if (!url || !*url)
+ return true; // Treat empty strings as absolute.
+
+ UInt32 charVal = UTF8Util::DecodeNextChar(&url);
+
+ // Fist character of '/' or '\\' means absolute url.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+
+ while (charVal != 0)
+ {
+ // Treat a colon followed by a slash as absolute.
+ if (charVal == ':')
+ {
+ charVal = UTF8Util::DecodeNextChar(&url);
+ // Protocol or windows drive. Absolute.
+ if ((charVal == '/') || (charVal == '\\'))
+ return true;
+ }
+ else if ((charVal == '/') || (charVal == '\\'))
+ {
+ // Not a first character (else 'if' above the loop would have caught it).
+ // Must be a relative url.
+ break;
+ }
+
+ charVal = UTF8Util::DecodeNextChar(&url);
+ }
+
+ // We get here for relative paths.
+ return false;
+}
+
+
+bool String::HasExtension(const char* path)
+{
+ const char* ext = 0;
+ ScanFilePath(path, 0, &ext);
+ return ext != 0;
+}
+bool String::HasProtocol(const char* path)
+{
+ return ScanPathProtocol(path) != 0;
+}
+
+
+String String::GetPath() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+
+ // Technically we can have extra logic somewhere for paths,
+ // such as enforcing protocol and '/' only based on flags,
+ // but we keep it simple for now.
+ return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize());
+}
+
+String String::GetProtocol() const
+{
+ const char* protocolEnd = ScanPathProtocol(ToCStr());
+ return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0);
+}
+
+String String::GetFilename() const
+{
+ const char* filename = 0;
+ ScanFilePath(ToCStr(), &filename, 0);
+ return String(filename);
+}
+String String::GetExtension() const
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ return String(ext);
+}
+
+void String::StripExtension()
+{
+ const char* ext = 0;
+ ScanFilePath(ToCStr(), 0, &ext);
+ if (ext)
+ {
+ *this = String(ToCStr(), ext-ToCStr());
+ }
+}
+
+void String::StripProtocol()
+{
+ const char* protocol = ScanPathProtocol(ToCStr());
+ if (protocol)
+ AssignString(protocol, OVR_strlen(protocol));
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.cpp b/LibOVR/Src/Kernel/OVR_SysFile.cpp
new file mode 100644
index 0000000..604527a
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_SysFile.cpp
@@ -0,0 +1,138 @@
+/**************************************************************************
+
+Filename : OVR_SysFile.cpp
+Content : File wrapper class implementation (Win32)
+
+Created : April 5, 1999
+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.
+
+**************************************************************************/
+
+#define GFILE_CXX
+
+// Standard C library (Captain Obvious guarantees!)
+#include <stdio.h>
+
+#include "OVR_SysFile.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+// This is - a dummy file that fails on all calls.
+
+class UnopenedFile : public File
+{
+public:
+ UnopenedFile() { }
+ ~UnopenedFile() { }
+
+ virtual const char* GetFilePath() { return 0; }
+
+ // ** File Information
+ virtual bool IsValid() { return 0; }
+ virtual bool IsWritable() { return 0; }
+
+ // Return position / file size
+ virtual int Tell() { return 0; }
+ virtual SInt64 LTell() { return 0; }
+ virtual int GetLength() { return 0; }
+ virtual SInt64 LGetLength() { return 0; }
+
+// virtual bool Stat(FileStats *pfs) { return 0; }
+ virtual int GetErrorCode() { return Error_FileNotFound; }
+
+ // ** Stream implementation & I/O
+ virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); }
+ virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); }
+ virtual int BytesAvailable() { return 0; }
+ virtual bool Flush() { return 0; }
+ virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+ virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); }
+
+ virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); }
+ virtual bool Close() { return 0; }
+};
+
+
+
+// ***** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path
+
+// ** Constructor
+SysFile::SysFile() : DelegatedFile(0)
+{
+ pFile = *new UnopenedFile;
+}
+
+Ptr<File> FileFILEOpen(const String& path, int flags, int mode);
+
+// Opens a file
+SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0)
+{
+ Open(path, flags, mode);
+}
+
+
+// ** Open & management
+// Will fail if file's already open
+bool SysFile::Open(const String& path, int flags, int mode)
+{
+ pFile = FileFILEOpen(path, flags, mode);
+ if ((!pFile) || (!pFile->IsValid()))
+ {
+ pFile = *new UnopenedFile;
+ OVR_DEBUG_LOG(("Failed to open file: %s", path.ToCStr()));
+ return 0;
+ }
+ //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing
+ if (flags & Open_Buffered)
+ pFile = *new BufferedFile(pFile);
+ return 1;
+}
+
+
+// ** Overrides
+
+int SysFile::GetErrorCode()
+{
+ return pFile ? pFile->GetErrorCode() : Error_FileNotFound;
+}
+
+
+// Overrides to provide re-open support
+bool SysFile::IsValid()
+{
+ return pFile && pFile->IsValid();
+}
+bool SysFile::Close()
+{
+ if (IsValid())
+ {
+ DelegatedFile::Close();
+ pFile = *new UnopenedFile;
+ return 1;
+ }
+ return 0;
+}
+
+} // OVR
diff --git a/LibOVR/Src/Kernel/OVR_SysFile.h b/LibOVR/Src/Kernel/OVR_SysFile.h
new file mode 100644
index 0000000..61ad6e8
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_SysFile.h
@@ -0,0 +1,104 @@
+/************************************************************************************
+
+PublicHeader: Kernel
+Filename : OVR_SysFile.h
+Content : Header for all internal file management - functions and structures
+ to be inherited by OS specific subclasses.
+Created : September 19, 2012
+Notes :
+
+Notes : errno may not be preserved across use of GBaseFile member functions
+ : Directories cannot be deleted while files opened from them are in use
+ (For the GetFullName function)
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_SysFile_h
+#define OVR_SysFile_h
+
+#include "OVR_File.h"
+
+namespace OVR {
+
+// ***** Declared classes
+class SysFile;
+
+//-----------------------------------------------------------------------------------
+// *** File Statistics
+
+// This class contents are similar to _stat, providing
+// creation, modify and other information about the file.
+struct FileStat
+{
+ // No change or create time because they are not available on most systems
+ SInt64 ModifyTime;
+ SInt64 AccessTime;
+ SInt64 FileSize;
+
+ bool operator== (const FileStat& stat) const
+ {
+ return ( (ModifyTime == stat.ModifyTime) &&
+ (AccessTime == stat.AccessTime) &&
+ (FileSize == stat.FileSize) );
+ }
+};
+
+//-----------------------------------------------------------------------------------
+// *** System File
+
+// System file is created to access objects on file system directly
+// This file can refer directly to path.
+// System file can be open & closed several times; however, such use is not recommended
+// This class is realy a wrapper around an implementation of File interface for a
+// particular platform.
+
+class SysFile : public DelegatedFile
+{
+protected:
+ SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); }
+public:
+
+ // ** Constructor
+ SysFile();
+ // Opens a file
+ SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ // ** Open & management
+ bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite);
+
+ OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite)
+ { return Open(path, Open_ReadWrite|Open_Create, mode); }
+
+ // Helper function: obtain file statistics information. In OVR, this is used to detect file changes.
+ // Return 0 if function failed, most likely because the file doesn't exist.
+ static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path);
+
+ // ** Overrides
+ // Overridden to provide re-open support
+ virtual int GetErrorCode();
+
+ virtual bool IsValid();
+
+ virtual bool Close();
+};
+
+} // Namespace OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_System.cpp b/LibOVR/Src/Kernel/OVR_System.cpp
new file mode 100644
index 0000000..3144ade
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_System.cpp
@@ -0,0 +1,81 @@
+/************************************************************************************
+
+Filename : OVR_System.cpp
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+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_System.h"
+#include "OVR_Threads.h"
+#include "OVR_Timer.h"
+
+namespace OVR {
+
+// ***** OVR::System Implementation
+
+// Initializes System core, installing allocator.
+void System::Init(Log* log, Allocator *palloc)
+{
+ if (!Allocator::GetInstance())
+ {
+ Log::SetGlobalLog(log);
+ Timer::initializeTimerSystem();
+ Allocator::setInstance(palloc);
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Init failed - duplicate call."));
+ }
+}
+
+void System::Destroy()
+{
+ if (Allocator::GetInstance())
+ {
+ // Wait for all threads to finish; this must be done so that memory
+ // allocator and all destructors finalize correctly.
+#ifdef OVR_ENABLE_THREADS
+ Thread::FinishAllThreads();
+#endif
+
+ // Shutdown heap and destroy SysAlloc singleton, if any.
+ Allocator::GetInstance()->onSystemShutdown();
+ Allocator::setInstance(0);
+
+ Timer::shutdownTimerSystem();
+ Log::SetGlobalLog(Log::GetDefaultLog());
+ }
+ else
+ {
+ OVR_DEBUG_LOG(("System::Destroy failed - System not initialized."));
+ }
+}
+
+// Returns 'true' if system was properly initialized.
+bool System::IsInitialized()
+{
+ return Allocator::GetInstance() != 0;
+}
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_System.h b/LibOVR/Src/Kernel/OVR_System.h
new file mode 100644
index 0000000..253fe19
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_System.h
@@ -0,0 +1,78 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_System.h
+Content : General kernel initialization/cleanup, including that
+ of the memory allocator.
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_System_h
+#define OVR_System_h
+
+#include "OVR_Allocator.h"
+#include "OVR_Log.h"
+
+namespace OVR {
+
+// ***** System Core Initialization class
+
+// System initialization must take place before any other OVR_Kernel objects are used;
+// this is done my calling System::Init(). Among other things, this is necessary to
+// initialize the memory allocator. Similarly, System::Destroy must be
+// called before program exist for proper cleanup. Both of these tasks can be achieved by
+// simply creating System object first, allowing its constructor/destructor do the work.
+
+// TBD: Require additional System class for Oculus Rift API?
+
+class System
+{
+public:
+
+ // System constructor expects allocator to be specified, if it is being substituted.
+ System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator* palloc = DefaultAllocator::InitSystemSingleton())
+ {
+ Init(log, palloc);
+ }
+
+ ~System()
+ {
+ Destroy();
+ }
+
+ // Returns 'true' if system was properly initialized.
+ static bool OVR_CDECL IsInitialized();
+
+ // Initializes System core. Users can override memory implementation by passing
+ // a different Allocator here.
+ static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug),
+ Allocator *palloc = DefaultAllocator::InitSystemSingleton());
+
+ // De-initializes System more, finalizing the threading system and destroying
+ // the global memory allocator.
+ static void OVR_CDECL Destroy();
+};
+
+} // OVR
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Threads.h b/LibOVR/Src/Kernel/OVR_Threads.h
new file mode 100644
index 0000000..307f107
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Threads.h
@@ -0,0 +1,407 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Threads.h
+Content : Contains thread-related (safe) functionality
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+#ifndef OVR_Threads_h
+#define OVR_Threads_h
+
+#include "OVR_Types.h"
+#include "OVR_Atomic.h"
+#include "OVR_RefCount.h"
+#include "OVR_Array.h"
+
+// Defines the infinite wait delay timeout
+#define OVR_WAIT_INFINITE 0xFFFFFFFF
+
+// To be defined in the project configuration options
+#ifdef OVR_ENABLE_THREADS
+
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ****** Declared classes
+
+// Declared with thread support only
+class Mutex;
+class WaitCondition;
+class Event;
+// Implementation forward declarations
+class MutexImpl;
+class WaitConditionImpl;
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Mutex
+
+// Mutex class represents a system Mutex synchronization object that provides access
+// serialization between different threads, allowing one thread mutually exclusive access
+// to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition.
+
+class Mutex
+{
+ friend class WaitConditionImpl;
+ friend class MutexImpl;
+
+ MutexImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ Mutex(bool recursive = 1);
+ ~Mutex();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock();
+
+ // Returns 1 if the mutes is currently locked by another thread
+ // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired.
+ bool IsLockedByAnotherThread();
+
+ // Locker class; Used for automatic locking of a mutex withing scope
+ class Locker
+ {
+ public:
+ Mutex *pMutex;
+ Locker(Mutex *pmutex)
+ { pMutex = pmutex; pMutex->DoLock(); }
+ ~Locker()
+ { pMutex->Unlock(); }
+ };
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** WaitCondition
+
+/*
+ WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor.
+ Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that
+ call Notify() or NotifyAll().
+
+ The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then
+ starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same
+ resource, this ensures that any condition checked for while the mutex was locked does not change before
+ the wait on the condition is actually initiated.
+*/
+
+class WaitCondition
+{
+ friend class WaitConditionImpl;
+ // Internal implementation structure
+ WaitConditionImpl *pImpl;
+
+public:
+ // Constructor/destructor
+ WaitCondition();
+ ~WaitCondition();
+
+ // Release mutex and wait for condition. The mutex is re-aquired after the wait.
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+// Event is a wait-able synchronization object similar to Windows event.
+// Event can be waited on until it's signaled by another thread calling
+// either SetEvent or PulseEvent.
+
+class Event
+{
+ // Event state, its mutex and the wait condition
+ volatile bool State;
+ volatile bool Temporary;
+ mutable Mutex StateMutex;
+ WaitCondition StateWaitCondition;
+
+ void updateState(bool newState, bool newTemp, bool mustNotify);
+
+public:
+ Event(bool setInitially = 0) : State(setInitially), Temporary(false) { }
+ ~Event() { }
+
+ // Wait on an event condition until it is set
+ // Delay is specified in milliseconds (1/1000 of a second).
+ bool Wait(unsigned delay = OVR_WAIT_INFINITE);
+
+ // Set an event, releasing objects waiting on it
+ void SetEvent()
+ { updateState(true, false, true); }
+
+ // Reset an event, un-signaling it
+ void ResetEvent()
+ { updateState(false, false, false); }
+
+ // Set and then reset an event once a waiter is released.
+ // If threads are already waiting, they will be notified and released
+ // If threads are not waiting, the event is set until the first thread comes in
+ void PulseEvent()
+ { updateState(true, true, true); }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Thread class
+
+// ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and
+// Thread::GetThreadId.
+typedef void* ThreadId;
+
+
+// *** Thread flags
+
+// Indicates that the thread is has been started, i.e. Start method has been called, and threads
+// OnExit() method has not yet been called/returned.
+#define OVR_THREAD_STARTED 0x01
+// This flag is set once the thread has ran, and finished.
+#define OVR_THREAD_FINISHED 0x02
+// This flag is set temporarily if this thread was started suspended. It is used internally.
+#define OVR_THREAD_START_SUSPENDED 0x08
+// This flag is used to ask a thread to exit. Message driven threads will usually check this flag
+// and finish once it is set.
+#define OVR_THREAD_EXIT 0x10
+
+
+class Thread : public RefCountBase<Thread>
+{ // NOTE: Waitable must be the first base since it implements RefCountImpl.
+
+public:
+
+ // *** Callback functions, can be used instead of overriding Run
+
+ // Run function prototypes.
+ // Thread function and user handle passed to it, executed by the default
+ // Thread::Run implementation if not null.
+ typedef int (*ThreadFn)(Thread *pthread, void* h);
+
+ // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried
+ ThreadFn ThreadFunction;
+ // User handle passes to a thread
+ void* UserHandle;
+
+ // Thread state to start a thread with
+ enum ThreadState
+ {
+ NotRunning = 0,
+ Running = 1,
+ Suspended = 2
+ };
+
+ // Thread priority
+ enum ThreadPriority
+ {
+ CriticalPriority,
+ HighestPriority,
+ AboveNormalPriority,
+ NormalPriority,
+ BelowNormalPriority,
+ LowestPriority,
+ IdlePriority,
+ };
+
+ // Thread constructor parameters
+ struct CreateParams
+ {
+ CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024,
+ int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority)
+ : threadFunction(func), userHandle(hand), stackSize(ssize),
+ processor(proc), initialState(state), priority(prior) {}
+ ThreadFn threadFunction; // Thread function
+ void* userHandle; // User handle passes to a thread
+ UPInt stackSize; // Thread stack size
+ int processor; // Thread hardware processor
+ ThreadState initialState; //
+ ThreadPriority priority; // Thread priority
+ };
+
+ // *** Constructors
+
+ // A default constructor always creates a thread in NotRunning state, because
+ // the derived class has not yet been initialized. The derived class can call Start explicitly.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(UPInt stackSize = 128 * 1024, int processor = -1);
+ // Constructors that initialize the thread with a pointer to function.
+ // An option to start a thread is available, but it should not be used if classes are derived from Thread.
+ // "processor" parameter specifies which hardware processor this thread will be run on.
+ // -1 means OS decides this. Implemented only on Win32
+ Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024,
+ int processor = -1, ThreadState initialState = NotRunning);
+ // Constructors that initialize the thread with a create parameters structure.
+ explicit Thread(const CreateParams& params);
+
+ // Destructor.
+ virtual ~Thread();
+
+ // Waits for all Threads to finish; should be called only from the root
+ // application thread. Once this function returns, we know that all other
+ // thread's references to Thread object have been released.
+ static void OVR_CDECL FinishAllThreads();
+
+
+ // *** Overridable Run function for thread processing
+
+ // - returning from this method will end the execution of the thread
+ // - return value is usually 0 for success
+ virtual int Run();
+ // Called after return/exit function
+ virtual void OnExit();
+
+
+ // *** Thread management
+
+ // Starts the thread if its not already running
+ // - internally sets up the threading and calls Run()
+ // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing
+ // - returns the exit code
+ virtual bool Start(ThreadState initialState = Running);
+
+ // Quits with an exit code
+ virtual void Exit(int exitCode=0);
+
+ // Suspend the thread until resumed
+ // Returns 1 for success, 0 for failure.
+ bool Suspend();
+ // Resumes currently suspended thread
+ // Returns 1 for success, 0 for failure.
+ bool Resume();
+
+ // Static function to return a pointer to the current thread
+ //static Thread* GetThread();
+
+
+ // *** Thread status query functions
+
+ bool GetExitFlag() const;
+ void SetExitFlag(bool exitFlag);
+
+ // Determines whether the thread was running and is now finished
+ bool IsFinished() const;
+ // Determines if the thread is currently suspended
+ bool IsSuspended() const;
+ // Returns current thread state
+ ThreadState GetThreadState() const;
+
+ // Returns the number of available CPUs on the system
+ static int GetCPUCount();
+
+ // Returns the thread exit code. Exit code is initialized to 0,
+ // and set to the return value if Run function after the thread is finished.
+ inline int GetExitCode() const { return ExitCode; }
+ // Returns an OS handle
+#if defined(OVR_OS_WIN32)
+ void* GetOSHandle() const { return ThreadHandle; }
+#else
+ pthread_t GetOSHandle() const { return ThreadHandle; }
+#endif
+
+#if defined(OVR_OS_WIN32)
+ ThreadId GetThreadId() const { return IdValue; }
+#else
+ ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); }
+#endif
+
+ static int GetOSPriority(ThreadPriority);
+ // *** Sleep
+
+ // Sleep secs seconds
+ static bool Sleep(unsigned secs);
+ // Sleep msecs milliseconds
+ static bool MSleep(unsigned msecs);
+
+
+ // *** Debugging functionality
+#if defined(OVR_OS_WIN32)
+ virtual void SetThreadName( const char* name );
+#else
+ virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); }
+#endif
+
+private:
+#if defined(OVR_OS_WIN32)
+ friend unsigned WINAPI Thread_Win32StartFn(void *pthread);
+
+#else
+ friend void *Thread_PthreadStartFn(void * phandle);
+
+ static int InitAttr;
+ static pthread_attr_t Attr;
+#endif
+
+protected:
+ // Thread state flags
+ AtomicInt<UInt32> ThreadFlags;
+ AtomicInt<SInt32> SuspendCount;
+ UPInt StackSize;
+
+ // Hardware processor which this thread is running on.
+ int Processor;
+ ThreadPriority Priority;
+
+#if defined(OVR_OS_WIN32)
+ void* ThreadHandle;
+ volatile ThreadId IdValue;
+
+ // System-specific cleanup function called from destructor
+ void CleanupSystemThread();
+
+#else
+ pthread_t ThreadHandle;
+#endif
+
+ // Exit code of the thread, as returned by Run.
+ int ExitCode;
+
+ // Internal run function.
+ int PRun();
+ // Finishes the thread and releases internal reference to it.
+ void FinishAndRelease();
+
+ void Init(const CreateParams& params);
+
+ // Protected copy constructor
+ Thread(const Thread &source) : RefCountBase<Thread>() { OVR_UNUSED(source); }
+
+};
+
+// Returns the unique Id of a thread it is called on, intended for
+// comparison purposes.
+ThreadId GetCurrentThreadId();
+
+
+} // OVR
+
+#endif // OVR_ENABLE_THREADS
+#endif // OVR_Threads_h
diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
new file mode 100644
index 0000000..da483d5
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp
@@ -0,0 +1,787 @@
+/************************************************************************************
+
+Filename : OVR_ThreadsPthread.cpp
+Content :
+Created :
+Notes :
+
+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_Threads.h"
+#include "OVR_Hash.h"
+
+#ifdef OVR_ENABLE_THREADS
+
+#include "OVR_Timer.h"
+#include "OVR_Log.h"
+
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+
+
+namespace OVR {
+
+// ***** Mutex implementation
+
+
+// *** Internal Mutex implementation structure
+
+class MutexImpl : public NewOverrideBase
+{
+ // System mutex or semaphore
+ pthread_mutex_t SMutex;
+ bool Recursive;
+ unsigned LockCount;
+ pthread_t LockedBy;
+
+ friend class WaitConditionImpl;
+
+public:
+ // Constructor/destructor
+ MutexImpl(Mutex* pmutex, bool recursive = 1);
+ ~MutexImpl();
+
+ // Locking functions
+ void DoLock();
+ bool TryLock();
+ void Unlock(Mutex* pmutex);
+ // Returns 1 if the mutes is currently locked
+ bool IsLockedByAnotherThread(Mutex* pmutex);
+ bool IsSignaled() const;
+};
+
+pthread_mutexattr_t Lock::RecursiveAttr;
+bool Lock::RecursiveAttrInit = 0;
+
+// *** Constructor/destructor
+MutexImpl::MutexImpl(Mutex* pmutex, bool recursive)
+{
+ OVR_UNUSED(pmutex);
+ Recursive = recursive;
+ LockCount = 0;
+
+ if (Recursive)
+ {
+ if (!Lock::RecursiveAttrInit)
+ {
+ pthread_mutexattr_init(&Lock::RecursiveAttr);
+ pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+ Lock::RecursiveAttrInit = 1;
+ }
+
+ pthread_mutex_init(&SMutex, &Lock::RecursiveAttr);
+ }
+ else
+ pthread_mutex_init(&SMutex, 0);
+}
+
+MutexImpl::~MutexImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+}
+
+
+// Lock and try lock
+void MutexImpl::DoLock()
+{
+ while (pthread_mutex_lock(&SMutex))
+ ;
+ LockCount++;
+ LockedBy = pthread_self();
+}
+
+bool MutexImpl::TryLock()
+{
+ if (!pthread_mutex_trylock(&SMutex))
+ {
+ LockCount++;
+ LockedBy = pthread_self();
+ return 1;
+ }
+
+ return 0;
+}
+
+void MutexImpl::Unlock(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0);
+
+ unsigned lockCount;
+ LockCount--;
+ lockCount = LockCount;
+
+ pthread_mutex_unlock(&SMutex);
+}
+
+bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex)
+{
+ OVR_UNUSED(pmutex);
+ // There could be multiple interpretations of IsLocked with respect to current thread
+ if (LockCount == 0)
+ return 0;
+ if (pthread_self() != LockedBy)
+ return 1;
+ return 0;
+}
+
+bool MutexImpl::IsSignaled() const
+{
+ // An mutex is signaled if it is not locked ANYWHERE
+ // Note that this is different from IsLockedByAnotherThread function,
+ // that takes current thread into account
+ return LockCount == 0;
+}
+
+
+// *** Actual Mutex class implementation
+
+Mutex::Mutex(bool recursive)
+{
+ // NOTE: RefCount mode already thread-safe for all waitables.
+ pImpl = new MutexImpl(this, recursive);
+}
+
+Mutex::~Mutex()
+{
+ delete pImpl;
+}
+
+// Lock and try lock
+void Mutex::DoLock()
+{
+ pImpl->DoLock();
+}
+bool Mutex::TryLock()
+{
+ return pImpl->TryLock();
+}
+void Mutex::Unlock()
+{
+ pImpl->Unlock(this);
+}
+bool Mutex::IsLockedByAnotherThread()
+{
+ return pImpl->IsLockedByAnotherThread(this);
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Event
+
+bool Event::Wait(unsigned delay)
+{
+ Mutex::Locker lock(&StateMutex);
+
+ // Do the correct amount of waiting
+ if (delay == OVR_WAIT_INFINITE)
+ {
+ while(!State)
+ StateWaitCondition.Wait(&StateMutex);
+ }
+ else if (delay)
+ {
+ if (!State)
+ StateWaitCondition.Wait(&StateMutex, delay);
+ }
+
+ bool state = State;
+ // Take care of temporary 'pulsing' of a state
+ if (Temporary)
+ {
+ Temporary = false;
+ State = false;
+ }
+ return state;
+}
+
+void Event::updateState(bool newState, bool newTemp, bool mustNotify)
+{
+ Mutex::Locker lock(&StateMutex);
+ State = newState;
+ Temporary = newTemp;
+ if (mustNotify)
+ StateWaitCondition.NotifyAll();
+}
+
+
+
+// ***** Wait Condition Implementation
+
+// Internal implementation class
+class WaitConditionImpl : public NewOverrideBase
+{
+ pthread_mutex_t SMutex;
+ pthread_cond_t Condv;
+
+public:
+
+ // Constructor/destructor
+ WaitConditionImpl();
+ ~WaitConditionImpl();
+
+ // Release mutex and wait for condition. The mutex is re-aqured after the wait.
+ bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE);
+
+ // Notify a condition, releasing at one object waiting
+ void Notify();
+ // Notify a condition, releasing all objects waiting
+ void NotifyAll();
+};
+
+
+WaitConditionImpl::WaitConditionImpl()
+{
+ pthread_mutex_init(&SMutex, 0);
+ pthread_cond_init(&Condv, 0);
+}
+
+WaitConditionImpl::~WaitConditionImpl()
+{
+ pthread_mutex_destroy(&SMutex);
+ pthread_cond_destroy(&Condv);
+}
+
+bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay)
+{
+ bool result = 1;
+ unsigned lockCount = pmutex->pImpl->LockCount;
+
+ // Mutex must have been locked
+ if (lockCount == 0)
+ return 0;
+
+ pthread_mutex_lock(&SMutex);
+
+ // Finally, release a mutex or semaphore
+ if (pmutex->pImpl->Recursive)
+ {
+ // Release the recursive mutex N times
+ pmutex->pImpl->LockCount = 0;
+ for(unsigned i=0; i<lockCount; i++)
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+ else
+ {
+ pmutex->pImpl->LockCount = 0;
+ pthread_mutex_unlock(&pmutex->pImpl->SMutex);
+ }
+
+ // Note that there is a gap here between mutex.Unlock() and Wait().
+ // The other mutex protects this gap.
+
+ if (delay == OVR_WAIT_INFINITE)
+ pthread_cond_wait(&Condv,&SMutex);
+ else
+ {
+ timespec ts;
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ ts.tv_sec = tv.tv_sec + (delay / 1000);
+ ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000;
+
+ if (ts.tv_nsec > 999999999)
+ {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ int r = pthread_cond_timedwait(&Condv,&SMutex, &ts);
+ OVR_ASSERT(r == 0 || r == ETIMEDOUT);
+ if (r)
+ result = 0;
+ }
+
+ pthread_mutex_unlock(&SMutex);
+
+ // Re-aquire the mutex
+ for(unsigned i=0; i<lockCount; i++)
+ pmutex->DoLock();
+
+ // Return the result
+ return result;
+}
+
+// Notify a condition, releasing the least object in a queue
+void WaitConditionImpl::Notify()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_signal(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+// Notify a condition, releasing all objects waiting
+void WaitConditionImpl::NotifyAll()
+{
+ pthread_mutex_lock(&SMutex);
+ pthread_cond_broadcast(&Condv);
+ pthread_mutex_unlock(&SMutex);
+}
+
+
+
+// *** Actual implementation of WaitCondition
+
+WaitCondition::WaitCondition()
+{
+ pImpl = new WaitConditionImpl;
+}
+WaitCondition::~WaitCondition()
+{
+ delete pImpl;
+}
+
+bool WaitCondition::Wait(Mutex *pmutex, unsigned delay)
+{
+ return pImpl->Wait(pmutex, delay);
+}
+// Notification
+void WaitCondition::Notify()
+{
+ pImpl->Notify();
+}
+void WaitCondition::NotifyAll()
+{
+ pImpl->NotifyAll();
+}
+
+
+// ***** Current thread
+
+// Per-thread variable
+/*
+static __thread Thread* pCurrentThread = 0;
+
+// Static function to return a pointer to the current thread
+void Thread::InitCurrentThread(Thread *pthread)
+{
+ pCurrentThread = pthread;
+}
+
+// Static function to return a pointer to the current thread
+Thread* Thread::GetThread()
+{
+ return pCurrentThread;
+}
+*/
+
+
+// *** Thread constructors.
+
+Thread::Thread(UPInt stackSize, int processor)
+{
+ // NOTE: RefCount mode already thread-safe for all Waitable objects.
+ CreateParams params;
+ params.stackSize = stackSize;
+ params.processor = processor;
+ Init(params);
+}
+
+Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize,
+ int processor, Thread::ThreadState initialState)
+{
+ CreateParams params(threadFunction, userHandle, stackSize, processor, initialState);
+ Init(params);
+}
+
+Thread::Thread(const CreateParams& params)
+{
+ Init(params);
+}
+
+void Thread::Init(const CreateParams& params)
+{
+ // Clear the variables
+ ThreadFlags = 0;
+ ThreadHandle = 0;
+ ExitCode = 0;
+ SuspendCount = 0;
+ StackSize = params.stackSize;
+ Processor = params.processor;
+ Priority = params.priority;
+
+ // Clear Function pointers
+ ThreadFunction = params.threadFunction;
+ UserHandle = params.userHandle;
+ if (params.initialState != NotRunning)
+ Start(params.initialState);
+}
+
+Thread::~Thread()
+{
+ // Thread should not running while object is being destroyed,
+ // this would indicate ref-counting issue.
+ //OVR_ASSERT(IsRunning() == 0);
+
+ // Clean up thread.
+ ThreadHandle = 0;
+}
+
+
+
+// *** Overridable User functions.
+
+// Default Run implementation
+int Thread::Run()
+{
+ // Call pointer to function, if available.
+ return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0;
+}
+void Thread::OnExit()
+{
+}
+
+
+// Finishes the thread and releases internal reference to it.
+void Thread::FinishAndRelease()
+{
+ // Note: thread must be US.
+ ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED);
+ ThreadFlags |= OVR_THREAD_FINISHED;
+
+ // Release our reference; this is equivalent to 'delete this'
+ // from the point of view of our thread.
+ Release();
+}
+
+
+
+// *** ThreadList - used to track all created threads
+
+class ThreadList : public NewOverrideBase
+{
+ //------------------------------------------------------------------------
+ struct ThreadHashOp
+ {
+ size_t operator()(const Thread* ptr)
+ {
+ return (((size_t)ptr) >> 6) ^ (size_t)ptr;
+ }
+ };
+
+ HashSet<Thread*, ThreadHashOp> ThreadSet;
+ Mutex ThreadMutex;
+ WaitCondition ThreadsEmpty;
+ // Track the root thread that created us.
+ pthread_t RootThreadId;
+
+ static ThreadList* volatile pRunningThreads;
+
+ void addThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Add(pthread);
+ }
+
+ void removeThread(Thread *pthread)
+ {
+ Mutex::Locker lock(&ThreadMutex);
+ ThreadSet.Remove(pthread);
+ if (ThreadSet.GetSize() == 0)
+ ThreadsEmpty.Notify();
+ }
+
+ void finishAllThreads()
+ {
+ // Only original root thread can call this.
+ OVR_ASSERT(pthread_self() == RootThreadId);
+
+ Mutex::Locker lock(&ThreadMutex);
+ while (ThreadSet.GetSize() != 0)
+ ThreadsEmpty.Wait(&ThreadMutex);
+ }
+
+public:
+
+ ThreadList()
+ {
+ RootThreadId = pthread_self();
+ }
+ ~ThreadList() { }
+
+
+ static void AddRunningThread(Thread *pthread)
+ {
+ // Non-atomic creation ok since only the root thread
+ if (!pRunningThreads)
+ {
+ pRunningThreads = new ThreadList;
+ OVR_ASSERT(pRunningThreads);
+ }
+ pRunningThreads->addThread(pthread);
+ }
+
+ // NOTE: 'pthread' might be a dead pointer when this is
+ // called so it should not be accessed; it is only used
+ // for removal.
+ static void RemoveRunningThread(Thread *pthread)
+ {
+ OVR_ASSERT(pRunningThreads);
+ pRunningThreads->removeThread(pthread);
+ }
+
+ static void FinishAllThreads()
+ {
+ // This is ok because only root thread can wait for other thread finish.
+ if (pRunningThreads)
+ {
+ pRunningThreads->finishAllThreads();
+ delete pRunningThreads;
+ pRunningThreads = 0;
+ }
+ }
+};
+
+// By default, we have no thread list.
+ThreadList* volatile ThreadList::pRunningThreads = 0;
+
+
+// FinishAllThreads - exposed publicly in Thread.
+void Thread::FinishAllThreads()
+{
+ ThreadList::FinishAllThreads();
+}
+
+// *** Run override
+
+int Thread::PRun()
+{
+ // Suspend us on start, if requested
+ if (ThreadFlags & OVR_THREAD_START_SUSPENDED)
+ {
+ Suspend();
+ ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED;
+ }
+
+ // Call the virtual run function
+ ExitCode = Run();
+ return ExitCode;
+}
+
+
+
+
+// *** User overridables
+
+bool Thread::GetExitFlag() const
+{
+ return (ThreadFlags & OVR_THREAD_EXIT) != 0;
+}
+
+void Thread::SetExitFlag(bool exitFlag)
+{
+ // The below is atomic since ThreadFlags is AtomicInt.
+ if (exitFlag)
+ ThreadFlags |= OVR_THREAD_EXIT;
+ else
+ ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT;
+}
+
+
+// Determines whether the thread was running and is now finished
+bool Thread::IsFinished() const
+{
+ return (ThreadFlags & OVR_THREAD_FINISHED) != 0;
+}
+// Determines whether the thread is suspended
+bool Thread::IsSuspended() const
+{
+ return SuspendCount > 0;
+}
+// Returns current thread state
+Thread::ThreadState Thread::GetThreadState() const
+{
+ if (IsSuspended())
+ return Suspended;
+ if (ThreadFlags & OVR_THREAD_STARTED)
+ return Running;
+ return NotRunning;
+}
+/*
+static const char* mapsched_policy(int policy)
+{
+ switch(policy)
+ {
+ case SCHED_OTHER:
+ return "SCHED_OTHER";
+ case SCHED_RR:
+ return "SCHED_RR";
+ case SCHED_FIFO:
+ return "SCHED_FIFO";
+
+ }
+ return "UNKNOWN";
+}
+ int policy;
+ sched_param sparam;
+ pthread_getschedparam(pthread_self(), &policy, &sparam);
+ int max_prior = sched_get_priority_max(policy);
+ int min_prior = sched_get_priority_min(policy);
+ printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior);
+#include <stdio.h>
+*/
+// ***** Thread management
+
+// The actual first function called on thread start
+void* Thread_PthreadStartFn(void* phandle)
+{
+ Thread* pthread = (Thread*)phandle;
+ int result = pthread->PRun();
+ // Signal the thread as done and release it atomically.
+ pthread->FinishAndRelease();
+ // At this point Thread object might be dead; however we can still pass
+ // it to RemoveRunningThread since it is only used as a key there.
+ ThreadList::RemoveRunningThread(pthread);
+ return reinterpret_cast<void*>(result);
+}
+
+int Thread::InitAttr = 0;
+pthread_attr_t Thread::Attr;
+
+/* static */
+int Thread::GetOSPriority(ThreadPriority p)
+//static inline int MapToSystemPrority(Thread::ThreadPriority p)
+{
+ OVR_UNUSED(p);
+ return -1;
+}
+
+bool Thread::Start(ThreadState initialState)
+{
+ if (initialState == NotRunning)
+ return 0;
+ if (GetThreadState() != NotRunning)
+ {
+ OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this));
+ return 0;
+ }
+
+ if (!InitAttr)
+ {
+ pthread_attr_init(&Attr);
+ pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&Attr, 128 * 1024);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(NormalPriority);
+ pthread_attr_setschedparam(&Attr, &sparam);
+ InitAttr = 1;
+ }
+
+ ExitCode = 0;
+ SuspendCount = 0;
+ ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED;
+
+ // AddRef to us until the thread is finished
+ AddRef();
+ ThreadList::AddRunningThread(this);
+
+ int result;
+ if (StackSize != 128 * 1024 || Priority != NormalPriority)
+ {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&attr, StackSize);
+ sched_param sparam;
+ sparam.sched_priority = Thread::GetOSPriority(Priority);
+ pthread_attr_setschedparam(&attr, &sparam);
+ result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this);
+ pthread_attr_destroy(&attr);
+ }
+ else
+ result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this);
+
+ if (result)
+ {
+ ThreadFlags = 0;
+ Release();
+ ThreadList::RemoveRunningThread(this);
+ return 0;
+ }
+ return 1;
+}
+
+
+// Suspend the thread until resumed
+bool Thread::Suspend()
+{
+ OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system"));
+ return 0;
+}
+
+// Resumes currently suspended thread
+bool Thread::Resume()
+{
+ return 0;
+}
+
+
+// Quits with an exit code
+void Thread::Exit(int exitCode)
+{
+ // Can only exist the current thread
+ // if (GetThread() != this)
+ // return;
+
+ // Call the virtual OnExit function
+ OnExit();
+
+ // Signal this thread object as done and release it's references.
+ FinishAndRelease();
+ ThreadList::RemoveRunningThread(this);
+
+ pthread_exit(reinterpret_cast<void*>(exitCode));
+}
+
+ThreadId GetCurrentThreadId()
+{
+ return (void*)pthread_self();
+}
+
+// *** Sleep functions
+
+/* static */
+bool Thread::Sleep(unsigned secs)
+{
+ sleep(secs);
+ return 1;
+}
+/* static */
+bool Thread::MSleep(unsigned msecs)
+{
+ usleep(msecs*1000);
+ return 1;
+}
+
+/* static */
+int Thread::GetCPUCount()
+{
+ return 1;
+}
+
+}
+
+#endif // OVR_ENABLE_THREADS
diff --git a/LibOVR/Src/Kernel/OVR_Timer.cpp b/LibOVR/Src/Kernel/OVR_Timer.cpp
new file mode 100644
index 0000000..a8de47d
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Timer.cpp
@@ -0,0 +1,287 @@
+/************************************************************************************
+
+Filename : OVR_Timer.cpp
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+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_Timer.h"
+#include "OVR_Log.h"
+
+#if defined (OVR_OS_WIN32)
+#include <windows.h>
+#elif defined(OVR_OS_ANDROID)
+#include <time.h>
+#include <android/log.h>
+
+#else
+#include <sys/time.h>
+#endif
+
+namespace OVR {
+
+// For recorded data playback
+bool Timer::useFakeSeconds = false;
+double Timer::FakeSeconds = 0;
+
+
+//------------------------------------------------------------------------
+// *** Timer - Platform Independent functions
+
+// Returns global high-resolution application timer in seconds.
+double Timer::GetSeconds()
+{
+ if(useFakeSeconds)
+ return FakeSeconds;
+
+ return double(Timer::GetTicksNanos()) * 0.000000001;
+}
+
+
+#ifndef OVR_OS_WIN32
+
+// Unused on OSs other then Win32.
+void Timer::initializeTimerSystem()
+{
+}
+void Timer::shutdownTimerSystem()
+{
+}
+
+#endif
+
+
+
+//------------------------------------------------------------------------
+// *** Android Specific Timer
+
+#if defined(OVR_OS_ANDROID)
+
+UInt64 Timer::GetTicksNanos()
+{
+ if (useFakeSeconds)
+ return (UInt64) (FakeSeconds * NanosPerSecond);
+
+ // Choreographer vsync timestamp is based on.
+ struct timespec tp;
+ const int status = clock_gettime(CLOCK_MONOTONIC, &tp);
+
+ if (status != 0)
+ {
+ OVR_DEBUG_LOG(("clock_gettime status=%i", status ));
+ }
+ const UInt64 result = (UInt64)tp.tv_sec * (UInt64)(1000 * 1000 * 1000) + UInt64(tp.tv_nsec);
+ return result;
+}
+
+
+//------------------------------------------------------------------------
+// *** Win32 Specific Timer
+
+#elif defined (OVR_OS_WIN32)
+
+
+// This helper class implements high-resolution wrapper that combines timeGetTime() output
+// with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits,
+// as it's tied to the system clock.
+struct PerformanceTimer
+{
+ PerformanceTimer()
+ : OldMMTimeMs(0), MMTimeWrapCounter(0), PrefFrequency(0),
+ LastResultNanos(0), PerfMinusTicksDeltaNanos(0)
+ { }
+
+ enum {
+ MMTimerResolutionNanos = 1000000
+ };
+
+ void Initialize();
+ void Shutdown();
+
+ UInt64 GetTimeNanos();
+
+
+ UINT64 getFrequency()
+ {
+ if (PrefFrequency == 0)
+ {
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+ PrefFrequency = freq.QuadPart;
+ }
+ return PrefFrequency;
+ }
+
+
+ CRITICAL_SECTION TimeCS;
+ // timeGetTime() support with wrap.
+ UInt32 OldMMTimeMs;
+ UInt32 MMTimeWrapCounter;
+ // Cached performance frequency result.
+ UInt64 PrefFrequency;
+
+ // Computed as (perfCounterNanos - ticksCounterNanos) initially,
+ // and used to adjust timing.
+ UInt64 PerfMinusTicksDeltaNanos;
+ // Last returned value in nanoseconds, to ensure we don't back-step in time.
+ UInt64 LastResultNanos;
+};
+
+PerformanceTimer Win32_PerfTimer;
+
+
+void PerformanceTimer::Initialize()
+{
+ timeBeginPeriod(1);
+ InitializeCriticalSection(&TimeCS);
+ MMTimeWrapCounter = 0;
+ getFrequency();
+}
+
+void PerformanceTimer::Shutdown()
+{
+ DeleteCriticalSection(&TimeCS);
+ timeEndPeriod(1);
+}
+
+UInt64 PerformanceTimer::GetTimeNanos()
+{
+ UInt64 resultNanos;
+ LARGE_INTEGER li;
+ DWORD mmTimeMs;
+
+ // On Win32 QueryPerformanceFrequency is unreliable due to SMP and
+ // performance levels, so use this logic to detect wrapping and track
+ // high bits.
+ ::EnterCriticalSection(&TimeCS);
+
+ // Get raw value and perf counter "At the same time".
+ mmTimeMs = timeGetTime();
+ QueryPerformanceCounter(&li);
+
+ if (OldMMTimeMs > mmTimeMs)
+ MMTimeWrapCounter++;
+ OldMMTimeMs = mmTimeMs;
+
+ // Normalize to nanoseconds.
+ UInt64 mmCounterNanos = ((UInt64(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000;
+ UInt64 frequency = getFrequency();
+ UInt64 perfCounterSeconds = UInt64(li.QuadPart) / frequency;
+ UInt64 perfRemainderNanos = ( (UInt64(li.QuadPart) - perfCounterSeconds * frequency) *
+ Timer::NanosPerSecond ) / frequency;
+ UInt64 perfCounterNanos = perfCounterSeconds * Timer::NanosPerSecond + perfRemainderNanos;
+
+ if (PerfMinusTicksDeltaNanos == 0)
+ PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos;
+
+
+ // Compute result before snapping.
+ //
+ // On first call, this evaluates to:
+ // resultNanos = mmCounterNanos.
+ // Next call, assuming no wrap:
+ // resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos).
+ // After wrap, this would be:
+ // resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos - prev_perfCounterNanos).
+ //
+ resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos;
+
+ // Snap the range so that resultNanos never moves further apart then its target resolution.
+ // It's better to allow more slack on the high side as timeGetTime() may be updated at sporadically
+ // larger then 1 ms intervals even when 1 ms resolution is requested.
+ if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos*2))
+ {
+ resultNanos = mmCounterNanos + MMTimerResolutionNanos*2;
+ if (resultNanos < LastResultNanos)
+ resultNanos = LastResultNanos;
+ PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
+ }
+ else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos))
+ {
+ resultNanos = mmCounterNanos - MMTimerResolutionNanos;
+ if (resultNanos < LastResultNanos)
+ resultNanos = LastResultNanos;
+ PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos;
+ }
+
+ LastResultNanos = resultNanos;
+ ::LeaveCriticalSection(&TimeCS);
+
+ //Tom's addition, to keep precision
+ static UInt64 initial_time = 0;
+ if (!initial_time) initial_time = resultNanos;
+ resultNanos -= initial_time;
+
+
+ return resultNanos;
+}
+
+
+// Delegate to PerformanceTimer.
+UInt64 Timer::GetTicksNanos()
+{
+ if (useFakeSeconds)
+ return (UInt64) (FakeSeconds * NanosPerSecond);
+
+ return Win32_PerfTimer.GetTimeNanos();
+}
+void Timer::initializeTimerSystem()
+{
+ Win32_PerfTimer.Initialize();
+
+}
+void Timer::shutdownTimerSystem()
+{
+ Win32_PerfTimer.Shutdown();
+}
+
+#else // !OVR_OS_WIN32 && !OVR_OS_ANDROID
+
+
+//------------------------------------------------------------------------
+// *** Standard OS Timer
+
+UInt64 Timer::GetTicksNanos()
+{
+ if (useFakeSeconds)
+ return (UInt64) (FakeSeconds * NanosPerSecond);
+
+ // TODO: prefer rdtsc when available?
+ UInt64 result;
+
+ // Return microseconds.
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+
+ result = (UInt64)tv.tv_sec * 1000000;
+ result += tv.tv_usec;
+
+ return result * 1000;
+}
+
+#endif // OS-specific
+
+
+
+} // OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_Timer.h b/LibOVR/Src/Kernel/OVR_Timer.h
new file mode 100644
index 0000000..12cba3b
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Timer.h
@@ -0,0 +1,88 @@
+/************************************************************************************
+
+PublicHeader: OVR
+Filename : OVR_Timer.h
+Content : Provides static functions for precise timing
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Timer_h
+#define OVR_Timer_h
+
+#include "OVR_Types.h"
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Timer
+
+// Timer class defines a family of static functions used for application
+// timing and profiling.
+
+class Timer
+{
+public:
+ enum {
+ MsPerSecond = 1000, // Milliseconds in one second.
+ NanosPerSecond = MsPerSecond * 1000 * 1000,
+ MksPerSecond = MsPerSecond * 1000
+ };
+
+ // ***** Timing APIs for Application
+
+ // These APIs should be used to guide animation and other program functions
+ // that require precision.
+
+ // Returns global high-resolution application timer in seconds.
+ static double OVR_STDCALL GetSeconds();
+
+ // Returns time in Nanoseconds, using highest possible system resolution.
+ static UInt64 OVR_STDCALL GetTicksNanos();
+
+ // Kept for compatibility.
+ // Returns ticks in milliseconds, as a 32-bit number. May wrap around every 49.2 days.
+ // Use either time difference of two values of GetTicks to avoid wrap-around.
+ static UInt32 OVR_STDCALL GetTicksMs()
+ { return UInt32(GetTicksNanos() / 1000000); }
+
+ // for recorded data playback
+ static void SetFakeSeconds(double fakeSeconds)
+ {
+ FakeSeconds = fakeSeconds;
+ useFakeSeconds = true;
+ }
+
+private:
+ friend class System;
+ // System called during program startup/shutdown.
+ static void initializeTimerSystem();
+ static void shutdownTimerSystem();
+
+ // for recorded data playback
+ static double FakeSeconds;
+ static bool useFakeSeconds;
+};
+
+
+} // OVR::Timer
+
+#endif
diff --git a/LibOVR/Src/Kernel/OVR_Types.h b/LibOVR/Src/Kernel/OVR_Types.h
new file mode 100644
index 0000000..8f2b3f3
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_Types.h
@@ -0,0 +1,474 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Types.h
+Content : Standard library defines and simple types
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Types_H
+#define OVR_Types_H
+
+//-----------------------------------------------------------------------------------
+// ****** Operating System
+//
+// Type definitions exist for the following operating systems: (OVR_OS_x)
+//
+// WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP)
+// DARWIN - Darwin OS (Mac OS X)
+// LINUX - Linux
+// ANDROID - Android
+// IPHONE - iPhone
+
+#if (defined(__APPLE__) && (defined(__GNUC__) ||\
+ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__)
+# if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
+# define OVR_OS_IPHONE
+# else
+# define OVR_OS_DARWIN
+# define OVR_OS_MAC
+# endif
+#elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
+# define OVR_OS_WIN32
+#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
+# define OVR_OS_WIN32
+#elif defined(__linux__) || defined(__linux)
+# define OVR_OS_LINUX
+#else
+# define OVR_OS_OTHER
+#endif
+
+#if defined(ANDROID)
+# define OVR_OS_ANDROID
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** CPU Architecture
+//
+// The following CPUs are defined: (OVR_CPU_x)
+//
+// X86 - x86 (IA-32)
+// X86_64 - x86_64 (amd64)
+// PPC - PowerPC
+// PPC64 - PowerPC64
+// MIPS - MIPS
+// OTHER - CPU for which no special support is present or needed
+
+
+#if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
+# define OVR_CPU_X86_64
+# define OVR_64BIT_POINTERS
+#elif defined(__i386__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_X86
+#elif defined(__powerpc64__)
+# define OVR_CPU_PPC64
+#elif defined(__ppc__)
+# define OVR_CPU_PPC
+#elif defined(__mips__) || defined(__MIPSEL__)
+# define OVR_CPU_MIPS
+#elif defined(__arm__)
+# define OVR_CPU_ARM
+#else
+# define OVR_CPU_OTHER
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Co-Processor Architecture
+//
+// The following co-processors are defined: (OVR_CPU_x)
+//
+// SSE - Available on all modern x86 processors.
+// Altivec - Available on all modern ppc processors.
+// Neon - Available on some armv7+ processors.
+
+#if defined(__SSE__) || defined(OVR_OS_WIN32)
+# define OVR_CPU_SSE
+#endif // __SSE__
+
+#if defined( __ALTIVEC__ )
+# define OVR_CPU_ALTIVEC
+#endif // __ALTIVEC__
+
+#if defined(__ARM_NEON__)
+# define OVR_CPU_ARM_NEON
+#endif // __ARM_NEON__
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler
+//
+// The following compilers are defined: (OVR_CC_x)
+//
+// MSVC - Microsoft Visual C/C++
+// INTEL - Intel C++ for Linux / Windows
+// GNU - GNU C++
+// ARM - ARM C/C++
+
+#if defined(__INTEL_COMPILER)
+// Intel 4.0 = 400
+// Intel 5.0 = 500
+// Intel 6.0 = 600
+// Intel 8.0 = 800
+// Intel 9.0 = 900
+# define OVR_CC_INTEL __INTEL_COMPILER
+
+#elif defined(_MSC_VER)
+// MSVC 5.0 = 1100
+// MSVC 6.0 = 1200
+// MSVC 7.0 (VC2002) = 1300
+// MSVC 7.1 (VC2003) = 1310
+// MSVC 8.0 (VC2005) = 1400
+// MSVC 9.0 (VC2008) = 1500
+// MSVC 10.0 (VC2010) = 1600
+// MSVC 11.0 (VC2012) = 1700
+// MSVC 12.0 (VC2013) = 1800
+# define OVR_CC_MSVC _MSC_VER
+
+#elif defined(__GNUC__)
+# define OVR_CC_GNU
+
+#elif defined(__CC_ARM)
+# define OVR_CC_ARM
+
+#else
+# error "Oculus does not support this Compiler"
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Compiler Warnings
+
+// Disable MSVC warnings
+#if defined(OVR_CC_MSVC)
+# pragma warning(disable : 4127) // Inconsistent dll linkage
+# pragma warning(disable : 4530) // Exception handling
+# if (OVR_CC_MSVC<1300)
+# pragma warning(disable : 4514) // Unreferenced inline function has been removed
+# pragma warning(disable : 4710) // Function not inlined
+# pragma warning(disable : 4714) // _force_inline not inlined
+# pragma warning(disable : 4786) // Debug variable name longer than 255 chars
+# endif // (OVR_CC_MSVC<1300)
+#endif // (OVR_CC_MSVC)
+
+
+
+// *** Linux Unicode - must come before Standard Includes
+
+#ifdef OVR_OS_LINUX
+// Use glibc unicode functions on linux.
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Standard Includes
+//
+#include <stddef.h>
+#include <limits.h>
+#include <float.h>
+
+
+// MSVC Based Memory Leak checking - for now
+#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG)
+# define _CRTDBG_MAP_ALLOC
+# include <stdlib.h>
+# include <crtdbg.h>
+
+#if 0
+// Uncomment this to help debug memory leaks under Visual Studio in OVR apps only.
+// This shouldn't be defined in customer releases.
+# ifndef OVR_DEFINE_NEW
+# define OVR_DEFINE_NEW new(__FILE__, __LINE__)
+# define new OVR_DEFINE_NEW
+# endif
+#endif
+
+#endif
+
+
+//-----------------------------------------------------------------------------------
+// ***** Type definitions for Common Systems
+
+namespace OVR {
+
+typedef char Char;
+
+// Pointer-sized integer
+typedef size_t UPInt;
+typedef ptrdiff_t SPInt;
+
+
+#if defined(OVR_OS_WIN32)
+
+typedef char SByte; // 8 bit Integer (Byte)
+typedef unsigned char UByte;
+typedef short SInt16; // 16 bit Integer (Word)
+typedef unsigned short UInt16;
+typedef long SInt32; // 32 bit Integer
+typedef unsigned long UInt32;
+typedef __int64 SInt64; // 64 bit Integer (QWord)
+typedef unsigned __int64 UInt64;
+
+
+#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU)
+
+typedef int SByte __attribute__((__mode__ (__QI__)));
+typedef unsigned int UByte __attribute__((__mode__ (__QI__)));
+typedef int SInt16 __attribute__((__mode__ (__HI__)));
+typedef unsigned int UInt16 __attribute__((__mode__ (__HI__)));
+typedef int SInt32 __attribute__((__mode__ (__SI__)));
+typedef unsigned int UInt32 __attribute__((__mode__ (__SI__)));
+typedef int SInt64 __attribute__((__mode__ (__DI__)));
+typedef unsigned int UInt64 __attribute__((__mode__ (__DI__)));
+
+#else
+
+#include <sys/types.h>
+typedef int8_t SByte;
+typedef uint8_t UByte;
+typedef int16_t SInt16;
+typedef uint16_t UInt16;
+typedef int32_t SInt32;
+typedef uint32_t UInt32;
+typedef int64_t SInt64;
+typedef uint64_t UInt64;
+
+#endif
+
+
+// ***** BaseTypes Namespace
+
+// BaseTypes namespace is explicitly declared to allow base types to be used
+// by customers directly without other contents of OVR namespace.
+//
+// Its is expected that OVR samples will declare 'using namespace OVR::BaseTypes'
+// to allow using these directly without polluting the target scope with other
+// OVR declarations, such as Ptr<>, String or Mutex.
+namespace BaseTypes
+{
+ using OVR::UPInt;
+ using OVR::SPInt;
+ using OVR::UByte;
+ using OVR::SByte;
+ using OVR::UInt16;
+ using OVR::SInt16;
+ using OVR::UInt32;
+ using OVR::SInt32;
+ using OVR::UInt64;
+ using OVR::SInt64;
+} // OVR::BaseTypes
+
+} // OVR
+
+
+//-----------------------------------------------------------------------------------
+// ***** Macro Definitions
+//
+// We define the following:
+//
+// OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN
+// OVR_FORCE_INLINE - Forces inline expansion of function
+// OVR_ASM - Assembly language prefix
+// OVR_STR - Prefixes string with L"" if building unicode
+//
+// OVR_STDCALL - Use stdcall calling convention (Pascal arg order)
+// OVR_CDECL - Use cdecl calling convention (C argument order)
+// OVR_FASTCALL - Use fastcall calling convention (registers)
+//
+
+// Byte order constants, OVR_BYTE_ORDER is defined to be one of these.
+#define OVR_LITTLE_ENDIAN 1
+#define OVR_BIG_ENDIAN 2
+
+
+// Force inline substitute - goes before function declaration
+#if defined(OVR_CC_MSVC)
+# define OVR_FORCE_INLINE __forceinline
+#elif defined(OVR_CC_GNU)
+# define OVR_FORCE_INLINE __attribute__((always_inline)) inline
+#else
+# define OVR_FORCE_INLINE inline
+#endif // OVR_CC_MSVC
+
+
+#if defined(OVR_OS_WIN32)
+
+ // ***** Win32
+
+ // Byte order
+ #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+
+ // Calling convention - goes after function return type but before function name
+ #ifdef __cplusplus_cli
+ # define OVR_FASTCALL __stdcall
+ #else
+ # define OVR_FASTCALL __fastcall
+ #endif
+
+ #define OVR_STDCALL __stdcall
+ #define OVR_CDECL __cdecl
+
+
+ // Assembly macros
+ #if defined(OVR_CC_MSVC)
+ # define OVR_ASM _asm
+ #else
+ # define OVR_ASM asm
+ #endif // (OVR_CC_MSVC)
+
+ #ifdef UNICODE
+ # define OVR_STR(str) L##str
+ #else
+ # define OVR_STR(str) str
+ #endif // UNICODE
+
+#else
+
+ // **** Standard systems
+
+ #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \
+ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64))
+ # define OVR_BYTE_ORDER OVR_BIG_ENDIAN
+ #else
+ # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN
+ #endif
+
+ // Assembly macros
+ #define OVR_ASM __asm__
+ #define OVR_ASM_PROC(procname) OVR_ASM
+ #define OVR_ASM_END OVR_ASM
+
+ // Calling convention - goes after function return type but before function name
+ #define OVR_FASTCALL
+ #define OVR_STDCALL
+ #define OVR_CDECL
+
+#endif // defined(OVR_OS_WIN32)
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_DEBUG_BREAK, OVR_ASSERT
+//
+// If not in debug build, macros do nothing
+#ifndef OVR_BUILD_DEBUG
+
+# define OVR_DEBUG_CODE(c) c
+# define OVR_DEBUG_BREAK ((void)0)
+# define OVR_ASSERT(p) ((void)0)
+
+#else
+
+// Microsoft Win32 specific debugging support
+#if defined(OVR_OS_WIN32)
+# ifdef OVR_CPU_X86
+# if defined(__cplusplus_cli)
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# elif defined(OVR_CC_GNU)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+# else
+# define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0)
+# endif
+# else
+# define OVR_DEBUG_BREAK do { __debugbreak(); } while(0)
+# endif
+// Unix specific debugging support
+#elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
+# define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0)
+#else
+# define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0)
+#endif
+
+#define OVR_DEBUG_CODE(c)
+
+// This will cause compiler breakpoint
+#define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0)
+
+#endif // OVR_BUILD_DEBUG
+
+
+// Compile-time assert; produces compiler error if condition is false
+#define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} }
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_UNUSED - Unused Argument handling
+
+// Macro to quiet compiler warnings about unused parameters/variables.
+#if defined(OVR_CC_GNU)
+# define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0)
+#else
+# define OVR_UNUSED(a) (a)
+#endif
+
+#define OVR_UNUSED1(a1) OVR_UNUSED(a1)
+#define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2)
+#define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3)
+#define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4)
+#define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5)
+#define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6)
+#define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7)
+#define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8)
+#define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9)
+
+
+//-----------------------------------------------------------------------------------
+// ***** Configuration Macros
+
+// SF Build type
+#ifdef OVR_BUILD_DEBUG
+# define OVR_BUILD_STRING "Debug"
+#else
+# define OVR_BUILD_STRING "Release"
+#endif
+
+
+//// Enables SF Debugging information
+//# define OVR_BUILD_DEBUG
+
+// OVR_DEBUG_STATEMENT injects a statement only in debug builds.
+// OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise.
+#ifdef OVR_BUILD_DEBUG
+#define OVR_DEBUG_STATEMENT(s) s
+#define OVR_DEBUG_SELECT(d, nd) d
+#else
+#define OVR_DEBUG_STATEMENT(s)
+#define OVR_DEBUG_SELECT(d, nd) nd
+#endif
+
+
+#define OVR_ENABLE_THREADS
+//
+// Prevents OVR from defining new within
+// type macros, so developers can override
+// new using the #define new new(...) trick
+// - used with OVR_DEFINE_NEW macro
+//# define OVR_BUILD_DEFINE_NEW
+//
+
+
+#endif // OVR_Types_h
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.cpp b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp
new file mode 100644
index 0000000..f8aa697
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_UTF8Util.cpp
@@ -0,0 +1,556 @@
+/**************************************************************************
+
+Filename : OVR_UTF8Util.cpp
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+Notes : Much useful info at "UTF-8 and Unicode FAQ"
+ http://www.cl.cam.ac.uk/~mgk25/unicode.html
+
+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_UTF8Util.h"
+
+namespace OVR { namespace UTF8Util {
+
+SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen)
+{
+ const char* p = buf;
+ SPInt length = 0;
+
+ if (buflen != -1)
+ {
+ while (p - buf < buflen)
+ {
+ // We should be able to have ASStrings with 0 in the middle.
+ UTF8Util::DecodeNextChar_Advance0(&p);
+ length++;
+ }
+ }
+ else
+ {
+ while (UTF8Util::DecodeNextChar_Advance0(&p))
+ length++;
+ }
+
+ return length;
+}
+
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+ UInt32 c = 0;
+
+ if (length != -1)
+ {
+ while (buf - putf8str < length)
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ if (index == 0)
+ return c;
+ index--;
+ }
+
+ return c;
+ }
+
+ do
+ {
+ c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ {
+ // We've hit the end of the string; don't go further.
+ OVR_ASSERT(index == 0);
+ return c;
+ }
+ } while (index >= 0);
+
+ return c;
+}
+
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length)
+{
+ const char* buf = putf8str;
+
+ if (length != -1)
+ {
+ while ((buf - putf8str) < length && index > 0)
+ {
+ UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+ }
+
+ return buf-putf8str;
+ }
+
+ while (index > 0)
+ {
+ UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf);
+ index--;
+
+ if (c == 0)
+ return buf-putf8str;
+ };
+
+ return buf-putf8str;
+}
+
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ return 1;
+ else if (ucs_character <= 0x7FF)
+ return 2;
+ else if (ucs_character <= 0xFFFF)
+ return 3;
+ else if (ucs_character <= 0x1FFFFF)
+ return 4;
+ else if (ucs_character <= 0x3FFFFFF)
+ return 5;
+ else if (ucs_character <= 0x7FFFFFFF)
+ return 6;
+ else
+ return 0;
+}
+
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer)
+{
+ UInt32 uc;
+ char c;
+
+ // Security considerations:
+ //
+ // Changed, this is now only the case for DecodeNextChar:
+ // - If we hit a zero byte, we want to return 0 without stepping
+ // the buffer pointer past the 0. th
+ //
+ // If we hit an "overlong sequence"; i.e. a character encoded
+ // in a longer multibyte string than is necessary, then we
+ // need to discard the character. This is so attackers can't
+ // disguise dangerous characters or character sequences --
+ // there is only one valid encoding for each character.
+ //
+ // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE,
+ // 0xFFFF } then we ignore them; they are not valid in UTF-8.
+
+ // This isn't actually an invalid character; it's a valid char that
+ // looks like an inverted question mark.
+#define INVALID_CHAR 0x0FFFD
+
+#define FIRST_BYTE(mask, shift) \
+ uc = (c & (mask)) << (shift);
+
+#define NEXT_BYTE(shift) \
+ c = **putf8Buffer; \
+ if (c == 0) return 0; /* end of buffer, do not advance */ \
+ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \
+ (*putf8Buffer)++; \
+ uc |= (c & 0x3F) << shift;
+
+ c = **putf8Buffer;
+ (*putf8Buffer)++;
+ if (c == 0)
+ return 0; // End of buffer.
+
+ if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII.
+
+ // Multi-byte sequences.
+ if ((c & 0xE0) == 0xC0)
+ {
+ // Two-byte sequence.
+ FIRST_BYTE(0x1F, 6);
+ NEXT_BYTE(0);
+ if (uc < 0x80) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xF0) == 0xE0)
+ {
+ // Three-byte sequence.
+ FIRST_BYTE(0x0F, 12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x800) return INVALID_CHAR; // overlong
+ // Not valid ISO 10646, but Flash requires these to work
+ // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0)
+ // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR;
+ // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646
+ return uc;
+ }
+ else if ((c & 0xF8) == 0xF0)
+ {
+ // Four-byte sequence.
+ FIRST_BYTE(0x07, 18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x010000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFC) == 0xF8)
+ {
+ // Five-byte sequence.
+ FIRST_BYTE(0x03, 24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x0200000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else if ((c & 0xFE) == 0xFC)
+ {
+ // Six-byte sequence.
+ FIRST_BYTE(0x01, 30);
+ NEXT_BYTE(24);
+ NEXT_BYTE(18);
+ NEXT_BYTE(12);
+ NEXT_BYTE(6);
+ NEXT_BYTE(0);
+ if (uc < 0x04000000) return INVALID_CHAR; // overlong
+ return uc;
+ }
+ else
+ {
+ // Invalid.
+ return INVALID_CHAR;
+ }
+}
+
+
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character)
+{
+ if (ucs_character <= 0x7F)
+ {
+ // Plain single-byte ASCII.
+ pbuffer[(*pindex)++] = (char) ucs_character;
+ }
+ else if (ucs_character <= 0x7FF)
+ {
+ // Two bytes.
+ pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0xFFFF)
+ {
+ // Three bytes.
+ pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x1FFFFF)
+ {
+ // Four bytes.
+ pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x3FFFFFF)
+ {
+ // Five bytes.
+ pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else if (ucs_character <= 0x7FFFFFFF)
+ {
+ // Six bytes.
+ pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F);
+ pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F);
+ }
+ else
+ {
+ // Invalid char; don't encode anything.
+ }
+}
+
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length)
+{
+ SPInt len = 0;
+ if (length != -1)
+ for (int i = 0; i < length; i++)
+ {
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ else
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ return len;
+ len += GetEncodeCharSize(pchar[i]);
+ }
+ return len;
+}
+
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length)
+{
+ SPInt ofs = 0;
+ if (length != -1)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0;; i++)
+ {
+ if (pchar[i] == 0)
+ break;
+ EncodeChar(pbuff, &ofs, pchar[i]);
+ }
+ }
+ pbuff[ofs] = 0;
+}
+
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen)
+{
+ wchar_t *pbegin = pbuff;
+ if (bytesLen == -1)
+ {
+ while (1)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&putf8str);
+ if (ch == 0)
+ break;
+ else if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+ else
+ {
+ const char* p = putf8str;
+ while ((p - putf8str) < bytesLen)
+ {
+ UInt32 ch = DecodeNextChar_Advance0(&p);
+ if (ch >= 0xFFFF)
+ ch = 0xFFFD;
+ *pbuff++ = wchar_t(ch);
+ }
+ }
+
+ *pbuff = 0;
+ return pbuff - pbegin;
+}
+
+
+#ifdef UTF8_UNIT_TEST
+
+// Compile this test case with something like:
+//
+// gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test
+//
+// or
+//
+// cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I..
+//
+// If possible, try running the test program with the first arg
+// pointing at the file:
+//
+// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+//
+// and examine the results by eye to make sure they are acceptable to
+// you.
+
+
+#include "base/utility.h"
+#include <stdio.h>
+
+
+bool check_equal(const char* utf8_in, const UInt32* ucs_in)
+{
+ for (;;)
+ {
+ UInt32 next_ucs = *ucs_in++;
+ UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in);
+ if (next_ucs != next_ucs_from_utf8)
+ {
+ return false;
+ }
+ if (next_ucs == 0)
+ {
+ OVR_ASSERT(next_ucs_from_utf8 == 0);
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+void log_ascii(const char* line)
+{
+ for (;;)
+ {
+ unsigned char c = (unsigned char) *line++;
+ if (c == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (c != '\n'
+ && (c < 32 || c > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<0x%02X>", (int) c);
+ }
+ else
+ {
+ printf("%c", c);
+ }
+ }
+}
+
+
+void log_ucs(const UInt32* line)
+{
+ for (;;)
+ {
+ UInt32 uc = *line++;
+ if (uc == 0)
+ {
+ // End of line.
+ return;
+ }
+ else if (uc != '\n'
+ && (uc < 32 || uc > 127))
+ {
+ // Non-printable as plain ASCII.
+ printf("<U-%04X>", uc);
+ }
+ else
+ {
+ printf("%c", (char) uc);
+ }
+ }
+}
+
+
+// Simple canned test.
+int main(int argc, const char* argv[])
+{
+ {
+ const char* test8 = "Ignacio Castaño";
+ const UInt32 test32[] =
+ {
+ 0x49, 0x67, 0x6E, 0x61, 0x63,
+ 0x69, 0x6F, 0x20, 0x43, 0x61,
+ 0x73, 0x74, 0x61, 0xF1, 0x6F,
+ 0x00
+ };
+
+ OVR_ASSERT(check_equal(test8, test32));
+ }
+
+ // If user passed an arg, try reading the file as UTF-8 encoded text.
+ if (argc > 1)
+ {
+ const char* filename = argv[1];
+ FILE* fp = fopen(filename, "rb");
+ if (fp == NULL)
+ {
+ printf("Can't open file '%s'\n", filename);
+ return 1;
+ }
+
+ // Read lines from the file, encode/decode them, and highlight discrepancies.
+ const int LINE_SIZE = 200; // max line size
+ char line_buffer_utf8[LINE_SIZE];
+ char reencoded_utf8[6 * LINE_SIZE];
+ UInt32 line_buffer_ucs[LINE_SIZE];
+
+ int byte_counter = 0;
+ for (;;)
+ {
+ int c = fgetc(fp);
+ if (c == EOF)
+ {
+ // Done.
+ break;
+ }
+ line_buffer_utf8[byte_counter++] = c;
+ if (c == '\n' || byte_counter >= LINE_SIZE - 2)
+ {
+ // End of line. Process the line.
+ line_buffer_utf8[byte_counter++] = 0; // terminate.
+
+ // Decode into UCS.
+ const char* p = line_buffer_utf8;
+ UInt32* q = line_buffer_ucs;
+ for (;;)
+ {
+ UInt32 uc = UTF8Util::DecodeNextChar(&p);
+ *q++ = uc;
+
+ OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE);
+ OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE);
+
+ if (uc == 0) break;
+ }
+
+ // Encode back into UTF-8.
+ q = line_buffer_ucs;
+ int index = 0;
+ for (;;)
+ {
+ UInt32 uc = *q++;
+ OVR_ASSERT(index < LINE_SIZE * 6 - 6);
+ int last_index = index;
+ UTF8Util::EncodeChar(reencoded_utf8, &index, uc);
+ OVR_ASSERT(index <= last_index + 6);
+ if (uc == 0) break;
+ }
+
+ // This can be useful for debugging.
+#if 0
+ // Show the UCS and the re-encoded UTF-8.
+ log_ucs(line_buffer_ucs);
+ log_ascii(reencoded_utf8);
+#endif // 0
+
+ OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs));
+ OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs));
+
+ // Start next line.
+ byte_counter = 0;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return 0;
+}
+
+
+#endif // UTF8_UNIT_TEST
+
+}} // namespace UTF8Util::OVR
+
diff --git a/LibOVR/Src/Kernel/OVR_UTF8Util.h b/LibOVR/Src/Kernel/OVR_UTF8Util.h
new file mode 100644
index 0000000..6a596012
--- /dev/null
+++ b/LibOVR/Src/Kernel/OVR_UTF8Util.h
@@ -0,0 +1,99 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_UTF8Util.h
+Content : UTF8 Unicode character encoding/decoding support
+Created : September 19, 2012
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_UTF8Util_h
+#define OVR_UTF8Util_h
+
+#include "OVR_Types.h"
+
+namespace OVR { namespace UTF8Util {
+
+//-----------------------------------------------------------------------------------
+
+// *** UTF8 string length and indexing.
+
+// Determines the length of UTF8 string in characters.
+// If source length is specified (in bytes), null 0 character is counted properly.
+SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1);
+
+// Gets a decoded UTF8 character at index; you can access up to the index returned
+// by GetLength. 0 will be returned for out of bounds access.
+UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1);
+
+// Converts UTF8 character index into byte offset.
+// -1 is returned if index was out of bounds.
+SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1);
+
+
+// *** 16-bit Unicode string Encoding/Decoding routines.
+
+// Determines the number of bytes necessary to encode a string.
+// Does not count the terminating 0 (null) character.
+SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1);
+
+// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at
+// least GetEncodeStringSize() + 1.
+void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1);
+
+// Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available.
+// Characters over 0xFFFF are replaced with 0xFFFD.
+// Returns the length of resulting string (number of characters)
+UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1);
+
+
+// *** Individual character Encoding/Decoding.
+
+// Determined the number of bytes necessary to encode a UCS character.
+int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter);
+
+// Encodes the given UCS character into the given UTF-8 buffer.
+// Writes the data starting at buffer[offset], and
+// increments offset by the number of bytes written.
+// May write up to 6 bytes, so make sure there's room in the buffer
+void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter);
+
+// Return the next Unicode character in the UTF-8 encoded buffer.
+// Invalid UTF-8 sequences produce a U+FFFD character as output.
+// Advances *utf8_buffer past the character returned. Pointer advance
+// occurs even if the terminating 0 character is hit, since that allows
+// strings with middle '\0' characters to be supported.
+UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer);
+
+// Safer version of DecodeNextChar, which doesn't advance pointer if
+// null character is hit.
+inline UInt32 DecodeNextChar(const char** putf8Buffer)
+{
+ UInt32 ch = DecodeNextChar_Advance0(putf8Buffer);
+ if (ch == 0)
+ (*putf8Buffer)--;
+ return ch;
+}
+
+
+}} // OVR::UTF8Util
+
+#endif
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;
+}
+
+
diff --git a/LibOVR/Src/OVR_CAPI.h b/LibOVR/Src/OVR_CAPI.h
new file mode 100644
index 0000000..ec4708c
--- /dev/null
+++ b/LibOVR/Src/OVR_CAPI.h
@@ -0,0 +1,790 @@
+/************************************************************************************
+
+Filename : OVR_CAPI.h
+Content : C Interface to Oculus sensors and rendering.
+Created : November 23, 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.
+
+************************************************************************************/
+#ifndef OVR_CAPI_h
+#define OVR_CAPI_h
+
+#include <stdint.h>
+
+typedef char ovrBool;
+
+//-----------------------------------------------------------------------------------
+// ***** OVR_EXPORT definition
+
+#if !defined(OVR_EXPORT)
+ #if defined(WIN32)
+ #define OVR_EXPORT __declspec(dllexport)
+ #else
+ #define OVR_EXPORT
+ #endif
+#endif
+
+//-----------------------------------------------------------------------------------
+// ***** Simple Math Structures
+
+// 2D integer
+typedef struct ovrVector2i_
+{
+ int x, y;
+} ovrVector2i;
+typedef struct ovrSizei_
+{
+ int w, h;
+} ovrSizei;
+typedef struct ovrRecti_
+{
+ ovrVector2i Pos;
+ ovrSizei Size;
+} ovrRecti;
+
+// 3D
+typedef struct ovrQuatf_
+{
+ float x, y, z, w;
+} ovrQuatf;
+typedef struct ovrVector2f_
+{
+ float x, y;
+} ovrVector2f;
+typedef struct ovrVector3f_
+{
+ float x, y, z;
+} ovrVector3f;
+typedef struct ovrMatrix4f_
+{
+ float M[4][4];
+} ovrMatrix4f;
+// Position and orientation together.
+typedef struct ovrPosef_
+{
+ ovrQuatf Orientation;
+ ovrVector3f Position;
+} ovrPosef;
+
+// Full pose (rigid body) configuration with first and second derivatives.
+typedef struct ovrPoseStatef_
+{
+ ovrPosef Pose;
+ ovrVector3f AngularVelocity;
+ ovrVector3f LinearVelocity;
+ ovrVector3f AngularAcceleration;
+ ovrVector3f LinearAcceleration;
+ double TimeInSeconds; // Absolute time of this state sample.
+} ovrPoseStatef;
+
+// Field Of View (FOV) in tangent of the angle units.
+// As an example, for a standard 90 degree vertical FOV, we would
+// have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }.
+typedef struct ovrFovPort_
+{
+ float UpTan;
+ float DownTan;
+ float LeftTan;
+ float RightTan;
+} ovrFovPort;
+
+
+//-----------------------------------------------------------------------------------
+// ***** HMD Types
+
+// Enumerates all HMD types that we support.
+typedef enum
+{
+ ovrHmd_None = 0,
+ ovrHmd_DK1 = 3,
+ ovrHmd_DKHD = 4,
+ ovrHmd_CrystalCoveProto = 5,
+ ovrHmd_DK2 = 6,
+ ovrHmd_Other // Some HMD other then the one in the enumeration.
+} ovrHmdType;
+
+// HMD capability bits reported by device.
+//
+typedef enum
+{
+ // Read-only flags.
+ ovrHmdCap_Present = 0x0001, // This HMD exists (as opposed to being unplugged).
+ ovrHmdCap_Available = 0x0002, // HMD and is sensor is available for use
+ // (if not owned by another app).
+
+ // These flags are intended for use with the new driver display mode.
+ /*
+ ovrHmdCap_ExtendDesktop = 0x0004, // Read only, means display driver is in compatibility mode.
+
+ ovrHmdCap_DisplayOff = 0x0040, // Turns off Oculus HMD screen and output.
+ ovrHmdCap_NoMirrorToWindow = 0x2000, // Disables mirrowing of HMD output to the window;
+ // may improve rendering performance slightly.
+ */
+
+ // Modifiable flags (through ovrHmd_SetEnabledCaps).
+ ovrHmdCap_LowPersistence = 0x0080, // Supports low persistence mode.
+ ovrHmdCap_LatencyTest = 0x0100, // Supports pixel reading for continuous latency testing.
+ ovrHmdCap_DynamicPrediction = 0x0200, // Adjust prediction dynamically based on DK2 Latency.
+ // Support rendering without VSync for debugging
+ ovrHmdCap_NoVSync = 0x1000,
+ ovrHmdCap_NoRestore = 0x4000,
+
+ // These bits can be modified by ovrHmd_SetEnabledCaps.
+ ovrHmdCap_Writable_Mask = 0x1380
+} ovrHmdCaps;
+
+
+// Sensor capability bits reported by device.
+// Used with ovrHmd_StartSensor.
+typedef enum
+{
+ ovrSensorCap_Orientation = 0x0010, // Supports orientation tracking (IMU).
+ ovrSensorCap_YawCorrection = 0x0020, // Supports yaw correction through magnetometer or other means.
+ ovrSensorCap_Position = 0x0040, // Supports positional tracking.
+
+} ovrSensorCaps;
+
+// Distortion capability bits reported by device.
+// Used with ovrHmd_ConfigureRendering and ovrHmd_CreateDistortionMesh.
+typedef enum
+{
+ ovrDistortionCap_Chromatic = 0x01, // Supports chromatic aberration correction.
+ ovrDistortionCap_TimeWarp = 0x02, // Supports timewarp.
+ ovrDistortionCap_Vignette = 0x08 // Supports vignetting around the edges of the view.
+} ovrDistortionCaps;
+
+
+// Specifies which eye is being used for rendering.
+// This type explicitly does not include a third "NoStereo" option, as such is
+// not required for an HMD-centered API.
+typedef enum
+{
+ ovrEye_Left = 0,
+ ovrEye_Right = 1,
+ ovrEye_Count = 2
+} ovrEyeType;
+
+
+// Handle to HMD; returned by ovrHmd_Create.
+typedef struct ovrHmdStruct* ovrHmd;
+
+// This is a complete descriptor of the HMD.
+typedef struct ovrHmdDesc_
+{
+ ovrHmd Handle; // Handle of this HMD.
+ ovrHmdType Type;
+
+ // Name string describing the product: "Oculus Rift DK1", etc.
+ const char* ProductName;
+ const char* Manufacturer;
+
+ // Capability bits described by ovrHmdCaps.
+ unsigned int HmdCaps;
+ // Capability bits described by ovrSensorCaps.
+ unsigned int SensorCaps;
+ // Capability bits described by ovrDistortionCaps.
+ unsigned int DistortionCaps;
+
+ // Resolution of the entire HMD screen (for both eyes) in pixels.
+ ovrSizei Resolution;
+ // Where monitor window should be on screen or (0,0).
+ ovrVector2i WindowsPos;
+
+ // These define the recommended and maximum optical FOVs for the HMD.
+ ovrFovPort DefaultEyeFov[ovrEye_Count];
+ ovrFovPort MaxEyeFov[ovrEye_Count];
+
+ // Preferred eye rendering order for best performance.
+ // Can help reduce latency on sideways-scanned screens.
+ ovrEyeType EyeRenderOrder[ovrEye_Count];
+
+ // Display that HMD should present on.
+ // TBD: It may be good to remove this information relying on WidowPos instead.
+ // Ultimately, we may need to come up with a more convenient alternative,
+ // such as a API-specific functions that return adapter, ot something that will
+ // work with our monitor driver.
+
+ // Windows: "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC.
+ const char* DisplayDeviceName;
+ // MacOS
+ int DisplayId;
+} ovrHmdDesc;
+
+// Describes the type of positional tracking being done.
+/*
+typedef enum
+{
+ ovrPose_None,
+ ovrPose_HeadModel,
+ ovrPose_Positional
+} ovrPoseType;
+*/
+
+
+// Bit flags describing the current status of sensor tracking.
+typedef enum
+{
+ ovrStatus_OrientationTracked = 0x0001, // Orientation is currently tracked (connected and in use).
+ ovrStatus_PositionTracked = 0x0002, // Position is currently tracked (FALSE if out of range).
+ ovrStatus_PositionConnected = 0x0020, // Position tracking HW is connected.
+ ovrStatus_HmdConnected = 0x0080 // HMD Display is available & connected.
+} ovrStatusBits;
+
+
+// State of the sensor at a given absolute time.
+typedef struct ovrSensorState_
+{
+ // Predicted pose configuration at requested absolute time.
+ // One can determine the time difference between predicted and actual
+ // readings by comparing ovrPoseState.TimeInSeconds.
+ ovrPoseStatef Predicted;
+ // Actual recorded pose configuration based on the sensor sample at a
+ // moment closest to the requested time.
+ ovrPoseStatef Recorded;
+
+ // Sensor temperature reading, in degrees Celsius, as sample time.
+ float Temperature;
+ // Sensor status described by ovrStatusBits.
+ unsigned int StatusFlags;
+} ovrSensorState;
+
+// For now.
+// TBD: Decide if this becomes a part of HMDDesc
+typedef struct ovrSensorDesc_
+{
+ // HID Vendor and ProductId of the device.
+ short VendorId;
+ short ProductId;
+ // Sensor (and display) serial number.
+ char SerialNumber[24];
+} ovrSensorDesc;
+
+
+
+// Frame data reported by ovrHmd_BeginFrameTiming().
+typedef struct ovrFrameTiming_
+{
+ // The amount of time that has passed since the previous frame returned
+ // BeginFrameSeconds value, usable for movement scaling.
+ // This will be clamped to no more than 0.1 seconds to prevent
+ // excessive movement after pauses for loading or initialization.
+ float DeltaSeconds;
+
+ // It is generally expected that the following hold:
+ // ThisFrameSeconds < TimewarpPointSeconds < NextFrameSeconds <
+ // EyeScanoutSeconds[EyeOrder[0]] <= ScanoutMidpointSeconds <= EyeScanoutSeconds[EyeOrder[1]]
+
+ // Absolute time value of when rendering of this frame began or is expected to
+ // begin; generally equal to NextFrameSeconds of the previous frame. Can be used
+ // for animation timing.
+ double ThisFrameSeconds;
+ // Absolute point when IMU expects to be sampled for this frame.
+ double TimewarpPointSeconds;
+ // Absolute time when frame Present + GPU Flush will finish, and the next frame starts.
+ double NextFrameSeconds;
+
+ // Time when when half of the screen will be scanned out. Can be passes as a prediction
+ // value to ovrHmd_GetSensorState() go get general orientation.
+ double ScanoutMidpointSeconds;
+ // Timing points when each eye will be scanned out to display. Used for rendering each eye.
+ double EyeScanoutSeconds[2];
+
+} ovrFrameTiming;
+
+
+
+// Rendering information for each eye, computed by either ovrHmd_ConfigureRendering().
+// or ovrHmd_GetRenderDesc() based on the specified Fov.
+// Note that the rendering viewport is not included here as it can be
+// specified separately and modified per frame though:
+// (a) calling ovrHmd_GetRenderScaleAndOffset with game-rendered api,
+// or (b) passing different values in ovrTexture in case of SDK-rendered distortion.
+typedef struct ovrEyeRenderDesc_
+{
+ ovrEyeType Eye;
+ ovrFovPort Fov;
+ ovrRecti DistortedViewport; // Distortion viewport
+ ovrVector2f PixelsPerTanAngleAtCenter; // How many display pixels will fit in tan(angle) = 1.
+ ovrVector3f ViewAdjust; // Translation to be applied to view matrix.
+} ovrEyeRenderDesc;
+
+
+//-----------------------------------------------------------------------------------
+// ***** Platform-independent Rendering Configuration
+
+// These types are used to hide platform-specific details when passing
+// render device, OS and texture data to the APIs.
+//
+// The benefit of having these wrappers vs. platform-specific API functions is
+// that they allow game glue code to be portable. A typical example is an
+// engine that has multiple back ends, say GL and D3D. Portable code that calls
+// these back ends may also use LibOVR. To do this, back ends can be modified
+// to return portable types such as ovrTexture and ovrRenderAPIConfig.
+
+typedef enum
+{
+ ovrRenderAPI_None,
+ ovrRenderAPI_OpenGL,
+ ovrRenderAPI_Android_GLES, // May include extra native window pointers, etc.
+ ovrRenderAPI_D3D9,
+ ovrRenderAPI_D3D10,
+ ovrRenderAPI_D3D11,
+ ovrRenderAPI_Count
+} ovrRenderAPIType;
+
+// Platform-independent part of rendering API-configuration data.
+// It is a part of ovrRenderAPIConfig, passed to ovrHmd_Configure.
+typedef struct ovrRenderAPIConfigHeader_
+{
+ ovrRenderAPIType API;
+ ovrSizei RTSize;
+ int Multisample;
+} ovrRenderAPIConfigHeader;
+
+typedef struct ovrRenderAPIConfig_
+{
+ ovrRenderAPIConfigHeader Header;
+ uintptr_t PlatformData[8];
+} ovrRenderAPIConfig;
+
+// Platform-independent part of eye texture descriptor.
+// It is a part of ovrTexture, passed to ovrHmd_EndFrame.
+// - If RenderViewport is all zeros, will be used.
+typedef struct ovrTextureHeader_
+{
+ ovrRenderAPIType API;
+ ovrSizei TextureSize;
+ ovrRecti RenderViewport; // Pixel viewport in texture that holds eye image.
+} ovrTextureHeader;
+
+typedef struct ovrTexture_
+{
+ ovrTextureHeader Header;
+ uintptr_t PlatformData[8];
+} ovrTexture;
+
+
+// -----------------------------------------------------------------------------------
+// ***** API Interfaces
+
+// Basic steps to use the API:
+//
+// Setup:
+// 1. ovrInitialize();
+// 2. ovrHMD hmd = ovrHmd_Create(0); ovrHmd_GetDesc(hmd, &hmdDesc);
+// 3. Use hmdDesc and ovrHmd_GetFovTextureSize() to determine graphics configuration.
+// 4. Call ovrHmd_StartSensor() to configure and initialize tracking.
+// 5. Call ovrHmd_ConfigureRendering() to setup graphics for SDK rendering,
+// which is the preferred approach.
+// Please refer to "Game-Side Rendering" below if you prefer to do that instead.
+// 5. Allocate textures as needed.
+//
+// Game Loop:
+// 6. Call ovrHmd_BeginFrame() to get frame timing and orientation information.
+// 7. Render each eye in between ovrHmd_BeginEyeRender and ovrHmd_EndEyeRender calls,
+// providing the result texture to the API.
+// 8. Call ovrHmd_EndFrame() to render distorted textures to the back buffer
+// and present them on the Hmd.
+//
+// Shutdown:
+// 9. ovrHmd_Destroy(hmd)
+// 10. ovr_Shutdown()
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Library init/shutdown, must be called around all other OVR code.
+// No other functions calls are allowed before ovr_Initialize succeeds or after ovr_Shutdown.
+OVR_EXPORT ovrBool ovr_Initialize();
+OVR_EXPORT void ovr_Shutdown();
+
+
+// Detects or re-detects HMDs and reports the total number detected.
+// Users can get information about each HMD by calling ovrHmd_Create with an index.
+OVR_EXPORT int ovrHmd_Detect();
+
+
+// Creates a handle to an HMD and optionally fills in data about it.
+// Index can [0 .. ovrHmd_Detect()-1]; index mappings can cange after each ovrHmd_Detect call.
+// If not null, returned handle must be freed with ovrHmd_Destroy.
+OVR_EXPORT ovrHmd ovrHmd_Create(int index);
+OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmd);
+
+// Creates a "fake" HMD used for debugging only. This is not tied to specific hardware,
+// but may be used to debug some of the related rendering.
+OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type);
+
+
+// Returns last error for HMD state. Returns null for no error.
+// String is valid until next call or GetLastError or HMD is destroyed.
+// Pass null hmd to get global error (for create, etc).
+OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmd);
+
+
+//-------------------------------------------------------------------------------------
+
+// Returns capability bits that are enabled at this time; described by ovrHmdCaps.
+// Note that this value is different font ovrHmdDesc::HmdCaps, which describes what
+// capabilities are available.
+OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmd);
+
+// Modifies capability bits described by ovrHmdCaps that can be modified,
+// such as ovrHmd_LowPersistance.
+OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int hmdCaps);
+
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor Interface
+
+// All sensor interface functions are thread-safe, allowing sensor state to be sampled
+// from different threads.
+// Starts sensor sampling, enabling specified capabilities, described by ovrSensorCaps.
+// - supportedSensorCaps specifies support that is requested. The function will succeed
+// even if these caps are not available (i.e. sensor or camera is unplugged). Support
+// will automatically be enabled if such device is plugged in later. Software should
+// check ovrSensorState.StatusFlags for real-time status.
+// - requiredSensorCaps specify sensor capabilities required at the time of the call.
+// If they are not available, the function will fail. Pass 0 if only specifying
+// supportedSensorCaps.
+OVR_EXPORT ovrBool ovrHmd_StartSensor(ovrHmd hmd, unsigned int supportedSensorCaps,
+ unsigned int requiredSensorCaps);
+// Stops sensor sampling, shutting down internal resources.
+OVR_EXPORT void ovrHmd_StopSensor(ovrHmd hmd);
+// Resets sensor orientation.
+OVR_EXPORT void ovrHmd_ResetSensor(ovrHmd hmd);
+
+// Returns sensor state reading based on the specified absolute system time.
+// Pass absTime value of 0.0 to request the most recent sensor reading; in this case
+// both PredictedPose and SamplePose will have the same value.
+// ovrHmd_GetEyePredictedSensorState relies on this internally.
+// This may also be used for more refined timing of FrontBuffer rendering logic, etc.
+OVR_EXPORT ovrSensorState ovrHmd_GetSensorState(ovrHmd hmd, double absTime);
+
+// Returns information about a sensor.
+// Only valid after StartSensor.
+OVR_EXPORT ovrBool ovrHmd_GetSensorDesc(ovrHmd hmd, ovrSensorDesc* descOut);
+
+
+//-------------------------------------------------------------------------------------
+// ***** Graphics Setup
+
+// Fills in description about HMD; this is the same as filled in by ovrHmd_Create.
+OVR_EXPORT void ovrHmd_GetDesc(ovrHmd hmd, ovrHmdDesc* desc);
+
+// Calculates texture size recommended for rendering one eye within HMD, given FOV cone.
+// Higher FOV will generally require larger textures to maintain quality.
+// - pixelsPerDisplayPixel specifies that number of render target pixels per display
+// pixel at center of distortion; 1.0 is the default value. Lower values
+// can improve performance.
+OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov,
+ float pixelsPerDisplayPixel);
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Rendering API Thread Safety
+
+// All of rendering APIs, inclusing Configure and frame functions are *NOT
+// Thread Safe*. It is ok to use ConfigureRendering on one thread and handle
+// frames on another thread, but explicit synchronization must be done since
+// functions that depend on configured state are not reentrant.
+//
+// As an extra requirement, any of the following calls must be done on
+// the render thread, which is the same thread that calls ovrHmd_BeginFrame
+// or ovrHmd_BeginFrameTiming.
+// - ovrHmd_EndFrame
+// - ovrHmd_BeginEyeRender
+// - ovrHmd_EndEyeRender
+// - ovrHmd_GetFramePointTime
+// - ovrHmd_GetEyePose
+// - ovrHmd_GetEyeTimewarpMatrices
+
+
+//-------------------------------------------------------------------------------------
+// ***** SDK-Rendering Functions
+
+// These functions support rendering of distortion by the SDK through direct
+// access to the underlying rendering HW, such as D3D or GL.
+// This is the recommended approach, as it allows for better support or future
+// Oculus hardware and a range of low-level optimizations.
+
+
+// Configures rendering; fills in computed render parameters.
+// This function can be called multiple times to change rendering settings.
+// The users pass in two eye view descriptors that are used to
+// generate complete rendering information for each eye in eyeRenderDescOut[2].
+//
+// - apiConfig provides D3D/OpenGL specific parameters. Pass null
+// to shutdown rendering and release all resources.
+// - distortionCaps describe distortion settings that will be applied.
+//
+OVR_EXPORT ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd,
+ const ovrRenderAPIConfig* apiConfig,
+ unsigned int distortionCaps,
+ const ovrFovPort eyeFovIn[2],
+ ovrEyeRenderDesc eyeRenderDescOut[2] );
+
+
+// Begins a frame, returning timing and orientation information useful for simulation.
+// This should be called in the beginning of game rendering loop (on render thread).
+// This function relies on ovrHmd_BeginFrameTiming for some of its functionality.
+// Pass 0 for frame index if not using GetFrameTiming.
+OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex);
+
+// Ends frame, rendering textures to frame buffer. This may perform distortion and scaling
+// internally, assuming is it not delegated to another thread.
+// Must be called on the same thread as BeginFrame. Calls ovrHmd_BeginEndTiming internally.
+// *** This Function will to Present/SwapBuffers and potentially wait for GPU Sync ***.
+OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd);
+
+
+// Marks beginning of eye rendering. Must be called on the same thread as BeginFrame.
+// This function uses ovrHmd_GetEyePose to predict sensor state that should be
+// used rendering the specified eye.
+// This combines current absolute time with prediction that is appropriate for this HMD.
+// It is ok to call ovrHmd_BeginEyeRender() on both eyes before calling ovrHmd_EndEyeRender.
+// If rendering one eye at a time, it is best to render eye specified by
+// HmdDesc.EyeRenderOrder[0] first.
+OVR_EXPORT ovrPosef ovrHmd_BeginEyeRender(ovrHmd hmd, ovrEyeType eye);
+
+// Marks the end of eye rendering and submits the eye texture for display after it is ready.
+// Rendering viewport within the texture can change per frame if necessary.
+// Specified texture may be presented immediately or wait until ovrHmd_EndFrame based
+// on the implementation. The API performs distortion and scaling internally.
+// 'renderPose' will typically be the value returned from ovrHmd_BeginEyeRender, but can
+// be different if a different pose was used for rendering.
+OVR_EXPORT void ovrHmd_EndEyeRender(ovrHmd hmd, ovrEyeType eye,
+ ovrPosef renderPose, ovrTexture* eyeTexture);
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Game-Side Rendering Functions
+
+// These functions provide distortion data and render timing support necessary to allow
+// game rendering of distortion. Game-side rendering involves the following steps:
+//
+// 1. Setup ovrEyeDesc based on desired texture size and Fov.
+// Call ovrHmd_GetRenderDesc to get the necessary rendering parameters for each eye.
+//
+// 2. Use ovrHmd_CreateDistortionMesh to generate distortion mesh.
+//
+// 3. Use ovrHmd_BeginFrameTiming, ovrHmd_GetEyePose and ovrHmd_BeginFrameTiming
+// in the rendering loop to obtain timing and predicted view orientation for
+// each eye.
+// - If relying on timewarp, use ovr_WaitTillTime after rendering+flush, followed
+// by ovrHmd_GetEyeTimewarpMatrices to obtain timewarp matrices used
+// in distortion pixel shader to reduce latency.
+//
+
+// Computes distortion viewport, view adjust and other rendering for the specified
+// eye. This can be used instead of ovrHmd_ConfigureRendering to help setup rendering on
+// the game side.
+OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd,
+ ovrEyeType eyeType, ovrFovPort fov);
+
+
+// Describes a vertex used for distortion; this is intended to be converted into
+// the engine-specific format.
+// Some fields may be unused based on ovrDistortionCaps selected. TexG and TexB, for example,
+// are not used if chromatic correction is not requested.
+typedef struct ovrDistortionVertex_
+{
+ ovrVector2f Pos;
+ float TimeWarpFactor; // Lerp factor between time-warp matrices. Can be encoded in Pos.z.
+ float VignetteFactor; // Vignette fade factor. Can be encoded in Pos.w.
+ ovrVector2f TexR;
+ ovrVector2f TexG;
+ ovrVector2f TexB;
+} ovrDistortionVertex;
+
+// Describes a full set of distortion mesh data, filled in by ovrHmd_CreateDistortionMesh.
+// Contents of this data structure, if not null, should be freed by ovrHmd_DestroyDistortionMesh.
+typedef struct ovrDistortionMesh_
+{
+ ovrDistortionVertex* pVertexData;
+ unsigned short* pIndexData;
+ unsigned int VertexCount;
+ unsigned int IndexCount;
+} ovrDistortionMesh;
+
+// Generate distortion mesh per eye.
+// Distortion capabilities will depend on 'distortionCaps' flags; user should rely on
+// appropriate shaders based on their settings.
+// Distortion mesh data will be allocated and stored into the ovrDistortionMesh data structure,
+// which should be explicitly freed with ovrHmd_DestroyDistortionMesh.
+// Users should call ovrHmd_GetRenderScaleAndOffset to get uvScale and Offset values for rendering.
+// The function shouldn't fail unless theres is a configuration or memory error, in which case
+// ovrDistortionMesh values will be set to null.
+OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmd,
+ ovrEyeType eyeType, ovrFovPort fov,
+ unsigned int distortionCaps,
+ ovrDistortionMesh *meshData );
+
+// Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements
+// are set to null and zeroes after the call.
+OVR_EXPORT void ovrHmd_DestroyDistortionMesh( ovrDistortionMesh* meshData );
+
+// 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] );
+
+
+// Thread-safe timing function for the main thread. Caller should increment frameIndex
+// with every frame and pass the index to RenderThread for processing.
+OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex);
+
+// Called at the beginning of the frame on the Render Thread.
+// Pass frameIndex == 0 if ovrHmd_GetFrameTiming isn't being used. Otherwise,
+// pass the same frame index as was used for GetFrameTiming on the main thread.
+OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frameIndex);
+
+// Marks the end of game-rendered frame, tracking the necessary timing information. This
+// function must be called immediately after Present/SwapBuffers + GPU sync. GPU sync is important
+// before this call to reduce latency and ensure proper timing.
+OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmd);
+
+// Initializes and resets frame time tracking. This is typically not necessary, but
+// is helpful if game changes vsync state or video mode. vsync is assumed to be on if this
+// isn't called. Resets internal frame index to the specified number.
+OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex);
+
+
+// Predicts and returns Pose that should be used rendering the specified eye.
+// Must be called between ovrHmd_BeginFrameTiming & ovrHmd_EndFrameTiming.
+OVR_EXPORT ovrPosef ovrHmd_GetEyePose(ovrHmd hmd, ovrEyeType eye);
+
+// Computes timewarp matrices used by distortion mesh shader, these are used to adjust
+// for orientation change since the last call to ovrHmd_GetEyePose for this eye.
+// The ovrDistortionVertex::TimeWarpFactor is used to blend between the matrices,
+// usually representing two different sides of the screen.
+// Must be called on the same thread as ovrHmd_BeginFrameTiming.
+OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye,
+ ovrPosef renderPose, ovrMatrix4f twmOut[2]);
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Stateless math setup functions
+
+// Used to generate projection from ovrEyeDesc::Fov.
+OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection( ovrFovPort fov,
+ float znear, float zfar, ovrBool rightHanded );
+
+// Used for 2D rendering, Y is down
+// orthoScale = 1.0f / pixelsPerTanAngleAtCenter
+// orthoDistance = distance from camera, such as 0.8m
+OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale,
+ float orthoDistance, float eyeViewAdjustX);
+
+// Returns global, absolute high-resolution time in seconds. This is the same
+// value as used in sensor messages.
+OVR_EXPORT double ovr_GetTimeInSeconds();
+
+// Waits until the specified absolute time.
+OVR_EXPORT double ovr_WaitTillTime(double absTime);
+
+
+
+// -----------------------------------------------------------------------------------
+// ***** Latency Test interface
+
+// Does latency test processing and returns 'TRUE' if specified rgb color should
+// be used to clear the screen.
+OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmd, unsigned char rgbColorOut[3]);
+
+// Returns non-null string once with latency test result, when it is available.
+// Buffer is valid until next call.
+OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmd);
+
+// Returns latency for HMDs that support internal latency testing via the
+// pixel-read back method (-1 for invalid or N/A)
+OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmd);
+
+
+// -----------------------------------------------------------------------------------
+// ***** Property Access
+
+// NOTICE: This is experimental part of API that is likely to go away or change.
+
+// These allow accessing different properties of the HMD and profile.
+// Some of the properties may go away with profile/HMD versions, so software should
+// use defaults and/or proper fallbacks.
+//
+
+// For now, access profile entries; this will change.
+#if !defined(OVR_KEY_USER)
+
+ #define OVR_KEY_USER "User"
+ #define OVR_KEY_NAME "Name"
+ #define OVR_KEY_GENDER "Gender"
+ #define OVR_KEY_PLAYER_HEIGHT "PlayerHeight"
+ #define OVR_KEY_EYE_HEIGHT "EyeHeight"
+ #define OVR_KEY_IPD "IPD"
+ #define OVR_KEY_NECK_TO_EYE_HORIZONTAL "NeckEyeHori"
+ #define OVR_KEY_NECK_TO_EYE_VERTICAL "NeckEyeVert"
+
+ #define OVR_DEFAULT_GENDER "Male"
+ #define OVR_DEFAULT_PLAYER_HEIGHT 1.778f
+ #define OVR_DEFAULT_EYE_HEIGHT 1.675f
+ #define OVR_DEFAULT_IPD 0.064f
+ #define OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL 0.12f
+ #define OVR_DEFAULT_NECK_TO_EYE_VERTICAL 0.12f
+#endif
+
+
+// Get float property. Returns first element if property is a float array.
+// Returns defaultValue if property doesn't exist.
+OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal);
+
+// Modify float property; false if property doesn't exist or is readonly.
+OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value);
+
+
+// Get float[] property. Returns the number of elements filled in, 0 if property doesn't exist.
+// Maximum of arraySize elements will be written.
+OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName,
+ float values[], unsigned int arraySize);
+
+// 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);
+
+// Get string property. Returns first element if property is a string array.
+// Returns defaultValue if property doesn't exist.
+// String memory is guaranteed to exist until next call to GetString or GetStringArray, or HMD is destroyed.
+OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmd, const char* propertyName,
+ const char* defaultVal);
+
+// 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);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+
+#endif // OVR_CAPI_h
diff --git a/LibOVR/Src/OVR_CAPI_GL.h b/LibOVR/Src/OVR_CAPI_GL.h
new file mode 100644
index 0000000..ceabb74
--- /dev/null
+++ b/LibOVR/Src/OVR_CAPI_GL.h
@@ -0,0 +1,73 @@
+/************************************************************************************
+
+Filename : OVR_CAPI_GL.h
+Content : GL specific structures used by the CAPI interface.
+Created : November 7, 2013
+Authors : Lee Cooper
+
+Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
+
+Use of this software is subject to the terms of the Oculus Inc license
+agreement provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+************************************************************************************/
+#ifndef OVR_CAPI_GL_h
+#define OVR_CAPI_GL_h
+
+#include "OVR_CAPI.h"
+
+//-----------------------------------------------------------------------------------
+// ***** GL Specific
+
+#if defined(OVR_OS_WIN32)
+ #include <Windows.h>
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+ #include <GL/wglext.h>
+#elif defined(OVR_OS_MAC)
+ #include <OpenGL/gl3.h>
+ #include <OpenGL/gl3ext.h>
+ #include <OpenGL/OpenGL.h>
+#else
+ #include <GL/gl.h>
+ #include <GL/glext.h>
+ #include <GL/glx.h>
+#endif
+
+
+// Used to configure slave GL rendering (i.e. for devices created externally).
+typedef struct ovrGLConfigData_s
+{
+ // General device settings.
+ ovrRenderAPIConfigHeader Header;
+
+#if defined(OVR_OS_WIN32)
+ HWND Window;
+#elif defined(OVR_OS_LINUX)
+ Display* Disp;
+ Window Win;
+#endif
+} ovrGLConfigData;
+
+union ovrGLConfig
+{
+ ovrRenderAPIConfig Config;
+ ovrGLConfigData OGL;
+};
+
+// Used to pass GL eye texture data to ovrHmd_EndFrame.
+typedef struct ovrGLTextureData_s
+{
+ // General device settings.
+ ovrTextureHeader Header;
+ GLuint TexId;
+} ovrGLTextureData;
+
+typedef union ovrGLTexture_s
+{
+ ovrTexture Texture;
+ ovrGLTextureData OGL;
+} ovrGLTexture;
+
+#endif // OVR_CAPI_GL_h
diff --git a/LibOVR/Src/OVR_Common_HMDDevice.cpp b/LibOVR/Src/OVR_Common_HMDDevice.cpp
new file mode 100644
index 0000000..6bedcbe
--- /dev/null
+++ b/LibOVR/Src/OVR_Common_HMDDevice.cpp
@@ -0,0 +1,384 @@
+/************************************************************************************
+
+Filename : OVR_Common_HMDDevice.cpp
+Content :
+Created :
+Authors :
+
+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.
+
+*************************************************************************************/
+
+// Should be #included from the relevant OVR_YourPlatformHere_HMDDevice.cpp
+
+#include "Kernel/OVR_Alg.h"
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceCreateDesc
+
+DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance()
+{
+ return new HMDDevice(this);
+}
+
+void HMDDeviceCreateDesc::SetScreenParameters(int x, int y,
+ int hres, int vres,
+ float hsize, float vsize,
+ float vCenterFromTopInMeters, float lensSeparationInMeters)
+{
+ Desktop.X = x;
+ Desktop.Y = y;
+ ResolutionInPixels = Sizei(hres, vres);
+ ScreenSizeInMeters = Sizef(hsize, vsize);
+ VCenterFromTopInMeters = vCenterFromTopInMeters;
+ LensSeparationInMeters = lensSeparationInMeters;
+
+ Contents |= Contents_Screen;
+}
+
+
+void HMDDeviceCreateDesc::SetDistortion(const float* dks)
+{
+ for (int i = 0; i < 4; i++)
+ DistortionK[i] = dks[i];
+ // TODO: add DistortionEqn
+ Contents |= Contents_Distortion;
+}
+
+HmdTypeEnum HMDDeviceCreateDesc::GetHmdType() const
+{
+ // Determine the HMD model
+ // The closest thing we have to a dependable model indicator are the
+ // the screen characteristics. Additionally we can check the sensor
+ // (on attached devices) to further refine our guess
+ HmdTypeEnum hmdType = HmdType_Unknown;
+
+ if ( ResolutionInPixels.w == 1280 )
+ {
+ if ( ScreenSizeInMeters.w > 0.1497f && ScreenSizeInMeters.w < 0.1498f )
+ hmdType = HmdType_DK1;
+ else
+ hmdType = HmdType_DKProto;
+ }
+ else if ( ResolutionInPixels.w == 1920 )
+ {
+ // DKHD protoypes, all 1920x1080
+ if ( ScreenSizeInMeters.w > 0.1209f && ScreenSizeInMeters.w < 0.1210f )
+ {
+ // Screen size 0.12096 x 0.06804
+ hmdType = HmdType_DKHDProto;
+ }
+ else if ( ScreenSizeInMeters.w > 0.1257f && ScreenSizeInMeters.w < 0.1258f )
+ {
+ // Screen size 0.125 x 0.071
+ // Could be a HmdType_DKHDProto566Mi, HmdType_CrystalCoveProto, or DK2
+ // - most likely the latter.
+ hmdType = HmdType_DK2;
+
+ // If available, check the sensor to determine exactly which variant this is
+ if (pDevice)
+ {
+ Ptr<SensorDevice> sensor = *((HMDDevice*)pDevice)->GetSensor();
+
+ SensorInfo sinfo;
+ if (sensor && sensor->GetDeviceInfo(&sinfo))
+ {
+ if (sinfo.ProductId == 1)
+ {
+ hmdType = HmdType_DKHDProto566Mi;
+ }
+ else
+ { // Crystal Cove uses 0.# firmware, DK2 uses 1.#
+ int firm_major = Alg::DecodeBCD((sinfo.Version >> 8) & 0x00ff);
+ int firm_minor = Alg::DecodeBCD(sinfo.Version & 0xff);
+ OVR_UNUSED(firm_minor);
+ if (firm_major == 0)
+ hmdType = HmdType_CrystalCoveProto;
+ else
+ hmdType = HmdType_DK2;
+ }
+ }
+ }
+ }
+ else if (ScreenSizeInMeters.w > 0.1295f && ScreenSizeInMeters.w < 0.1297f)
+ {
+ // Screen size 0.1296 x 0.0729
+ hmdType = HmdType_DKHD2Proto;
+ }
+ }
+
+ OVR_ASSERT( hmdType != HmdType_Unknown );
+ return hmdType;
+}
+
+bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_HMD) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ HmdTypeEnum hmdType = GetHmdType();
+ char const* deviceName = "Oculus HMD";
+ switch (hmdType)
+ {
+ case HmdType_DKProto: deviceName = "Oculus Rift Prototype"; break;
+ case HmdType_DK1: deviceName = "Oculus Rift DK1"; break;
+ case HmdType_DKHDProto: deviceName = "Oculus Rift DKHD"; break;
+ case HmdType_DKHD2Proto: deviceName = "Oculus Rift DKHD2"; break;
+ case HmdType_DKHDProto566Mi: deviceName = "Oculus Rift DKHD 566 Mi"; break;
+ case HmdType_CrystalCoveProto: deviceName = "Oculus Rift Crystal Cove"; break;
+ case HmdType_DK2: deviceName = "Oculus Rift DK2"; break;
+ default: deviceName = "Oculus HMD"; break;
+ }
+
+ info->ProductName = deviceName;
+ info->Manufacturer = "Oculus VR";
+ info->Type = Device_HMD;
+ info->Version = 0;
+
+ // Display detection.
+ if (info->InfoClassType == Device_HMD)
+ {
+ HMDInfo* hmdInfo = static_cast<HMDInfo*>(info);
+
+ hmdInfo->HmdType = hmdType;
+ hmdInfo->DesktopX = Desktop.X;
+ hmdInfo->DesktopY = Desktop.Y;
+ hmdInfo->ResolutionInPixels = ResolutionInPixels;
+ hmdInfo->ScreenSizeInMeters = ScreenSizeInMeters; // Includes ScreenGapSizeInMeters
+ hmdInfo->ScreenGapSizeInMeters = 0.0f;
+ hmdInfo->CenterFromTopInMeters = VCenterFromTopInMeters;
+ hmdInfo->LensSeparationInMeters = LensSeparationInMeters;
+ // TODO: any other information we get from the hardware itself should be added to this list
+
+ switch ( hmdInfo->HmdType )
+ {
+ case HmdType_DKProto:
+ // WARNING - estimated.
+ hmdInfo->Shutter.Type = HmdShutter_RollingTopToBottom;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.000052f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.016580f;
+ hmdInfo->Shutter.PixelSettleTime = 0.015f; // estimated.
+ hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence
+ break;
+ case HmdType_DK1:
+ // Data from specs.
+ hmdInfo->Shutter.Type = HmdShutter_RollingTopToBottom;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.00018226f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.01620089f;
+ hmdInfo->Shutter.PixelSettleTime = 0.017f; // estimated.
+ hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence
+ break;
+ case HmdType_DKHDProto:
+ // Data from specs.
+ hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.0000859f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0164948f;
+ hmdInfo->Shutter.PixelSettleTime = 0.012f; // estimated.
+ hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence
+ break;
+ case HmdType_DKHD2Proto:
+ // Data from specs.
+ hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.000052f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.016580f;
+ hmdInfo->Shutter.PixelSettleTime = 0.015f; // estimated.
+ hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence
+ break;
+ case HmdType_DKHDProto566Mi:
+#if 0
+ // Low-persistence global shutter
+ hmdInfo->Shutter.Type = HmdShutter_Global;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f + 0.0131033f; // Global shutter - first visible scan line is actually the last!
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.000f; // Global shutter - all visible at once.
+ hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us
+ hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame.
+#else
+ // Low-persistence rolling shutter
+ hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f;
+ hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us
+ hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame.
+#endif
+ break;
+ case HmdType_CrystalCoveProto:
+ // Low-persistence rolling shutter
+ hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f;
+ hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us
+ hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame.
+ break;
+ case HmdType_DK2:
+ // Low-persistence rolling shutter
+ hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft;
+ hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f;
+ hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f;
+ hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us
+ hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame.
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+
+ OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName),
+ DisplayDeviceName.ToCStr());
+#if defined(OVR_OS_WIN32)
+ // Nothing special for Win32.
+#elif defined(OVR_OS_MAC)
+ hmdInfo->DisplayId = DisplayId;
+#elif defined(OVR_OS_LINUX)
+ hmdInfo->DisplayId = DisplayId;
+#elif defined(OVR_OS_ANDROID)
+ hmdInfo->DisplayId = DisplayId;
+#else
+#error Unknown platform
+#endif
+
+ }
+
+ return true;
+}
+
+
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDevice
+
+HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc)
+ : OVR::DeviceImpl<OVR::HMDDevice>(createDesc, 0)
+{
+}
+HMDDevice::~HMDDevice()
+{
+}
+
+bool HMDDevice::Initialize(DeviceBase* parent)
+{
+ pParent = parent;
+ return true;
+}
+void HMDDevice::Shutdown()
+{
+ ProfileName.Clear();
+ pCachedProfile.Clear();
+ pParent.Clear();
+}
+
+Profile* HMDDevice::GetProfile()
+{
+ // Loads and returns a cached profile based on this device and current user
+ if (pCachedProfile == NULL)
+ {
+ ProfileManager* mgr = GetManager()->GetProfileManager();
+ const char* profile_name = GetProfileName();
+ if (profile_name && profile_name[0])
+ pCachedProfile = *mgr->GetProfile(this, profile_name);
+
+ if (pCachedProfile == NULL)
+ pCachedProfile = *mgr->GetDefaultProfile(this);
+
+ }
+ return pCachedProfile.GetPtr();
+}
+
+const char* HMDDevice::GetProfileName()
+{
+ if (ProfileName.IsEmpty())
+ { // If the profile name has not been initialized then
+ // retrieve the stored default user for this specific device
+ ProfileManager* mgr = GetManager()->GetProfileManager();
+ const char* name = mgr->GetDefaultUser(this);
+ ProfileName = name;
+ }
+
+ return ProfileName.ToCStr();
+}
+
+bool HMDDevice::SetProfileName(const char* name)
+{
+ if (ProfileName == name)
+ return true; // already set
+
+ // Flush the old profile
+ pCachedProfile.Clear();
+ if (!name)
+ {
+ ProfileName.Clear();
+ return false;
+ }
+
+ // Set the name and attempt to cache the profile
+ ProfileName = name;
+ if (GetProfile())
+ {
+ return true;
+ }
+ else
+ {
+ ProfileName.Clear();
+ return false;
+ }
+}
+
+OVR::SensorDevice* HMDDevice::GetSensor()
+{
+ // Just return first sensor found since we have no way to match it yet.
+
+ // Create DK2 sensor if it exists otherwise create first DK1 sensor.
+ SensorDevice* sensor = NULL;
+
+ DeviceEnumerator<SensorDevice> enumerator = GetManager()->EnumerateDevices<SensorDevice>();
+
+ while(enumerator.GetType() != Device_None)
+ {
+ SensorInfo info;
+ enumerator.GetDeviceInfo(&info);
+
+ if (info.ProductId == Device_Tracker2_ProductId)
+ {
+ sensor = enumerator.CreateDevice();
+ break;
+ }
+
+ enumerator.Next();
+ }
+
+ if (sensor == NULL)
+ {
+ sensor = GetManager()->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+ if (sensor)
+ {
+ sensor->SetCoordinateFrame(SensorDevice::Coord_HMD);
+ }
+
+ return sensor;
+}
diff --git a/LibOVR/Src/OVR_Device.h b/LibOVR/Src/OVR_Device.h
new file mode 100644
index 0000000..52a41f9
--- /dev/null
+++ b/LibOVR/Src/OVR_Device.h
@@ -0,0 +1,1135 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Device.h
+Content : Definition of HMD-related Device interfaces
+Created : September 21, 2012
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Device_h
+#define OVR_Device_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+#include "OVR_DeviceMessages.h"
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+
+
+namespace OVR {
+
+// Declared externally
+class Profile;
+class ProfileManager; // << Should be renamed for consistency
+
+// Forward declarations
+class SensorDevice;
+class DeviceCommon;
+class DeviceManager;
+
+// MessageHandler is a base class from which users derive to receive messages,
+// its OnMessage handler will be called for messages once it is installed on
+// a device. Same message handler can be installed on multiple devices.
+class MessageHandler
+{
+ friend class MessageHandlerImpl;
+public:
+ MessageHandler();
+ virtual ~MessageHandler();
+
+ // Returns 'true' if handler is currently installed on any devices.
+ bool IsHandlerInstalled() const;
+
+ // Should be called from derived class destructor to avoid handler
+ // being called after it exits.
+ void RemoveHandlerFromDevices();
+
+ // Returns a pointer to the internal lock object that is locked by a
+ // background thread while OnMessage() is called.
+ // This lock guaranteed to survive until ~MessageHandler.
+ Lock* GetHandlerLock() const;
+
+
+ virtual void OnMessage(const Message&) { }
+
+ // Determines if handler supports a specific message type. Can
+ // be used to filter out entire message groups. The result
+ // returned by this function shouldn't change after handler creation.
+ virtual bool SupportsMessageType(MessageType) const { return true; }
+
+private:
+ UPInt Internal[8];
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+// DeviceBase is the base class for all OVR Devices. It provides the following basic
+// functionality:
+// - Reports device type, manager, and associated parent (if any).
+// - Supports installable message handlers, which are notified of device events.
+// - Device objects are created through DeviceHandle::CreateDevice or more commonly
+// through DeviceEnumerator<>::CreateDevice.
+// - Created devices are reference counted, starting with RefCount of 1.
+// - Device is resources are cleaned up when it is Released, although its handles
+// may survive longer if referenced.
+
+class DeviceBase : public NewOverrideBase
+{
+ friend class DeviceHandle;
+ friend class DeviceManagerImpl;
+public:
+
+ // Enumerating DeviceBase enumerates all devices.
+ enum { EnumDeviceType = Device_All };
+
+ virtual ~DeviceBase() { }
+ virtual void AddRef();
+ virtual void Release();
+
+ virtual DeviceBase* GetParent() const;
+ virtual DeviceManager* GetManager() const;
+
+ virtual void AddMessageHandler(MessageHandler* handler);
+
+ virtual DeviceType GetType() const;
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ // Returns true if device is connected and usable
+ virtual bool IsConnected();
+
+ // returns the MessageHandler's lock
+ Lock* GetHandlerLock() const;
+protected:
+ // Internal
+ virtual DeviceCommon* getDeviceCommon() const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceInfo
+
+// DeviceInfo describes a device and its capabilities, obtained by calling
+// GetDeviceInfo. This base class only contains device-independent functionality;
+// users will normally use a derived HMDInfo or SensorInfo classes for more
+// extensive device info.
+
+class DeviceInfo
+{
+public:
+ DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0)
+ {}
+
+ // Type of device for which DeviceInfo is intended.
+ // This will be set to Device_HMD for HMDInfo structure, note that this may be
+ // different form the actual device type since (Device_None) is valid.
+ const DeviceType InfoClassType;
+ // Type of device this describes. This must be the same as InfoClassType when
+ // InfoClassType != Device_None.
+ DeviceType Type;
+ // Name string describing the product: "Oculus Rift DK1", etc.
+ String ProductName;
+ String Manufacturer;
+ unsigned Version;
+
+protected:
+ DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0)
+ {}
+ void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx.
+class DeviceEnumerationArgs
+{
+public:
+ DeviceEnumerationArgs(DeviceType enumType, bool availableOnly)
+ : EnumType(enumType), AvailableOnly(availableOnly)
+ { }
+
+ // Helper; returns true if args match our enumeration criteria.
+ bool MatchRule(DeviceType type, bool available) const
+ {
+ return ((EnumType == type) || (EnumType == Device_All)) &&
+ (available || !AvailableOnly);
+ }
+
+protected:
+ DeviceType EnumType;
+ bool AvailableOnly;
+};
+
+
+// DeviceEnumerator<> is used to enumerate and create devices of specified class,
+// it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will
+// refer to the first device of specified type. Additional devices can be accessed by
+// calling Next().
+
+template<class T = DeviceBase>
+class DeviceEnumerator : public DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+public:
+ DeviceEnumerator()
+ : DeviceHandle(), EnumArgs(Device_None, true) { }
+
+ // Next advances enumeration to the next device that first criteria.
+ // Returns false if no more devices exist that match enumeration criteria.
+ bool Next() { return enumerateNext(EnumArgs); }
+
+ // Creates an instance of the device referenced by enumerator; returns null
+ // if enumerator does not refer to a valid device or device is unavailable.
+ // If device was already created, the same object with incremented ref-count is returned.
+ T* CreateDevice() { return static_cast<T*>(DeviceHandle::CreateDevice()); }
+
+protected:
+ DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args)
+ : DeviceHandle(dev), EnumArgs(args)
+ { }
+
+ DeviceEnumerationArgs EnumArgs;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager
+
+// DeviceManager maintains and provides access to devices supported by OVR, such as
+// HMDs and sensors. A single instance of DeviceManager is normally created at
+// program startup, allowing devices to be enumerated and created. DeviceManager is
+// reference counted and is AddRefed by its created child devices, causing it to
+// always be the last object that is released.
+//
+// Install MessageHandler on DeviceManager to detect when devices are inserted or removed.
+//
+// The following code will create the manager and its first available HMDDevice,
+// and then release it when not needed:
+//
+// DeviceManager* manager = DeviceManager::Create();
+// HMDDevice* hmd = manager->EnumerateDevices<HMDDevice>().CreateDevice();
+//
+// if (hmd) hmd->Release();
+// if (manager) manager->Release();
+
+
+class DeviceManager : public DeviceBase
+{
+public:
+
+ DeviceManager()
+ { }
+
+ // DeviceBase implementation.
+ virtual DeviceType GetType() const { return Device_Manager; }
+ virtual DeviceManager* GetManager() const { return const_cast<DeviceManager*>(this); }
+
+ // Every DeviceManager has an associated profile manager, which us used to store
+ // user settings that may affect device behavior.
+ virtual ProfileManager* GetProfileManager() const = 0;
+
+
+ // EnumerateDevices enumerates all of the available devices of the specified class,
+ // returning an enumerator that references the first device. An empty enumerator is
+ // returned if no devices are available. The following APIs are exposed through
+ // DeviceEnumerator:
+ // DeviceEnumerator::GetType() - Check device type. Returns Device_None
+ // if no device was found/pointed to.
+ // DeviceEnumerator::GetDeviceInfo() - Get more information on device.
+ // DeviceEnumerator::CreateDevice() - Create an instance of device.
+ // DeviceEnumerator::Next() - Move onto next device.
+ template<class D>
+ DeviceEnumerator<D> EnumerateDevices(bool availableOnly = true)
+ {
+ // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle.
+ DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly));
+ return *reinterpret_cast<DeviceEnumerator<D>*>(&e);
+ }
+
+ // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating
+ // devices based on dynamically specified DeviceType in DeviceEnumerationArgs.
+ // End users should call DeumerateDevices<>() instead.
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0;
+
+ // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time.
+ static DeviceManager* Create();
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Manager };
+
+
+
+ // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0;
+
+protected:
+ DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args)
+ { return DeviceEnumerator<>(h, args); }
+
+ DeviceManager* getThis() { return this; }
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** HMDInfo
+
+// This structure describes various aspects of the HMD allowing us to configure rendering.
+//
+// Currently included data:
+// - Physical screen dimensions, resolution, and eye distances.
+// (some of these will be configurable with a tool in the future).
+// These arguments allow us to properly setup projection across HMDs.
+// - DisplayDeviceName for identifying HMD screen; system-specific interpretation.
+//
+// TBD:
+// - Power on/ off?
+// - Sensor rates and capabilities
+// - Distortion radius/variables
+// - Screen update frequency
+// - Distortion needed flag
+// - Update modes:
+// Set update mode: Stereo (both sides together), mono (same in both eyes),
+// Alternating, Alternating scan-lines.
+
+class HMDInfo : public DeviceInfo
+{
+public:
+ // Characteristics of the HMD screen and enclosure
+ HmdTypeEnum HmdType;
+ Size<int> ResolutionInPixels;
+ Size<float> ScreenSizeInMeters;
+ float ScreenGapSizeInMeters;
+ float CenterFromTopInMeters;
+ float LensSeparationInMeters;
+
+ // Timing & shutter data. All values in seconds.
+ struct ShutterInfo
+ {
+ HmdShutterTypeEnum Type;
+ float VsyncToNextVsync; // 1/framerate
+ float VsyncToFirstScanline; // for global shutter, vsync->shutter open.
+ float FirstScanlineToLastScanline; // for global shutter, will be zero.
+ float PixelSettleTime; // estimated.
+ float PixelPersistence; // Full persistence = 1/framerate.
+ } Shutter;
+
+ // Desktop coordinate position of the screen (can be negative; may not be present on all platforms)
+ int DesktopX;
+ int DesktopY;
+
+ // Windows:
+ // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC.
+ char DisplayDeviceName[32];
+
+ // MacOS:
+ int DisplayId;
+
+
+ // Constructor initializes all values to 0s.
+ // To create a "virtualized" HMDInfo, use CreateDebugHMDInfo instead.
+ HMDInfo()
+ : DeviceInfo(Device_HMD),
+ HmdType(HmdType_None),
+ ResolutionInPixels(0),
+ ScreenSizeInMeters(0.0f),
+ ScreenGapSizeInMeters(0.0f),
+ CenterFromTopInMeters(0),
+ LensSeparationInMeters(0),
+ DisplayId(0)
+ {
+ DesktopX = 0;
+ DesktopY = 0;
+ DisplayDeviceName[0] = 0;
+ Shutter.Type = HmdShutter_LAST;
+ Shutter.VsyncToNextVsync = 0.0f;
+ Shutter.VsyncToFirstScanline = 0.0f;
+ Shutter.FirstScanlineToLastScanline = 0.0f;
+ Shutter.PixelSettleTime = 0.0f;
+ Shutter.PixelPersistence = 0.0f;
+ }
+
+ // Operator = copies local fields only (base class must be correct already)
+ void operator = (const HMDInfo& src)
+ {
+ HmdType = src.HmdType;
+ ResolutionInPixels = src.ResolutionInPixels;
+ ScreenSizeInMeters = src.ScreenSizeInMeters;
+ ScreenGapSizeInMeters = src.ScreenGapSizeInMeters;
+ CenterFromTopInMeters = src.CenterFromTopInMeters;
+ LensSeparationInMeters = src.LensSeparationInMeters;
+ DesktopX = src.DesktopX;
+ DesktopY = src.DesktopY;
+ Shutter = src.Shutter;
+ memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName));
+
+ DisplayId = src.DisplayId;
+ }
+
+ bool IsSameDisplay(const HMDInfo& o) const
+ {
+ return DisplayId == o.DisplayId &&
+ String::CompareNoCase(DisplayDeviceName,
+ o.DisplayDeviceName) == 0;
+ }
+
+};
+
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then.
+
+// TBD:
+// - Configure Sensor
+// - APIs to set On-Screen message, other states?
+
+class HMDDevice : public DeviceBase
+{
+public:
+ HMDDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_HMD };
+
+ virtual DeviceType GetType() const { return Device_HMD; }
+
+ // Creates a sensor associated with this HMD.
+ virtual SensorDevice* GetSensor() = 0;
+
+
+ // Requests the currently used profile. This profile affects the
+ // settings reported by HMDInfo.
+ virtual Profile* GetProfile() = 0;
+ // Obtains the currently used profile name. This is initialized to the default
+ // profile name, if any; it can then be changed per-device by SetProfileName.
+ virtual const char* GetProfileName() = 0;
+ // Sets the profile user name, changing the data returned by GetProfileInfo.
+ virtual bool SetProfileName(const char* name) = 0;
+
+
+ // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD.
+ // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL).
+ HMDDevice* Disconnect(SensorDevice*);
+
+ // Returns 'true' if HMD device is a 'fake' HMD (was created this way or
+ // 'Disconnect' method was called).
+ bool IsDisconnected() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorRange & SensorInfo
+
+// SensorRange specifies maximum value ranges that SensorDevice hardware is configured
+// to detect. Although this range doesn't affect the scale of MessageBodyFrame values,
+// physical motions whose positive or negative magnitude is outside the specified range
+// may get clamped or misreported. Setting lower values may result in higher precision
+// tracking.
+struct SensorRange
+{
+ SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f,
+ float maxMagneticField = 0.0f)
+ : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate),
+ MaxMagneticField(maxMagneticField)
+ { }
+
+ // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed,
+ // where G is ~9.81 m/s^2.
+ // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G.
+ float MaxAcceleration;
+ // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed.
+ // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default).
+ float MaxRotationRate;
+ // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed.
+ // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss.
+ float MaxMagneticField;
+};
+
+// SensorInfo describes capabilities of the sensor device.
+class SensorInfo : public DeviceInfo
+{
+public:
+ SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0)
+ {
+ }
+
+ // HID Vendor and ProductId of the device.
+ UInt16 VendorId;
+ UInt16 ProductId;
+ // MaxRanges report maximum sensor range values supported by HW.
+ SensorRange MaxRanges;
+ // Sensor (and display) serial number.
+ String SerialNumber;
+
+private:
+ void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed.
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Serial Number feature report. (DK1)
+struct SerialReport
+{
+ static const int SERIAL_NUMBER_SIZE = 12; // Serial Number size = 12 bytes. (Refer 'Tracker Firmware Specification Section 4.9, Pg 18)
+
+ SerialReport()
+ : CommandId(0)
+ {
+ memset(SerialNumberValue, 0, sizeof(SerialNumberValue));
+ }
+
+ SerialReport(UInt16 commandId,
+ UByte SNo[SERIAL_NUMBER_SIZE])
+ : CommandId(commandId)
+ {
+ for (int i=0; i < SERIAL_NUMBER_SIZE; i++)
+ {
+ SerialNumberValue[i] = SNo[i];
+ }
+ }
+
+ UInt16 CommandId;
+ UByte SerialNumberValue[SERIAL_NUMBER_SIZE]; // See 'Tracker Firmware Specification' document for
+ // a description of Serial Report.
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//Added Serial Report Implementation.
+
+struct SerialImpl
+{
+ enum { PacketSize = 15 };
+ UByte Buffer[PacketSize];
+
+ SerialReport Settings;
+
+ SerialImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 10;
+ }
+
+ SerialImpl(const SerialReport& settings)
+ :Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 10;
+ Alg::EncodeUInt16(Buffer+1, Settings.CommandId);
+ for (int i = 0; i < Settings.SERIAL_NUMBER_SIZE; ++i)
+ Buffer[3 + i] = Settings.SerialNumberValue[i];
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = Alg::DecodeUInt16(Buffer+1);
+ for (int i = 0; i < Settings.SERIAL_NUMBER_SIZE; ++i)
+ Settings.SerialNumberValue[i] = Buffer[3 + i];
+ }
+
+};
+
+
+// Tracking settings (DK2).
+struct TrackingReport
+{
+ TrackingReport()
+ : CommandId(0), Pattern(0),
+ Enable(0), Autoincrement(0), UseCarrier(0),
+ SyncInput(0), VsyncLock(0), CustomPattern(0),
+ ExposureLength(0), FrameInterval(0),
+ VsyncOffset(0), DutyCycle(0)
+ {}
+
+ TrackingReport( UInt16 commandId,
+ UByte pattern,
+ bool enable,
+ bool autoincrement,
+ bool useCarrier,
+ bool syncInput,
+ bool vsyncLock,
+ bool customPattern,
+ UInt16 exposureLength,
+ UInt16 frameInterval,
+ UInt16 vsyncOffset,
+ UByte dutyCycle)
+ : CommandId(commandId), Pattern(pattern),
+ Enable(enable), Autoincrement(autoincrement), UseCarrier(useCarrier),
+ SyncInput(syncInput), VsyncLock(vsyncLock), CustomPattern(customPattern),
+ ExposureLength(exposureLength), FrameInterval(frameInterval),
+ VsyncOffset(vsyncOffset), DutyCycle(dutyCycle)
+ { }
+
+ UInt16 CommandId;
+ UByte Pattern; // Tracking LED pattern index.
+ bool Enable; // Enables the tracking LED exposure and updating.
+ bool Autoincrement; // Autoincrement pattern after each exposure.
+ bool UseCarrier; // Modulate tracking LEDs at 85kHz.
+ bool SyncInput; // Trigger LED exposure from wired sync signal.
+ bool VsyncLock; // Trigger LED exposure from panel Vsync.
+ bool CustomPattern; // Use custom LED sequence.
+ UInt16 ExposureLength; // Tracking LED illumination (and exposure) length in microseconds.
+ UInt16 FrameInterval; // LED exposure interval in microseconds when in
+ // 'internal timer' mode (when SyncInput = VsyncLock = false).
+ UInt16 VsyncOffset; // Exposure offset in microseconds from vsync when in
+ // 'vsync lock' mode (when VsyncLock = true).
+ UByte DutyCycle; // Duty cycle of 85kHz modulation when in 'use carrier' mode
+ // (when UseCarrier = true). 128 = 50% duty cycle.
+};
+
+// Display settings (DK2).
+struct DisplayReport
+{
+ enum ShutterTypeEnum
+ {
+ // These are not yet defined.
+ ShutterType_Default = 0,
+ };
+
+ enum CurrentLimitEnum
+ {
+ // These are not yet defined.
+ CurrentLimit_Default = 0,
+ };
+
+ DisplayReport()
+ : CommandId(0), Brightness(0),
+ ShutterType(ShutterType_Default), CurrentLimit(CurrentLimit_Default), UseRolling(0),
+ ReverseRolling(0), HighBrightness(0), SelfRefresh(0),
+ ReadPixel(0), DirectPentile(0),
+ Persistence(0), LightingOffset(0),
+ PixelSettle(0), TotalRows(0)
+ {}
+
+ DisplayReport( UInt16 commandId,
+ UByte brightness,
+ ShutterTypeEnum shutterType,
+ CurrentLimitEnum currentLimit,
+ bool useRolling,
+ bool reverseRolling,
+ bool highBrightness,
+ bool selfRefresh,
+ bool readPixel,
+ bool directPentile,
+ UInt16 persistence,
+ UInt16 lightingOffset,
+ UInt16 pixelSettle,
+ UInt16 totalRows)
+ : CommandId(commandId), Brightness(brightness),
+ ShutterType(shutterType), CurrentLimit(currentLimit), UseRolling(useRolling),
+ ReverseRolling(reverseRolling), HighBrightness(highBrightness), SelfRefresh(selfRefresh),
+ ReadPixel(readPixel), DirectPentile(directPentile),
+ Persistence(persistence), LightingOffset(lightingOffset),
+ PixelSettle(pixelSettle), TotalRows(totalRows)
+ { }
+
+ UInt16 CommandId;
+ UByte Brightness; // See 'DK2 Firmware Specification' document for a description of
+ ShutterTypeEnum ShutterType; // display settings.
+ CurrentLimitEnum CurrentLimit;
+ bool UseRolling;
+ bool ReverseRolling;
+ bool HighBrightness;
+ bool SelfRefresh;
+ bool ReadPixel;
+ bool DirectPentile;
+ UInt16 Persistence;
+ UInt16 LightingOffset;
+ UInt16 PixelSettle;
+ UInt16 TotalRows;
+};
+
+// MagCalibration matrix (DK2).
+struct MagCalibrationReport
+{
+ MagCalibrationReport()
+ : CommandId(0), Version(0), Calibration()
+ {}
+
+ MagCalibrationReport( UInt16 commandId,
+ UByte version,
+ const Matrix4f& calibration)
+ : CommandId(commandId), Version(version), Calibration(calibration)
+ { }
+
+ UInt16 CommandId;
+ UByte Version; // Version of the calibration procedure used to generate the calibration matrix.
+ Matrix4f Calibration; // Calibration matrix. Note only the first three rows are used by the feature report.
+};
+
+// PositionCalibration values (DK2).
+// - Sensor interface versions before 5 do not support Normal and Rotation.
+struct PositionCalibrationReport
+{
+ enum PositionTypeEnum
+ {
+ PositionType_LED = 0,
+ PositionType_IMU = 1
+ };
+
+ PositionCalibrationReport()
+ : CommandId(0), Version(0),
+ Position(0), Normal(0), Angle(0),
+ PositionIndex(0), NumPositions(0), PositionType(PositionType_LED)
+ {}
+
+ PositionCalibrationReport(UInt16 commandId,
+ UByte version,
+ const Vector3d& position,
+ const Vector3d& normal,
+ double rotation,
+ UInt16 positionIndex,
+ UInt16 numPositions,
+ PositionTypeEnum positionType)
+ : CommandId(commandId), Version(version),
+ Position(position), Normal(normal), Angle(rotation),
+ PositionIndex(positionIndex), NumPositions(numPositions), PositionType(positionType)
+ {
+ }
+
+ UInt16 CommandId;
+ UByte Version; // The version of the calibration procedure used to generate the stored positions.
+ Vector3d Position; // Position of the LED or inertial tracker in meters. This is relative to the
+ // center of the emitter plane of the display at nominal focus.
+ Vector3d Normal; // Normal of the LED or inertial tracker. This is a signed integer in
+ // meters. The normal is relative to the position.
+ double Angle; // The rotation about the normal. This is in radians.
+ UInt16 PositionIndex; // The current position being read or written to. Autoincrements on reads, gets set
+ // to the written value on writes.
+ UInt16 NumPositions; // The read-only number of items with positions stored. The last position is that of
+ // the inertial tracker, all others are LED positions.
+ PositionTypeEnum PositionType; // The type of the item which has its position reported in the current report
+};
+
+// CustomPattern values (DK2).
+struct CustomPatternReport
+{
+ CustomPatternReport()
+ : CommandId(0), SequenceLength(0), Sequence(0),
+ LEDIndex(0), NumLEDs(0)
+ {}
+
+ CustomPatternReport(UInt16 commandId,
+ UByte sequenceLength,
+ UInt32 sequence,
+ UInt16 ledIndex,
+ UInt16 numLEDs)
+ : CommandId(commandId), SequenceLength(sequenceLength), Sequence(sequence),
+ LEDIndex(ledIndex), NumLEDs(numLEDs)
+ { }
+
+ UInt16 CommandId;
+ UByte SequenceLength; // See 'DK2 Firmware Specification' document for a description of
+ UInt32 Sequence; // LED custom patterns.
+ UInt16 LEDIndex;
+ UInt16 NumLEDs;
+};
+
+// KeepAliveMux settings (DK2).
+struct KeepAliveMuxReport
+{
+ KeepAliveMuxReport()
+ : CommandId(0), INReport(0), Interval(0)
+ {}
+
+ KeepAliveMuxReport( UInt16 commandId,
+ UByte inReport,
+ UInt16 interval)
+ : CommandId(commandId), INReport(inReport), Interval(interval)
+ { }
+
+ UInt16 CommandId;
+ UByte INReport; // Requested IN report type (1 = DK1, 11 = DK2).
+ UInt16 Interval; // Keep alive period in milliseconds.
+};
+
+// Manufacturing test result (DK2).
+struct ManufacturingReport
+{
+ ManufacturingReport()
+ : CommandId(0), NumStages(0), Stage(0),
+ StageVersion(0), StageLocation(0), StageTime(0), Result(0)
+ {}
+
+ ManufacturingReport( UInt16 commandId,
+ UByte numStages,
+ UByte stage,
+ UByte version,
+ UInt16 stageLocation,
+ UInt32 stageTime,
+ UInt32 result)
+ : CommandId(commandId), NumStages(numStages), Stage(stage),
+ StageVersion(version), StageLocation(stageLocation), StageTime(stageTime), Result(result)
+ { }
+
+ UInt16 CommandId;
+ UByte NumStages; // See 'DK2 Firmware Specification' document for a description of
+ UByte Stage; // manufacturing test results.
+ UByte StageVersion;
+ UInt16 StageLocation;
+ UInt32 StageTime;
+ UInt32 Result;
+};
+
+// UUID (DK2).
+struct UUIDReport
+{
+ static const int UUID_SIZE = 20;
+
+ UUIDReport()
+ : CommandId(0)
+ {
+ memset(UUIDValue, 0, sizeof(UUIDValue));
+ }
+
+ UUIDReport( UInt16 commandId,
+ UByte uuid[UUID_SIZE])
+ : CommandId(commandId)
+ {
+ for (int i=0; i<UUID_SIZE; i++)
+ {
+ UUIDValue[i] = uuid[i];
+ }
+ }
+
+ UInt16 CommandId;
+ UByte UUIDValue[UUID_SIZE]; // See 'DK2 Firmware Specification' document for
+ // a description of UUID.
+};
+
+// Lens Distortion (DK2).
+struct LensDistortionReport
+{
+ LensDistortionReport()
+ : CommandId(0),
+ NumDistortions(0),
+ DistortionIndex(0),
+ Bitmask(0),
+ LensType(0),
+ Version(0),
+ EyeRelief(0),
+ MaxR(0),
+ MetersPerTanAngleAtCenter(0)
+ {}
+
+ LensDistortionReport( UInt16 commandId,
+ UByte numDistortions,
+ UByte distortionIndex,
+ UByte bitmask,
+ UInt16 lensType,
+ UInt16 version,
+ UInt16 eyeRelief,
+ UInt16 kCoefficients[11],
+ UInt16 maxR,
+ UInt16 metersPerTanAngleAtCenter,
+ UInt16 chromaticAberration[4])
+ : CommandId(commandId),
+ NumDistortions(numDistortions),
+ DistortionIndex(distortionIndex),
+ Bitmask(bitmask),
+ LensType(lensType),
+ Version(version),
+ EyeRelief(eyeRelief),
+ MaxR(maxR),
+ MetersPerTanAngleAtCenter(metersPerTanAngleAtCenter)
+ {
+ memcpy(KCoefficients, kCoefficients, sizeof(KCoefficients));
+ memcpy(ChromaticAberration, chromaticAberration, sizeof(ChromaticAberration));
+ }
+
+ UInt16 CommandId;
+ UByte NumDistortions;
+ UByte DistortionIndex;
+ UByte Bitmask;
+ UInt16 LensType;
+ UInt16 Version;
+ UInt16 EyeRelief;
+ UInt16 KCoefficients[11];
+ UInt16 MaxR;
+ UInt16 MetersPerTanAngleAtCenter;
+ UInt16 ChromaticAberration[4];
+};
+
+// Temperature calibration result (DK2).
+struct TemperatureReport
+{
+ TemperatureReport()
+ : CommandId(0), Version(0),
+ NumBins(0), Bin(0), NumSamples(0), Sample(0),
+ TargetTemperature(0), ActualTemperature(0),
+ Time(0), Offset(0)
+ {}
+
+ TemperatureReport( UInt16 commandId,
+ UByte version,
+ UByte numBins,
+ UByte bin,
+ UByte numSamples,
+ UByte sample,
+ double targetTemperature,
+ double actualTemperature,
+ UInt32 time,
+ Vector3d offset)
+ : CommandId(commandId), Version(version),
+ NumBins(numBins), Bin(bin), NumSamples(numSamples), Sample(sample),
+ TargetTemperature(targetTemperature), ActualTemperature(actualTemperature),
+ Time(time), Offset(offset)
+ { }
+
+ UInt16 CommandId;
+ UByte Version; // See 'DK2 Firmware Specification' document for a description of
+ UByte NumBins; // temperature calibration data.
+ UByte Bin;
+ UByte NumSamples;
+ UByte Sample;
+ double TargetTemperature;
+ double ActualTemperature;
+ UInt32 Time; // Better hope nobody tries to use this in 2038
+ Vector3d Offset;
+};
+
+// Gyro autocalibration result (DK2).
+struct GyroOffsetReport
+{
+ enum VersionEnum
+ {
+ // These are not yet defined.
+ Version_NoOffset = 0,
+ Version_ShortAvg = 1,
+ Version_LongAvg = 2
+ };
+
+ GyroOffsetReport()
+ : CommandId(0), Version(Version_NoOffset),
+ Offset(0), Temperature(0)
+ {}
+
+ GyroOffsetReport( UInt16 commandId,
+ VersionEnum version,
+ Vector3d offset,
+ double temperature)
+ : CommandId(commandId), Version(version),
+ Offset(offset), Temperature(temperature)
+ {}
+
+ UInt16 CommandId;
+ VersionEnum Version;
+ Vector3d Offset;
+ double Temperature;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+// SensorDevice is an interface to sensor data.
+// Install a MessageHandler of SensorDevice instance to receive MessageBodyFrame
+// notifications.
+//
+// TBD: Add Polling API? More HID interfaces?
+
+class SensorDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ SensorDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_Sensor };
+
+ virtual DeviceType GetType() const { return Device_Sensor; }
+
+ virtual UByte GetDeviceInterfaceVersion() = 0;
+
+
+ // CoordinateFrame defines whether messages come in the coordinate frame
+ // of the sensor device or HMD, which has a different internal sensor.
+ // Sensors obtained form the HMD will automatically use HMD coordinates.
+ enum CoordinateFrame
+ {
+ Coord_Sensor = 0,
+ Coord_HMD = 1
+ };
+
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0;
+ virtual CoordinateFrame GetCoordinateFrame() const = 0;
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz) = 0;
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const = 0;
+
+ // Sets maximum range settings for the sensor described by SensorRange.
+ // The function will fail if you try to pass values outside Maximum supported
+ // by the HW, as described by SensorInfo.
+ // Pass waitFlag == true to wait for command completion. For waitFlag == true,
+ // returns true if the range was applied successfully (no HW error).
+ // For waitFlag = false, return 'true' means that command was enqueued successfully.
+ virtual bool SetRange(const SensorRange& range, bool waitFlag = false) = 0;
+
+ // Return the current sensor range settings for the device. These may not exactly
+ // match the values applied through SetRange.
+ virtual void GetRange(SensorRange* range) const = 0;
+
+ // Return the factory calibration parameters for the IMU
+ virtual void GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset,
+ Matrix4f* AccelMatrix, Matrix4f* GyroMatrix,
+ float* Temperature) = 0;
+ // Enable/disable onboard IMU calibration
+ // If set to false, the device will return raw values
+ virtual void SetOnboardCalibrationEnabled(bool enabled) = 0;
+ // Return true if the mag is calibrated
+ virtual bool IsMagCalibrated() { return false; }
+
+ // Get/set feature reports from DK1 added to DK2. See 'Tracker Firmware Specification' document for details.
+ virtual bool SetSerialReport(const SerialReport&) { return false; }
+ virtual bool GetSerialReport(SerialReport*) { return false; }
+
+ // Get/set feature reports added to DK2. See 'DK2 Firmware Specification' document for details.
+ virtual bool SetTrackingReport(const TrackingReport&) { return false; }
+ virtual bool GetTrackingReport(TrackingReport*) { return false; }
+
+ virtual bool SetDisplayReport(const DisplayReport&) { return false; }
+ virtual bool GetDisplayReport(DisplayReport*) { return false; }
+
+ virtual bool SetMagCalibrationReport(const MagCalibrationReport&) { return false; }
+ virtual bool GetMagCalibrationReport(MagCalibrationReport*) { return false; }
+
+ virtual bool SetPositionCalibrationReport(const PositionCalibrationReport&) { return false; }
+ virtual bool GetAllPositionCalibrationReports(Array<PositionCalibrationReport>*) { return false; }
+
+ virtual bool SetCustomPatternReport(const CustomPatternReport&) { return false; }
+ virtual bool GetCustomPatternReport(CustomPatternReport*) { return false; }
+
+ virtual bool SetKeepAliveMuxReport(const KeepAliveMuxReport&) { return false; }
+ virtual bool GetKeepAliveMuxReport(KeepAliveMuxReport*) { return false; }
+
+ virtual bool SetManufacturingReport(const ManufacturingReport&) { return false; }
+ virtual bool GetManufacturingReport(ManufacturingReport*) { return false; }
+
+ virtual bool SetUUIDReport(const UUIDReport&) { return false; }
+ virtual bool GetUUIDReport(UUIDReport*) { return false; }
+
+ virtual bool SetTemperatureReport(const TemperatureReport&) { return false; }
+ virtual bool GetAllTemperatureReports(Array<Array<TemperatureReport> >*) { return false; }
+
+ virtual bool GetGyroOffsetReport(GyroOffsetReport*) { return false; }
+
+ virtual bool SetLensDistortionReport(const LensDistortionReport&) { return false; }
+ virtual bool GetLensDistortionReport(LensDistortionReport*) { return false; }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestConfiguration
+// LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device.
+struct LatencyTestConfiguration
+{
+ LatencyTestConfiguration(const Color& threshold, bool sendSamples = false)
+ : Threshold(threshold), SendSamples(sendSamples)
+ {
+ }
+
+ // The color threshold for triggering a detected display change.
+ Color Threshold;
+ // Flag specifying whether we wish to receive a stream of color values from the sensor.
+ bool SendSamples;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDisplay
+// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display.
+// See the 'Latency Tester Specification' document for more details.
+struct LatencyTestDisplay
+{
+ LatencyTestDisplay(UByte mode, UInt32 value)
+ : Mode(mode), Value(value)
+ {
+ }
+
+ UByte Mode; // The display mode that we wish to select.
+ UInt32 Value; // The value to display.
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+// LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency.
+class LatencyTestDevice : public HIDDeviceBase, public DeviceBase
+{
+public:
+ LatencyTestDevice()
+ { }
+
+ // Static constant for this device type, used in template cast type checks.
+ enum { EnumDeviceType = Device_LatencyTester };
+
+ virtual DeviceType GetType() const { return Device_LatencyTester; }
+
+ // Specifies configuration information including the threshold for triggering a detected color change,
+ // and a flag to enable a stream of sensor values (typically used for debugging).
+ virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0;
+
+ // Get configuration information from device.
+ virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0;
+
+ // Used to calibrate the latency tester at the start of a test. Display the specified color on the screen
+ // beneath the latency tester and then call this method. Calibration information is lost
+ // when power is removed from the device.
+ virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false) = 0;
+
+ // Triggers the start of a measurement. This starts the millisecond timer on the device and
+ // causes it to respond with the 'MessageLatencyTestStarted' message.
+ virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false) = 0;
+
+ // Used to set the value displayed on the LED display panel.
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0;
+
+ virtual DeviceBase* GetDevice() { return this; }
+};
+
+} // namespace OVR
+
+
+
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceConstants.h b/LibOVR/Src/OVR_DeviceConstants.h
new file mode 100644
index 0000000..6b40b7d
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceConstants.h
@@ -0,0 +1,142 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceConstants.h
+Content : Device constants
+Created : February 5, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceConstants_h
+#define OVR_DeviceConstants_h
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// Different device types supported by OVR; this type is reported by DeviceBase::GetType.
+//
+enum DeviceType
+{
+ Device_None = 0,
+ Device_Manager = 1,
+ Device_HMD = 2,
+ Device_Sensor = 3,
+ Device_LatencyTester = 4,
+ Device_BootLoader = 5,
+ Device_Camera = 6,
+ Device_Display = 7,
+ Device_All = 0xFF // Set for enumeration only, to enumerate all device types.
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// Different lens distortion types supported by devices.
+//
+enum DistortionEqnType
+{
+ Distortion_No_Override = -1,
+ // These two are leagcy and deprecated.
+ Distortion_Poly4 = 0, // scale = (K0 + K1*r^2 + K2*r^4 + K3*r^6)
+ Distortion_RecipPoly4 = 1, // scale = 1/(K0 + K1*r^2 + K2*r^4 + K3*r^6)
+
+ // CatmullRom10 is the preferred distortion format.
+ Distortion_CatmullRom10 = 2, // scale = Catmull-Rom spline through points (1.0, K[1]...K[9])
+
+ Distortion_LAST // For ease of enumeration.
+};
+
+
+//-------------------------------------------------------------------------------------
+// HMD types.
+//
+enum HmdTypeEnum
+{
+ HmdType_None,
+
+ HmdType_DKProto, // First duct-tape model, never sold.
+ HmdType_DK1, // DevKit1 - on sale to developers.
+ HmdType_DKHDProto, // DKHD - shown at various shows, never sold.
+ HmdType_DKHD2Proto, // DKHD2, 5.85-inch panel, never sold.
+ HmdType_DKHDProto566Mi, // DKHD, 5.66-inch panel, never sold.
+ HmdType_CrystalCoveProto, // Crystal Cove, 5.66-inch panel, shown at shows but never sold.
+ HmdType_DK2,
+
+ // Reminder - this header file is public - codenames only!
+
+ HmdType_Unknown, // Used for unnamed HW lab experiments.
+
+ HmdType_LAST
+};
+
+
+//-------------------------------------------------------------------------------------
+// HMD shutter types.
+//
+enum HmdShutterTypeEnum
+{
+ HmdShutter_Global,
+ HmdShutter_RollingTopToBottom,
+ HmdShutter_RollingLeftToRight,
+ HmdShutter_RollingRightToLeft,
+ // TODO:
+ // color-sequential e.g. LCOS?
+ // alternate eyes?
+ // alternate columns?
+ // outside-in?
+
+ HmdShutter_LAST
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// For headsets that use eye cups
+//
+enum EyeCupType
+{
+ // Public lenses
+ EyeCup_DK1A = 0,
+ EyeCup_DK1B = 1,
+ EyeCup_DK1C = 2,
+
+ EyeCup_DK2A = 3,
+
+ // Internal R&D codenames.
+ // Reminder - this header file is public - codenames only!
+ EyeCup_DKHD2A,
+ EyeCup_OrangeA,
+ EyeCup_RedA,
+ EyeCup_PinkA,
+ EyeCup_BlueA,
+ EyeCup_Delilah1A,
+ EyeCup_Delilah2A,
+ EyeCup_JamesA,
+ EyeCup_SunMandalaA,
+
+ EyeCup_LAST
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceHandle.cpp b/LibOVR/Src/OVR_DeviceHandle.cpp
new file mode 100644
index 0000000..cf6f05f
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceHandle.cpp
@@ -0,0 +1,185 @@
+/************************************************************************************
+
+Filename : OVR_DeviceHandle.cpp
+Content : Implementation of device handle class
+Created : February 5, 2013
+Authors : Lee Cooper
+
+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_DeviceHandle.h"
+
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl)
+{
+ if (pImpl)
+ pImpl->AddRef();
+}
+
+DeviceHandle::~DeviceHandle()
+{
+ if (pImpl)
+ pImpl->Release();
+}
+
+void DeviceHandle::operator = (const DeviceHandle& src)
+{
+ if (src.pImpl)
+ src.pImpl->AddRef();
+ if (pImpl)
+ pImpl->Release();
+ pImpl = src.pImpl;
+}
+
+DeviceBase* DeviceHandle::GetDevice_AddRef() const
+{
+ if (pImpl && pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ return NULL;
+}
+
+// Returns true, if the handle contains the same device ptr
+// as specified in the parameter.
+bool DeviceHandle::IsDevice(DeviceBase* pdev) const
+{
+ return (pdev && pImpl && pImpl->pDevice) ?
+ pImpl->pDevice == pdev : false;
+}
+
+DeviceType DeviceHandle::GetType() const
+{
+ return pImpl ? pImpl->Type : Device_None;
+}
+
+bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const
+{
+ return pImpl ? pImpl->GetDeviceInfo(info) : false;
+}
+bool DeviceHandle::IsAvailable() const
+{
+ // This isn't "atomically safe", but the function only returns the
+ // recent state that may change.
+ return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false;
+}
+
+bool DeviceHandle::IsCreated() const
+{
+ return pImpl ? (pImpl->pDevice != 0) : false;
+}
+
+DeviceBase* DeviceHandle::CreateDevice()
+{
+ if (!pImpl)
+ return 0;
+
+ DeviceBase* device = 0;
+ Ptr<DeviceManagerImpl> manager= 0;
+
+ // Since both manager and device pointers can only be destroyed during a lock,
+ // hold it while checking for availability.
+ // AddRef to manager so that it doesn't get released on us.
+ {
+ Lock::Locker deviceLockScope(pImpl->GetLock());
+
+ if (pImpl->pDevice)
+ {
+ pImpl->pDevice->AddRef();
+ return pImpl->pDevice;
+ }
+ manager = pImpl->GetManagerImpl();
+ }
+
+ if (manager)
+ {
+ if (manager->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ // Queue up a CreateDevice request. This fills in '&device' with AddRefed value,
+ // or keep it at null.
+ manager->GetThreadQueue()->PushCallAndWaitResult(
+ manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread,
+ &device, pImpl, (DeviceBase*)0);
+ }
+ else
+ device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0);
+ }
+ return device;
+}
+
+void DeviceHandle::Clear()
+{
+ if (pImpl)
+ {
+ pImpl->Release();
+ pImpl = 0;
+ }
+}
+
+bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args)
+{
+ if (GetType() == Device_None)
+ return false;
+
+ Ptr<DeviceManagerImpl> managerKeepAlive;
+ Lock::Locker lockScope(pImpl->GetLock());
+
+ DeviceCreateDesc* next = pImpl;
+ // If manager was destroyed, we get removed from the list.
+ if (!pImpl->pNext)
+ return false;
+
+ managerKeepAlive = next->GetManagerImpl();
+ OVR_ASSERT(managerKeepAlive);
+
+ do {
+ next = next->pNext;
+
+ if (managerKeepAlive->Devices.IsNull(next))
+ {
+ pImpl->Release();
+ pImpl = 0;
+ return false;
+ }
+
+ } while(!args.MatchRule(next->Type, next->Enumerated));
+
+ next->AddRef();
+ pImpl->Release();
+ pImpl = next;
+
+ return true;
+}
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceHandle.h b/LibOVR/Src/OVR_DeviceHandle.h
new file mode 100644
index 0000000..dd3e92b
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceHandle.h
@@ -0,0 +1,108 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceHandle.h
+Content : Handle to a device that was enumerated
+Created : February 5, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceHandle_h
+#define OVR_DeviceHandle_h
+
+#include "OVR_DeviceConstants.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceInfo;
+
+// Internal
+class DeviceCreateDesc;
+class DeviceEnumerationArgs;
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceHandle
+
+// DeviceHandle references a specific device that was enumerated; it can be assigned
+// directly from DeviceEnumerator.
+//
+// Devices represented by DeviceHandle are not necessarily created or available.
+// A device may become unavailable if, for example, it its unplugged. If the device
+// is available, it can be created by calling CreateDevice.
+//
+
+class DeviceHandle
+{
+ friend class DeviceManager;
+ friend class DeviceManagerImpl;
+ template<class B> friend class HIDDeviceImpl;
+
+public:
+ DeviceHandle() : pImpl(0) { }
+ DeviceHandle(const DeviceHandle& src);
+ ~DeviceHandle();
+
+ void operator = (const DeviceHandle& src);
+
+ bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; }
+ bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; }
+
+ // operator bool() returns true if Handle/Enumerator points to a valid device.
+ operator bool () const { return GetType() != Device_None; }
+
+ // Returns existing device, or NULL if !IsCreated. The returned ptr is
+ // addref-ed.
+ DeviceBase* GetDevice_AddRef() const;
+ DeviceType GetType() const;
+ bool GetDeviceInfo(DeviceInfo* info) const;
+ bool IsAvailable() const;
+ bool IsCreated() const;
+ // Returns true, if the handle contains the same device ptr
+ // as specified in the parameter.
+ bool IsDevice(DeviceBase*) const;
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1.
+ DeviceBase* CreateDevice();
+
+ // Creates a device, or returns AddRefed pointer if one is already created.
+ // New devices start out with RefCount of 1. DeviceT is used to cast the
+ // DeviceBase* to a concreete type.
+ template <class DeviceT>
+ DeviceT* CreateDeviceTyped() const
+ {
+ return static_cast<DeviceT*>(DeviceHandle(*this).CreateDevice());
+ }
+
+ // Resets the device handle to uninitialized state.
+ void Clear();
+
+protected:
+ explicit DeviceHandle(DeviceCreateDesc* impl);
+ bool enumerateNext(const DeviceEnumerationArgs& args);
+ DeviceCreateDesc* pImpl;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceImpl.cpp b/LibOVR/Src/OVR_DeviceImpl.cpp
new file mode 100644
index 0000000..5b77708
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceImpl.cpp
@@ -0,0 +1,794 @@
+/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+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_DeviceImpl.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "OVR_DeviceImpl.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_Profile.h"
+
+namespace OVR {
+
+
+//-------------------------------------------------------------------------------------
+// ***** MessageHandler
+
+// Threading notes:
+// The OnMessage() handler and SetMessageHandler are currently synchronized
+// through a separately stored shared Lock object to avoid calling the handler
+// from background thread while it's being removed.
+
+static SharedLock MessageHandlerSharedLock;
+
+
+class MessageHandlerImpl
+{
+public:
+ enum
+ {
+ MaxHandlerRefsCount = 4
+ };
+
+ MessageHandlerImpl()
+ : pLock(MessageHandlerSharedLock.GetLockAddRef()), HandlerRefsCount(0)
+ {
+ }
+ ~MessageHandlerImpl()
+ {
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+ }
+
+ static MessageHandlerImpl* FromHandler(MessageHandler* handler)
+ { return (MessageHandlerImpl*)&handler->Internal; }
+ static const MessageHandlerImpl* FromHandler(const MessageHandler* handler)
+ { return (const MessageHandlerImpl*)&handler->Internal; }
+
+ // This lock is held while calling a handler and when we are applied/
+ // removed from a device.
+ Lock* pLock;
+ // List of devices we are applied to.
+ int HandlerRefsCount;
+ MessageHandlerRef* pHandlerRefs[MaxHandlerRefsCount];
+};
+
+
+MessageHandlerRef::MessageHandlerRef(DeviceBase* device)
+ : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), HandlersCount(0)
+{
+}
+
+MessageHandlerRef::~MessageHandlerRef()
+{
+ {
+ Lock::Locker lockScope(pLock);
+
+ while (HandlersCount > 0)
+ removeHandler(0);
+ }
+ MessageHandlerSharedLock.ReleaseLock(pLock);
+ pLock = 0;
+}
+
+void MessageHandlerRef::Call(const Message& msg)
+{
+ Lock::Locker lockScope(pLock);
+
+ for (int i = 0; i < HandlersCount; i++)
+ pHandlers[i]->OnMessage(msg);
+}
+
+void MessageHandlerRef::AddHandler(MessageHandler* handler)
+{
+ OVR_ASSERT(!handler ||
+ MessageHandlerImpl::FromHandler(handler)->pLock == pLock);
+ Lock::Locker lockScope(pLock);
+ AddHandler_NTS(handler);
+}
+
+void MessageHandlerRef::AddHandler_NTS(MessageHandler* handler)
+{
+ OVR_ASSERT(handler != NULL);
+
+ OVR_ASSERT(HandlersCount < MaxHandlersCount);
+ for (int i = 0; i < HandlersCount; i++)
+ if (pHandlers[i] == handler)
+ // handler already installed - do nothing
+ return;
+ pHandlers[HandlersCount] = handler;
+ HandlersCount++;
+
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler);
+ OVR_ASSERT(handlerImpl->HandlerRefsCount < MessageHandlerImpl::MaxHandlerRefsCount);
+ handlerImpl->pHandlerRefs[handlerImpl->HandlerRefsCount] = this;
+ handlerImpl->HandlerRefsCount++;
+
+ // TBD: Call notifier on device?
+}
+
+bool MessageHandlerRef::RemoveHandler(MessageHandler* handler)
+{
+ Lock::Locker lockScope(pLock);
+
+ for (int i = 0; i < HandlersCount; i++)
+ {
+ if (pHandlers[i] == handler)
+ return removeHandler(i);
+ }
+ return false;
+}
+
+bool MessageHandlerRef::removeHandler(int idx)
+{
+ OVR_ASSERT(idx < HandlersCount);
+
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(pHandlers[idx]);
+ for (int i = 0; i < handlerImpl->HandlerRefsCount; i++)
+ if (handlerImpl->pHandlerRefs[i] == this)
+ {
+ handlerImpl->pHandlerRefs[i] = handlerImpl->pHandlerRefs[handlerImpl->HandlerRefsCount - 1];
+ handlerImpl->HandlerRefsCount--;
+
+ pHandlers[idx] = pHandlers[HandlersCount - 1];
+ HandlersCount--;
+
+ return true;
+ }
+
+ // couldn't find a link in the opposite direction, assert in Debug
+ OVR_ASSERT(0);
+
+ pHandlers[idx] = pHandlers[HandlersCount - 1];
+ HandlersCount--;
+
+ return true;
+}
+
+MessageHandler::MessageHandler()
+{
+ OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl));
+ Construct<MessageHandlerImpl>(Internal);
+}
+
+MessageHandler::~MessageHandler()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ {
+ Lock::Locker lockedScope(handlerImpl->pLock);
+ OVR_ASSERT_LOG(handlerImpl->HandlerRefsCount == 0,
+ ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this));
+ }
+
+ Destruct<MessageHandlerImpl>(handlerImpl);
+}
+
+bool MessageHandler::IsHandlerInstalled() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+
+ return handlerImpl->HandlerRefsCount > 0;
+}
+
+void MessageHandler::RemoveHandlerFromDevices()
+{
+ MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ Lock::Locker lockedScope(handlerImpl->pLock);
+
+ while (handlerImpl->HandlerRefsCount > 0)
+ {
+ MessageHandlerRef* use = handlerImpl->pHandlerRefs[0];
+ use->RemoveHandler(this);
+ }
+}
+
+Lock* MessageHandler::GetHandlerLock() const
+{
+ const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this);
+ return handlerImpl->pLock;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceBase
+
+
+// Delegate relevant implementation to DeviceRectord to avoid re-implementation in
+// every derived Device.
+void DeviceBase::AddRef()
+{
+ getDeviceCommon()->DeviceAddRef();
+}
+void DeviceBase::Release()
+{
+ getDeviceCommon()->DeviceRelease();
+}
+DeviceBase* DeviceBase::GetParent() const
+{
+ return getDeviceCommon()->pParent.GetPtr();
+}
+DeviceManager* DeviceBase::GetManager() const
+{
+ return getDeviceCommon()->pCreateDesc->GetManagerImpl();
+}
+
+void DeviceBase::AddMessageHandler(MessageHandler* handler)
+{
+ getDeviceCommon()->HandlerRef.AddHandler(handler);
+}
+
+DeviceType DeviceBase::GetType() const
+{
+ return getDeviceCommon()->pCreateDesc->Type;
+}
+
+bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const
+{
+ return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info);
+ //info->Name[0] = 0;
+ //return false;
+}
+
+// Returns true if device is connected and usable
+bool DeviceBase::IsConnected()
+{
+ return getDeviceCommon()->ConnectedFlag;
+}
+
+// returns the MessageHandler's lock
+Lock* DeviceBase::GetHandlerLock() const
+{
+ return getDeviceCommon()->HandlerRef.GetLock();
+}
+
+// Derive DeviceManagerCreateDesc to provide abstract function implementation.
+class DeviceManagerCreateDesc : public DeviceCreateDesc
+{
+public:
+ DeviceManagerCreateDesc(DeviceFactory* factory)
+ : DeviceCreateDesc(factory, Device_Manager) { }
+
+ // We don't need there on Manager since it isn't assigned to DeviceHandle.
+ virtual DeviceCreateDesc* Clone() const { return 0; }
+ virtual MatchResult MatchDevice(const DeviceCreateDesc&,
+ DeviceCreateDesc**) const { return Match_None; }
+ virtual DeviceBase* NewDeviceInstance() { return 0; }
+ virtual bool GetDeviceInfo(DeviceInfo*) const { return false; }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+DeviceManagerImpl::DeviceManagerImpl()
+ : DeviceImpl<OVR::DeviceManager>(CreateManagerDesc(), 0)
+ //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0)
+{
+ if (pCreateDesc)
+ {
+ pCreateDesc->pLock->pManager = this;
+ }
+}
+
+DeviceManagerImpl::~DeviceManagerImpl()
+{
+ // Shutdown must've been called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+
+ // Remove all factories
+ while(!Factories.IsEmpty())
+ {
+ DeviceFactory* factory = Factories.GetFirst();
+ factory->RemovedFromManager();
+ factory->RemoveNode();
+ }
+}
+
+DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc()
+{
+ DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0);
+ if (managerDesc)
+ {
+ managerDesc->pLock = *new DeviceManagerLock;
+ }
+ return managerDesc;
+}
+
+bool DeviceManagerImpl::Initialize(DeviceBase* parent)
+{
+ OVR_UNUSED(parent);
+ if (!pCreateDesc || !pCreateDesc->pLock)
+ return false;
+
+ pProfileManager = *ProfileManager::Create();
+
+ return true;
+}
+
+void DeviceManagerImpl::Shutdown()
+{
+ // Remove all device descriptors from list while the lock is held.
+ // Some descriptors may survive longer due to handles.
+ while(!Devices.IsEmpty())
+ {
+ DeviceCreateDesc* devDesc = Devices.GetFirst();
+ OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists.
+ devDesc->Enumerated = false;
+ devDesc->RemoveNode();
+ devDesc->pNext = devDesc->pPrev = 0;
+
+ if (devDesc->HandleCount == 0)
+ {
+ delete devDesc;
+ }
+ }
+ Devices.Clear();
+
+ // These must've been cleared by caller.
+ OVR_ASSERT(pCreateDesc->pDevice == 0);
+ OVR_ASSERT(pCreateDesc->pLock->pManager == 0);
+
+ pProfileManager.Clear();
+}
+
+
+// Callbacks for DeviceCreation/Release
+DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent)
+{
+ // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager,
+ // so 'this' must remain valid.
+ OVR_ASSERT(createDesc->pLock->pManager);
+
+ Lock::Locker devicesLock(GetLock());
+
+ // If device already exists, just AddRef to it.
+ if (createDesc->pDevice)
+ {
+ createDesc->pDevice->AddRef();
+ return createDesc->pDevice;
+ }
+
+ if (!parent)
+ parent = this;
+
+ DeviceBase* device = createDesc->NewDeviceInstance();
+
+ if (device)
+ {
+ if (device->getDeviceCommon()->Initialize(parent))
+ {
+ createDesc->pDevice = device;
+ }
+ else
+ {
+ // Don't go through Release() to avoid PushCall behaviour,
+ // as it is not needed here.
+ delete device;
+ device = 0;
+ }
+ }
+
+ return device;
+}
+
+Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device)
+{
+ // descKeepAlive will keep ManagerLock object alive as well,
+ // allowing us to exit gracefully.
+ Ptr<DeviceCreateDesc> descKeepAlive;
+ Lock::Locker devicesLock(GetLock());
+ DeviceCommon* devCommon = device->getDeviceCommon();
+
+ while(1)
+ {
+ UInt32 refCount = devCommon->RefCount;
+
+ if (refCount > 1)
+ {
+ if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ // We decreented from initial count higher then 1;
+ // nothing else to do.
+ return 0;
+ }
+ }
+ else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0))
+ {
+ // { 1 -> 0 } decrement succeded. Destroy this device.
+ break;
+ }
+ }
+
+ // At this point, may be releasing the device manager itself.
+ // This does not matter, however, since shutdown logic is the same
+ // in both cases. DeviceManager::Shutdown with begin shutdown process for
+ // the internal manager thread, which will eventually destroy itself.
+ // TBD: Clean thread shutdown.
+ descKeepAlive = devCommon->pCreateDesc;
+ descKeepAlive->pDevice = 0;
+ devCommon->Shutdown();
+ delete device;
+ return 0;
+}
+
+
+
+Void DeviceManagerImpl::EnumerateAllFactoryDevices()
+{
+ // 1. Mark matching devices as NOT enumerated.
+ // 2. Call factory to enumerate all HW devices, adding any device that
+ // was not matched.
+ // 3. Remove non-matching devices.
+
+ Lock::Locker deviceLock(GetLock());
+
+ DeviceCreateDesc* devDesc, *nextdevDesc;
+
+ // 1.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ //if (devDesc->pFactory == factory)
+ devDesc->Enumerated = false;
+ }
+
+ // 2.
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ EnumerateFactoryDevices(factory);
+ factory = factory->pNext;
+ }
+
+
+ // 3.
+ for(devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = nextdevDesc)
+ {
+ // In case 'devDesc' gets removed.
+ nextdevDesc = devDesc->pNext;
+
+ // Note, device might be not enumerated since it is opened and
+ // in use! Do NOT notify 'device removed' in this case (!AB)
+ if (!devDesc->Enumerated)
+ {
+ // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle.
+ CallOnDeviceRemoved(devDesc);
+
+ /*
+ if (devDesc->HandleCount == 0)
+ {
+ // Device must be dead if it ever existed, since it AddRefs to us.
+ // ~DeviceCreateDesc removes its node from list.
+ OVR_ASSERT(!devDesc->pDevice);
+ delete devDesc;
+ }
+ */
+ }
+ }
+
+ return 0;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::AddDevice_NeedsLock(
+ const DeviceCreateDesc& createDesc)
+{
+ // If found, mark as enumerated and we are done.
+ DeviceCreateDesc* descCandidate = 0;
+
+ for(DeviceCreateDesc* devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate);
+ if (mr == DeviceCreateDesc::Match_Found)
+ {
+ devDesc->Enumerated = true;
+ if (!devDesc->pDevice)
+ CallOnDeviceAdded(devDesc);
+ return devDesc;
+ }
+ }
+
+ // Update candidate (this may involve writing fields to HMDDevice createDesc).
+ if (descCandidate)
+ {
+ bool newDevice = false;
+ if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice))
+ {
+ descCandidate->Enumerated = true;
+ if (!descCandidate->pDevice || newDevice)
+ CallOnDeviceAdded(descCandidate);
+ return descCandidate;
+ }
+ }
+
+ // If not found, add new device.
+ // - This stores a new descriptor with
+ // {pDevice = 0, HandleCount = 1, Enumerated = true}
+ DeviceCreateDesc* desc = createDesc.Clone();
+ desc->pLock = pCreateDesc->pLock;
+ Devices.PushBack(desc);
+ desc->Enumerated = true;
+
+ CallOnDeviceAdded(desc);
+
+ return desc;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindDevice(
+ const String& path,
+ DeviceType deviceType)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if ((deviceType == Device_None || deviceType == devDesc->Type) &&
+ devDesc->MatchDevice(path))
+ return devDesc;
+ }
+ return NULL;
+}
+
+Ptr<DeviceCreateDesc> DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc, bool created)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceCreateDesc* devDesc;
+
+ for (devDesc = Devices.GetFirst();
+ !Devices.IsNull(devDesc); devDesc = devDesc->pNext)
+ {
+ if (created)
+ { // Search for matching device that is created
+ if (devDesc->MatchHIDDevice(hidDevDesc) && devDesc->pDevice)
+ return devDesc;
+ }
+ else
+ { // Search for any matching device
+ if (devDesc->MatchHIDDevice(hidDevDesc))
+ return devDesc;
+ }
+ }
+ return NULL;
+}
+
+void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc)
+{
+ Lock::Locker deviceLock(GetLock());
+ DeviceFactory* factory = Factories.GetFirst();
+ while(!Factories.IsNull(factory))
+ {
+ if (factory->DetectHIDDevice(this, hidDevDesc))
+ break;
+ factory = factory->pNext;
+ }
+
+}
+
+// Enumerates devices for a particular factory.
+Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory)
+{
+
+ class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor
+ {
+ DeviceManagerImpl* pManager;
+ DeviceFactory* pFactory;
+ public:
+ FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory)
+ : pManager(manager), pFactory(factory) { }
+
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ pManager->AddDevice_NeedsLock(createDesc);
+ }
+ };
+
+ FactoryEnumerateVisitor newDeviceVisitor(this, factory);
+ factory->EnumerateDevices(newDeviceVisitor);
+
+
+ return 0;
+}
+
+
+DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ Lock::Locker deviceLock(GetLock());
+
+ if (Devices.IsEmpty())
+ return DeviceEnumerator<>();
+
+ DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst();
+ DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args);
+
+ if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated))
+ {
+ e.Next();
+ }
+
+ return e;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCommon
+
+void DeviceCommon::DeviceAddRef()
+{
+ RefCount++;
+}
+
+void DeviceCommon::DeviceRelease()
+{
+ while(1)
+ {
+ UInt32 refCount = RefCount;
+ OVR_ASSERT(refCount > 0);
+
+ if (refCount == 1)
+ {
+ DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl();
+ ThreadCommandQueue* queue = manager->GetThreadQueue();
+
+ // Enqueue ReleaseDevice for {1 -> 0} transition with no wait.
+ // We pass our reference ownership into the queue to destroy.
+ // It's in theory possible for another thread to re-steal our device reference,
+ // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice.
+ if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread,
+ pCreateDesc->pDevice))
+ {
+ // PushCall shouldn't fail because background thread runs while manager is
+ // alive and we are holding Manager alive through pParent chain.
+ OVR_ASSERT(false);
+ }
+
+ // Warning! At his point everything, including manager, may be dead.
+ break;
+ }
+ else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1))
+ {
+ break;
+ }
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceCreateDesc
+
+
+void DeviceCreateDesc::AddRef()
+{
+ // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock,
+ // but we leave this to caller to worry about (happens during enumeration).
+ HandleCount++;
+}
+
+void DeviceCreateDesc::Release()
+{
+ while(1)
+ {
+ UInt32 handleCount = HandleCount;
+ // HandleCount must obviously be >= 1, since we are releasing it.
+ OVR_ASSERT(handleCount > 0);
+
+ // {1 -> 0} transition may cause us to be destroyed, so require a lock.
+ if (handleCount == 1)
+ {
+ Ptr<DeviceManagerLock> lockKeepAlive;
+ Lock::Locker deviceLockScope(GetLock());
+
+ if (!HandleCount.CompareAndSet_NoSync(handleCount, 0))
+ continue;
+
+ OVR_ASSERT(pDevice == 0);
+
+ // Destroy *this if the manager was destroyed already, or Enumerated
+ // is false (device no longer available).
+ if (!GetManagerImpl() || !Enumerated)
+ {
+ lockKeepAlive = pLock;
+
+ // Remove from manager list (only matters for !Enumerated).
+ if (pNext)
+ {
+ RemoveNode();
+ pNext = pPrev = 0;
+ }
+
+ delete this;
+ }
+
+ // Available DeviceCreateDesc may survive with { HandleCount == 0 },
+ // in case it might be enumerated again later.
+ break;
+ }
+ else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1))
+ {
+ break;
+ }
+ }
+}
+
+HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor)
+{
+ if (!psensor)
+ return NULL;
+
+ OVR::DeviceManager* manager = GetManager();
+ if (manager)
+ {
+ //DeviceManagerImpl* mgrImpl = static_cast<DeviceManagerImpl*>(manager);
+ Ptr<DeviceCreateDesc> desc = getDeviceCommon()->pCreateDesc;
+ if (desc)
+ {
+ class Visitor : public DeviceFactory::EnumerateVisitor
+ {
+ Ptr<DeviceCreateDesc> Desc;
+ public:
+ Visitor(DeviceCreateDesc* desc) : Desc(desc) {}
+ virtual void Visit(const DeviceCreateDesc& createDesc)
+ {
+ Lock::Locker lock(Desc->GetLock());
+ Desc->UpdateMatchedCandidate(createDesc);
+ }
+ } visitor(desc);
+ //SensorDeviceImpl* sImpl = static_cast<SensorDeviceImpl*>(psensor);
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor);
+ }
+ }
+ }
+ }
+ return this;
+}
+
+bool HMDDevice::IsDisconnected() const
+{
+ OVR::HMDInfo info;
+ GetDeviceInfo(&info);
+ // if strlen(info.DisplayDeviceName) == 0 then
+ // this HMD is 'fake' (created using sensor).
+ return (strlen(info.DisplayDeviceName) == 0);
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_DeviceImpl.h b/LibOVR/Src/OVR_DeviceImpl.h
new file mode 100644
index 0000000..8e737a5
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceImpl.h
@@ -0,0 +1,428 @@
+/************************************************************************************
+
+Filename : OVR_DeviceImpl.h
+Content : Partial back-end independent implementation of Device interfaces
+Created : October 10, 2012
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceImpl_h
+#define OVR_DeviceImpl_h
+
+#include "OVR_Device.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+
+#include "Kernel/OVR_Threads.h"
+#include "OVR_ThreadCommandQueue.h"
+#include "OVR_HIDDevice.h"
+
+namespace OVR {
+
+class DeviceManagerImpl;
+class DeviceFactory;
+
+enum
+{
+ Oculus_VendorId = 0x2833,
+ Device_Tracker_ProductId = 0x0001,
+ Device_Tracker2_ProductId = 0x0021,
+ Device_KTracker_ProductId = 0x0010,
+};
+
+
+// Wrapper for MessageHandler that includes synchronization logic.
+class MessageHandlerRef
+{
+ enum
+ {
+ MaxHandlersCount = 4
+ };
+public:
+ MessageHandlerRef(DeviceBase* device);
+ ~MessageHandlerRef();
+
+ bool HasHandlers() const { return HandlersCount > 0; };
+ void AddHandler(MessageHandler* handler);
+ // returns false if the handler is not found
+ bool RemoveHandler(MessageHandler* handler);
+ // Not-thread-safe version
+ void AddHandler_NTS(MessageHandler* handler);
+
+ void Call(const Message& msg);
+
+ Lock* GetLock() const { return pLock; }
+ DeviceBase* GetDevice() const { return pDevice; }
+
+private:
+ Lock* pLock; // Cached global handler lock.
+ DeviceBase* pDevice;
+
+ int HandlersCount;
+ MessageHandler* pHandlers[MaxHandlersCount];
+
+ bool removeHandler(int idx);
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// DeviceManagerLock is a synchronization lock used by DeviceManager for Devices
+// and is allocated separately for potentially longer lifetime.
+//
+// DeviceManagerLock is used for all of the following:
+// - Adding/removing devices
+// - Reporting manager lifetime (pManager != 0) for DeviceHandles
+// - Protecting device creation/shutdown.
+
+class DeviceManagerLock : public RefCountBase<DeviceManagerLock>
+{
+public:
+ Lock CreateLock;
+ DeviceManagerImpl* pManager;
+
+ DeviceManagerLock() : pManager(0) { }
+};
+
+
+// DeviceCreateDesc provides all of the information needed to create any device, a derived
+// instance of this class is created by DeviceFactory during enumeration.
+// - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0).
+// - Referenced and kept alive by DeviceHandle.
+
+class DeviceCreateDesc : public ListNode<DeviceCreateDesc>, public NewOverrideBase
+{
+ void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning.
+public:
+ DeviceCreateDesc(DeviceFactory* factory, DeviceType type)
+ : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true)
+ {
+ pNext = pPrev = 0;
+ }
+
+ virtual ~DeviceCreateDesc()
+ {
+ OVR_ASSERT(!pDevice);
+ if (pNext)
+ RemoveNode();
+ }
+
+ DeviceManagerImpl* GetManagerImpl() const { return pLock->pManager; }
+ Lock* GetLock() const { return &pLock->CreateLock; }
+
+ // DeviceCreateDesc reference counting is tied to Devices list management,
+ // see comments for HandleCount.
+ void AddRef();
+ void Release();
+
+
+ // *** Device creation/matching Interface
+
+
+ // Cloning copies us to an allocated object when new device is enumerated.
+ virtual DeviceCreateDesc* Clone() const = 0;
+ // Creates a new device instance without Initializing it; the
+ // later is done my Initialize()/Shutdown() methods of the device itself.
+ virtual DeviceBase* NewDeviceInstance() = 0;
+ // Override to return device-specific info.
+ virtual bool GetDeviceInfo(DeviceInfo* info) const = 0;
+
+
+ enum MatchResult
+ {
+ Match_None,
+ Match_Found,
+ Match_Candidate
+ };
+
+ // Override to return Match_Found if descriptor matches our device.
+ // Match_Candidate can be returned, with pcandicate update, if this may be a match
+ // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called.
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const = 0;
+
+ // Called for matched candidate after all potential matches are iterated.
+ // Used to update HMDevice creation arguments from Sensor.
+ // Optional return param 'newDeviceFlag' will be set to true if the
+ // 'desc' refers to a new device; false, otherwise.
+ // Return 'false' to create new object, 'true' if done with this argument.
+ virtual bool UpdateMatchedCandidate(
+ const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL)
+ { OVR_UNUSED2(desc, newDeviceFlag); return false; }
+
+ // Matches HID device to the descriptor.
+ virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; }
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& /*path*/) { return false; }
+//protected:
+ DeviceFactory* const pFactory;
+ const DeviceType Type;
+
+ // List in which this descriptor lives. pList->CreateLock required if added/removed.
+ Ptr<DeviceManagerLock> pLock;
+
+ // Strong references to us: Incremented by Device, DeviceHandles & Enumerators.
+ // May be 0 if device not created and there are no handles.
+ // Following transitions require pList->CreateLock:
+ // {1 -> 0}: May delete & remove handle if no longer available.
+ // {0 -> 1}: Device creation is only possible if manager is still alive.
+ AtomicInt<UInt32> HandleCount;
+ // If not null, points to our created device instance. Modified during lock only.
+ DeviceBase* pDevice;
+ // True if device is marked as available during enumeration.
+ bool Enumerated;
+};
+
+
+
+// Common data present in the implementation of every DeviceBase.
+// Injected by DeviceImpl.
+class DeviceCommon
+{
+public:
+ AtomicInt<UInt32> RefCount;
+ Ptr<DeviceCreateDesc> pCreateDesc;
+ Ptr<DeviceBase> pParent;
+ volatile bool ConnectedFlag;
+ MessageHandlerRef HandlerRef;
+
+ DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent)
+ : RefCount(1), pCreateDesc(createDesc), pParent(parent),
+ ConnectedFlag(true), HandlerRef(device)
+ {
+ }
+ virtual ~DeviceCommon() {}
+
+ // Device reference counting delegates to Manager thread to actually kill devices.
+ void DeviceAddRef();
+ void DeviceRelease();
+
+ Lock* GetLock() const { return pCreateDesc->GetLock(); }
+
+ virtual bool Initialize(DeviceBase* parent) = 0;
+ virtual void Shutdown() = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+// DeviceImpl address DeviceRecord implementation to a device base class B.
+// B must be derived form DeviceBase.
+
+template<class B>
+class DeviceImpl : public B, public DeviceCommon
+{
+public:
+ DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceCommon(createDesc, getThis(), parent)
+ {
+ }
+
+ // Convenience method to avoid manager access typecasts.
+ DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; }
+
+ // Inline to avoid warnings.
+ DeviceImpl* getThis() { return this; }
+
+ // Common implementation delegate to avoid virtual inheritance and dynamic casts.
+ virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; }
+
+ /*
+ virtual void AddRef() { pCreateDesc->DeviceAddRef(); }
+ virtual void Release() { pCreateDesc->DeviceRelease(); }
+ virtual DeviceBase* GetParent() const { return pParent.GetPtr(); }
+ virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;}
+ virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); }
+ virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ virtual DeviceType GetType() const { return pCreateDesc->Type; }
+ */
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceFactory
+
+// DeviceFactory is maintained in DeviceManager for each separately-enumerable
+// device type; factories allow separation of unrelated enumeration code.
+
+class DeviceFactory : public ListNode<DeviceFactory>, public NewOverrideBase
+{
+public:
+
+ DeviceFactory() : pManager(0)
+ {
+ pNext = pPrev = 0;
+ }
+ virtual ~DeviceFactory() { }
+
+ DeviceManagerImpl* GetManagerImpl() { return pManager; }
+
+ // Notifiers called when we are added to/removed from a device.
+ virtual bool AddedToManager(DeviceManagerImpl* manager)
+ {
+ OVR_ASSERT(pManager == 0);
+ pManager = manager;
+ return true;
+ }
+
+ virtual void RemovedFromManager()
+ {
+ pManager = 0;
+ }
+
+
+ // *** Device Enumeration/Creation Support
+
+ // Passed to EnumerateDevices to be informed of every device detected.
+ class EnumerateVisitor
+ {
+ public:
+ virtual void Visit(const DeviceCreateDesc& createDesc) = 0;
+ };
+
+ // Enumerates factory devices by notifying EnumerateVisitor about every
+ // device that is present.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0;
+
+ // Matches vendorId/productId pair with the factory; returns 'true'
+ // if the factory can handle the device.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+ {
+ OVR_UNUSED2(vendorId, productId);
+ return false;
+ }
+
+ // Detects the HID device and adds the DeviceCreateDesc into Devices list, if
+ // the device belongs to this factory. Returns 'false', if not.
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED2(pdevMgr, desc);
+ return false;
+ }
+
+protected:
+ DeviceManagerImpl* pManager;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManagerImpl
+
+// DeviceManagerImpl is a partial default DeviceManager implementation that
+// maintains a list of devices and supports their enumeration.
+
+class DeviceManagerImpl : public DeviceImpl<OVR::DeviceManager>, public ThreadCommandQueue
+{
+public:
+ DeviceManagerImpl();
+ ~DeviceManagerImpl();
+
+ // Constructor helper function to create Descriptor and manager lock during initialization.
+ static DeviceCreateDesc* CreateManagerDesc();
+
+ // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must
+ // be called by the platform-specific derived class.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+
+ // Every DeviceManager has an associated profile manager, which is used to store
+ // user settings that may affect device behavior.
+ virtual ProfileManager* GetProfileManager() const { return pProfileManager.GetPtr(); }
+
+ // Override to return ThreadCommandQueue implementation used to post commands
+ // to the background device manager thread (that must be created by Initialize).
+ virtual ThreadCommandQueue* GetThreadQueue() = 0;
+
+ // Returns the thread id of the DeviceManager.
+ virtual ThreadId GetThreadId() const = 0;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+
+ //
+ void AddFactory(DeviceFactory* factory)
+ {
+ // This lock is only needed if we call AddFactory after manager thread creation.
+ Lock::Locker scopeLock(GetLock());
+ Factories.PushBack(factory);
+ factory->AddedToManager(this);
+ }
+
+ void CallOnDeviceAdded(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc)));
+ }
+ void CallOnDeviceRemoved(DeviceCreateDesc* desc)
+ {
+ HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc)));
+ }
+
+ // Helper to access Common data for a device.
+ static DeviceCommon* GetDeviceCommon(DeviceBase* device)
+ {
+ return device->getDeviceCommon();
+ }
+
+
+ // Background-thread callbacks for DeviceCreation/Release. These
+ DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0);
+ Void ReleaseDevice_MgrThread(DeviceBase* device);
+
+
+ // Calls EnumerateDevices() on all factories
+ virtual Void EnumerateAllFactoryDevices();
+ // Enumerates devices for a particular factory.
+ virtual Void EnumerateFactoryDevices(DeviceFactory* factory);
+
+ virtual HIDDeviceManager* GetHIDDeviceManager() const
+ {
+ return HidDeviceManager;
+ }
+
+ // Adds device (DeviceCreateDesc*) into Devices. Returns NULL,
+ // if unsuccessful or device is already in the list.
+ virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc);
+
+ // Finds a device descriptor by path and optional type.
+ Ptr<DeviceCreateDesc> FindDevice(const String& path, DeviceType = Device_None);
+
+ // Finds HID device by HIDDeviceDesc.
+ Ptr<DeviceCreateDesc> FindHIDDevice(const HIDDeviceDesc&, bool created);
+ void DetectHIDDevice(const HIDDeviceDesc&);
+
+ // Manager Lock-protected list of devices.
+ List<DeviceCreateDesc> Devices;
+
+ // Factories used to detect and manage devices.
+ List<DeviceFactory> Factories;
+
+protected:
+ Ptr<HIDDeviceManager> HidDeviceManager;
+ Ptr<ProfileManager> pProfileManager;
+};
+
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_DeviceMessages.h b/LibOVR/Src/OVR_DeviceMessages.h
new file mode 100644
index 0000000..c182404
--- /dev/null
+++ b/LibOVR/Src/OVR_DeviceMessages.h
@@ -0,0 +1,273 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_DeviceMessages.h
+Content : Definition of messages generated by devices
+Created : February 5, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_DeviceMessages_h
+#define OVR_DeviceMessages_h
+
+#include "OVR_DeviceConstants.h"
+#include "OVR_DeviceHandle.h"
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_Color.h"
+#include "Kernel/OVR_String.h"
+
+namespace OVR {
+
+class DeviceBase;
+class DeviceHandle;
+class String;
+
+
+#define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex)
+
+// MessageType identifies the structure of the Message class; based on the message,
+// casting can be used to obtain the exact value.
+enum MessageType
+{
+ // Used for unassigned message types.
+ Message_None = 0,
+
+ // Device Manager Messages
+ Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager.
+ Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged.
+ // Sensor Messages
+ Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals.
+ Message_ExposureFrame = OVR_MESSAGETYPE(Sensor, 1),
+ Message_PixelRead = OVR_MESSAGETYPE(Sensor, 2),
+
+ // Latency Tester Messages
+ Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0),
+ Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1),
+ Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2),
+ Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3),
+
+ Message_CameraFrame = OVR_MESSAGETYPE(Camera, 0),
+ Message_CameraAdded = OVR_MESSAGETYPE(Camera, 1),
+};
+
+//-------------------------------------------------------------------------------------
+// Base class for all messages.
+class Message
+{
+public:
+ Message(MessageType type = Message_None,
+ DeviceBase* pdev = 0) : Type(type), pDevice(pdev)
+ { }
+
+ MessageType Type; // What kind of message this is.
+ DeviceBase* pDevice; // Device that emitted the message.
+};
+
+
+// Sensor BodyFrame notification.
+// Sensor uses Right-Handed coordinate system to return results, with the following
+// axis definitions:
+// - Y Up positive
+// - X Right Positive
+// - Z Back Positive
+// Rotations a counter-clockwise (CCW) while looking in the negative direction
+// of the axis. This means they are interpreted as follows:
+// - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane.
+// - Yaw is rotation around Y, positive for turning left.
+// - Pitch is rotation around X, positive for pitching up.
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor
+
+class MessageBodyFrame : public Message
+{
+public:
+ MessageBodyFrame(DeviceBase* dev)
+ : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f)
+ {
+ }
+
+ Vector3f Acceleration; // Acceleration in m/s^2.
+ Vector3f RotationRate; // Angular velocity in rad/s.
+ Vector3f MagneticField; // Magnetic field strength in Gauss.
+ float Temperature; // Temperature reading on sensor surface, in degrees Celsius.
+ float TimeDelta; // Time passed since last Body Frame, in seconds.
+
+ // The absolute time from the host computers perspective that the message should be
+ // interpreted as. This is based on incoming timestamp and processed by a filter
+ // that syncs the clocks while attempting to keep the distance between messages
+ // device clock matching.
+ //
+ // Integration should use TimeDelta, but prediction into the future should derive
+ // the delta time from PredictToSeconds - AbsoluteTimeSeconds.
+ //
+ // This value will generally be <= the return from a call to ovr_GetTimeInSeconds(),
+ // but could be greater by under 1 ms due to system time update interrupt delays.
+ //
+ double AbsoluteTimeSeconds;
+};
+
+// Sent when we receive a device status changes (e.g.:
+// Message_DeviceAdded, Message_DeviceRemoved).
+class MessageDeviceStatus : public Message
+{
+public:
+ MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev)
+ : Message(type, dev), Handle(hdev) { }
+
+ DeviceHandle Handle;
+};
+
+class MessageExposureFrame : public Message
+{
+public:
+ MessageExposureFrame(DeviceBase* dev)
+ : Message(Message_ExposureFrame, dev),
+ CameraPattern(0), CameraFrameCount(0), CameraTimeSeconds(0) { }
+
+ UByte CameraPattern;
+ UInt32 CameraFrameCount;
+ double CameraTimeSeconds;
+};
+
+class MessagePixelRead : public Message
+{
+public:
+ MessagePixelRead(DeviceBase* dev)
+ : Message(Message_PixelRead, dev),
+ PixelReadValue(0), SensorTimeSeconds(0), FrameTimeSeconds(0) { }
+
+ UByte PixelReadValue;
+ UInt32 RawSensorTime;
+ UInt32 RawFrameTime;
+ double SensorTimeSeconds;
+ double FrameTimeSeconds;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Latency Tester
+
+// Sent when we receive Latency Tester samples.
+class MessageLatencyTestSamples : public Message
+{
+public:
+ MessageLatencyTestSamples(DeviceBase* dev)
+ : Message(Message_LatencyTestSamples, dev)
+ {
+ }
+
+ Array<Color> Samples;
+};
+
+// Sent when a Latency Tester 'color detected' event occurs.
+class MessageLatencyTestColorDetected : public Message
+{
+public:
+ MessageLatencyTestColorDetected(DeviceBase* dev)
+ : Message(Message_LatencyTestColorDetected, dev)
+ {
+ }
+
+ UInt16 Elapsed;
+ Color DetectedValue;
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'change color' event occurs.
+class MessageLatencyTestStarted : public Message
+{
+public:
+ MessageLatencyTestStarted(DeviceBase* dev)
+ : Message(Message_LatencyTestStarted, dev)
+ {
+ }
+
+ Color TargetValue;
+};
+
+// Sent when a Latency Tester 'button' event occurs.
+class MessageLatencyTestButton : public Message
+{
+public:
+ MessageLatencyTestButton(DeviceBase* dev)
+ : Message(Message_LatencyTestButton, dev)
+ {
+ }
+
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Camera
+
+// Sent by camera, frame.
+class MessageCameraFrame : public Message
+{
+public:
+ MessageCameraFrame(DeviceBase* dev)
+ : Message(Message_CameraFrame, dev), CameraHandle(NULL), pFrameData(NULL)
+ {
+ LostFrames = 0;
+ }
+
+ void SetInfo(UInt32 frameNumber, double timeSeconds, UInt32 width, UInt32 height, UInt32 format)
+ {
+ FrameNumber = frameNumber;
+ ArrivalTimeSeconds = timeSeconds;
+ Width = width;
+ Height = height;
+ Format = format;
+ }
+
+ void SetData(const UByte* pdata, UInt32 sizeInBytes)
+ {
+ pFrameData = pdata;
+ FrameSizeInBytes = sizeInBytes;
+ }
+
+ UInt32 FrameNumber; // an index of the frame
+ double ArrivalTimeSeconds; // frame time in seconds, as recorded by the host computer
+ const UByte* pFrameData; // a ptr to frame data.
+ UInt32 FrameSizeInBytes; // size of the data in the pFrameData.
+ UInt32 Width, Height; // width & height in pixels.
+ UInt32 Format; // format of pixel, see CameraDevice::PixelFormat enum
+ UInt32 LostFrames; // number of lost frames before this frame
+ String DeviceIdentifier; // identifies the device sensing the message
+ UInt32* CameraHandle; // Identifies the camera object associated with this frame
+};
+
+// Sent when a new camera is connected
+class MessageCameraAdded : public Message
+{
+public:
+ MessageCameraAdded(DeviceBase* dev)
+ : Message(Message_CameraAdded, dev) { }
+
+ MessageCameraAdded(UInt32* cam)
+ : Message(Message_CameraAdded, NULL), CameraHandle(cam) { }
+
+ UInt32* CameraHandle; // Identifies the camera object associated with this frame
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDevice.h b/LibOVR/Src/OVR_HIDDevice.h
new file mode 100644
index 0000000..24bfcfa
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDevice.h
@@ -0,0 +1,154 @@
+/************************************************************************************
+
+Filename : OVR_HIDDevice.h
+Content : Cross platform HID device interface.
+Created : February 22, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDevice_h
+#define OVR_HIDDevice_h
+
+#include "OVR_HIDDeviceBase.h"
+
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Timer.h"
+
+namespace OVR {
+
+class HIDDevice;
+class DeviceManager;
+
+// HIDDeviceDesc contains interesting attributes of a HID device, including a Path
+// that can be used to create it.
+struct HIDDeviceDesc
+{
+ UInt16 VendorId;
+ UInt16 ProductId;
+ UInt16 VersionNumber;
+ UInt16 Usage;
+ UInt16 UsagePage;
+ String Path; // Platform specific.
+ String Manufacturer;
+ String Product;
+ String SerialNumber;
+};
+
+// HIDEnumerateVisitor exposes a Visit interface called for every detected device
+// by HIDDeviceManager::Enumerate.
+class HIDEnumerateVisitor
+{
+public:
+
+ // Should return true if we are interested in supporting
+ // this HID VendorId and ProductId pair.
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ { OVR_UNUSED2(vendorId, productId); return true; }
+
+ // Override to get notified about available device. Will only be called for
+ // devices that matched MatchVendorProduct.
+ virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceManager
+
+// Internal manager for enumerating and opening HID devices.
+// If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the
+// DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call
+// 'Create' below.
+class HIDDeviceManager : public RefCountBase<HIDDeviceManager>
+{
+public:
+
+ // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time.
+ static HIDDeviceManager* Create(Ptr<OVR::DeviceManager>& deviceManager);
+
+ // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class.
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0;
+
+ // Open a HID device with the specified path.
+ virtual HIDDevice* Open(const String& path) = 0;
+
+protected:
+ HIDDeviceManager()
+ { }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDevice
+
+// HID device object. This is designed to be operated in synchronous
+// and asynchronous modes. With no handler set, input messages will be
+// stored and can be retrieved by calling 'Read' or 'ReadBlocking'.
+class HIDDevice : public RefCountBase<HIDDevice>, public HIDDeviceBase
+{
+public:
+
+ HIDDevice()
+ : Handler(NULL)
+ {
+ }
+
+ virtual ~HIDDevice() {}
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+
+// Not yet implemented.
+/*
+ virtual bool Write(UByte* data, UInt32 length) = 0;
+
+ virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0;
+ virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0;
+*/
+
+ class HIDHandler
+ {
+ public:
+ virtual void OnInputReport(UByte* pData, UInt32 length)
+ { OVR_UNUSED2(pData, length); }
+
+ virtual double OnTicks(double tickSeconds)
+ { OVR_UNUSED1(tickSeconds); return 1000.0 ; }
+
+ enum HIDDeviceMessageType
+ {
+ HIDDeviceMessage_DeviceAdded = 0,
+ HIDDeviceMessage_DeviceRemoved = 1
+ };
+
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ { OVR_UNUSED1(messageType); }
+ };
+
+ void SetHandler(HIDHandler* handler)
+ { Handler = handler; }
+
+protected:
+ HIDHandler* Handler;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceBase.h b/LibOVR/Src/OVR_HIDDeviceBase.h
new file mode 100644
index 0000000..7dfd6b4
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDeviceBase.h
@@ -0,0 +1,51 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_HIDDeviceBase.h
+Content : Definition of HID device interface.
+Created : March 11, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceBase_h
+#define OVR_HIDDeviceBase_h
+
+#include "Kernel/OVR_Types.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** HIDDeviceBase
+
+// Base interface for HID devices.
+class HIDDeviceBase
+{
+public:
+
+ virtual ~HIDDeviceBase() { }
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0;
+ virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_HIDDeviceImpl.h b/LibOVR/Src/OVR_HIDDeviceImpl.h
new file mode 100644
index 0000000..1399da6
--- /dev/null
+++ b/LibOVR/Src/OVR_HIDDeviceImpl.h
@@ -0,0 +1,201 @@
+/************************************************************************************
+
+Filename : OVR_HIDDeviceImpl.h
+Content : Implementation of HIDDevice.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_HIDDeviceImpl_h
+#define OVR_HIDDeviceImpl_h
+
+//#include "OVR_Device.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+class HIDDeviceCreateDesc : public DeviceCreateDesc
+{
+public:
+ HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc)
+ : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { }
+ HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { }
+
+ virtual bool MatchDevice(const String& path)
+ {
+ // should it be case insensitive?
+ return HIDDesc.Path.CompareNoCase(path) == 0;
+ }
+
+ HIDDeviceDesc HIDDesc;
+};
+
+//-------------------------------------------------------------------------------------
+template<class B>
+class HIDDeviceImpl : public DeviceImpl<B>, public HIDDevice::HIDHandler
+{
+public:
+ HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent)
+ : DeviceImpl<B>(createDesc, parent)
+ {
+ }
+
+ // HIDDevice::Handler interface.
+ virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
+ {
+ MessageType handlerMessageType;
+ switch (messageType) {
+ case HIDDeviceMessage_DeviceAdded:
+ handlerMessageType = Message_DeviceAdded;
+ DeviceImpl<B>::ConnectedFlag = true;
+ break;
+
+ case HIDDeviceMessage_DeviceRemoved:
+ handlerMessageType = Message_DeviceRemoved;
+ DeviceImpl<B>::ConnectedFlag = false;
+ break;
+
+ default: OVR_ASSERT(0); return;
+ }
+
+ // Do device notification.
+ MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc));
+ this->HandlerRef.Call(status);
+
+ // Do device manager notification.
+ DeviceManagerImpl* manager = this->GetManagerImpl();
+ switch (handlerMessageType) {
+ case Message_DeviceAdded:
+ manager->CallOnDeviceAdded(this->pCreateDesc);
+ break;
+
+ case Message_DeviceRemoved:
+ manager->CallOnDeviceRemoved(this->pCreateDesc);
+ break;
+
+ default:;
+ }
+ }
+
+ virtual bool Initialize(DeviceBase* parent)
+ {
+ // Open HID device.
+ HIDDeviceDesc& hidDesc = *getHIDDesc();
+ HIDDeviceManager* pManager = GetHIDDeviceManager();
+
+
+ HIDDevice* device = pManager->Open(hidDesc.Path);
+ if (!device)
+ {
+ return false;
+ }
+
+ InternalDevice = *device;
+ InternalDevice->SetHandler(this);
+
+ // AddRef() to parent, forcing chain to stay alive.
+ DeviceImpl<B>::pParent = parent;
+
+ return true;
+ }
+
+ virtual void Shutdown()
+ {
+ InternalDevice->SetHandler(NULL);
+
+ DeviceImpl<B>::pParent.Clear();
+ }
+
+ DeviceManager* GetDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl();
+ }
+
+ HIDDeviceManager* GetHIDDeviceManager()
+ {
+ return DeviceImpl<B>::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager();
+ }
+
+ bool SetFeatureReport(UByte* data, UInt32 length)
+ {
+ // Push call with wait.
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, data, length))
+ return false;
+
+ return result;
+ }
+
+ bool setFeatureReport(UByte* data, UInt32 length)
+ {
+ return InternalDevice->SetFeatureReport(data, length);
+ }
+
+ bool GetFeatureReport(UByte* data, UInt32 length)
+ {
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length))
+ return false;
+
+ return result;
+ }
+
+ bool getFeatureReport(UByte* data, UInt32 length)
+ {
+ return InternalDevice->GetFeatureReport(data, length);
+ }
+
+ UByte GetDeviceInterfaceVersion()
+ {
+ UInt16 versionNumber = getHIDDesc()->VersionNumber;
+
+ // Our interface and hardware versions are represented as two BCD digits each.
+ // Interface version is in the last two digits.
+ UByte interfaceVersion = (UByte) ((versionNumber & 0x000F) >> 0) * 1 +
+ ((versionNumber & 0x00F0) >> 4) * 10;
+ return interfaceVersion;
+ }
+
+protected:
+ HIDDevice* GetInternalDevice() const
+ {
+ return InternalDevice;
+ }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+
+ HIDDeviceCreateDesc* getCreateDesc() const
+ { return (HIDDeviceCreateDesc*) &(*DeviceImpl<B>::pCreateDesc); }
+
+private:
+ Ptr<HIDDevice> InternalDevice;
+};
+
+} // namespace OVR
+
+#endif
diff --git a/LibOVR/Src/OVR_JSON.cpp b/LibOVR/Src/OVR_JSON.cpp
new file mode 100644
index 0000000..262a0d9
--- /dev/null
+++ b/LibOVR/Src/OVR_JSON.cpp
@@ -0,0 +1,1185 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_JSON.h
+Content : JSON format reader and writer
+Created : April 9, 2013
+Author : Brant Lewis
+Notes :
+ The code is a derivative of the cJSON library written by Dave Gamble and subject
+ to the following permissive copyright.
+
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+
+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 <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include "OVR_JSON.h"
+#include "Kernel/OVR_SysFile.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------
+// Create a new copy of a string
+static char* JSON_strdup(const char* str)
+{
+ UPInt len = OVR_strlen(str) + 1;
+ char* copy = (char*)OVR_ALLOC(len);
+ if (!copy)
+ return 0;
+ memcpy(copy, str, len);
+ return copy;
+}
+
+
+//-----------------------------------------------------------------------------
+// Render the number from the given item into a string.
+static char* PrintNumber(double d)
+{
+ char *str;
+ //double d=item->valuedouble;
+ int valueint = (int)d;
+ if (fabs(((double)valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+ {
+ str=(char*)OVR_ALLOC(21); // 2^64+1 can be represented in 21 chars.
+ if (str)
+ OVR_sprintf(str, 21, "%d", valueint);
+ }
+ else
+ {
+ str=(char*)OVR_ALLOC(64); // This is a nice tradeoff.
+ if (str)
+ {
+ if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)
+ OVR_sprintf(str, 64, "%.0f", d);
+ else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)
+ OVR_sprintf(str, 64, "%e", d);
+ else
+ OVR_sprintf(str, 64, "%f", d);
+ }
+ }
+ return str;
+}
+
+// Parse the input text into an un-escaped cstring, and populate item.
+static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+// Helper to assign error sting and return 0.
+const char* AssignError(const char** perror, const char *errorMessage)
+{
+ if (perror)
+ *perror = errorMessage;
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// ***** JSON Node class
+
+JSON::JSON(JSONItemType itemType)
+ : Type(itemType), dValue(0.0)
+{
+}
+
+JSON::~JSON()
+{
+ JSON* child = Children.GetFirst();
+ while (!Children.IsNull(child))
+ {
+ child->RemoveNode();
+ child->Release();
+ child = Children.GetFirst();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Parse the input text to generate a number, and populate the result into item
+// Returns the text position after the parsed number
+const char* JSON::parseNumber(const char *num)
+{
+ const char* num_start = num;
+ double n=0, sign=1, scale=0;
+ int subscale = 0,
+ signsubscale = 1;
+
+ // Could use sscanf for this?
+ if (*num=='-')
+ sign=-1,num++; // Has sign?
+ if (*num=='0')
+ num++; // is zero
+
+ if (*num>='1' && *num<='9')
+ {
+ do
+ {
+ n=(n*10.0)+(*num++ -'0');
+ }
+ while (*num>='0' && *num<='9'); // Number?
+ }
+
+ if (*num=='.' && num[1]>='0' && num[1]<='9')
+ {
+ num++;
+ do
+ {
+ n=(n*10.0)+(*num++ -'0');
+ scale--;
+ }
+ while (*num>='0' && *num<='9'); // Fractional part?
+ }
+
+ if (*num=='e' || *num=='E') // Exponent?
+ {
+ num++;
+ if (*num=='+')
+ num++;
+ else if (*num=='-')
+ {
+ signsubscale=-1;
+ num++; // With sign?
+ }
+
+ while (*num>='0' && *num<='9')
+ subscale=(subscale*10)+(*num++ - '0'); // Number?
+ }
+
+ // Number = +/- number.fraction * 10^+/- exponent
+ n = sign*n*pow(10.0,(scale+subscale*signsubscale));
+
+ // Assign parsed value.
+ Type = JSON_Number;
+ dValue = n;
+ Value.AssignString(num_start, num - num_start);
+
+ return num;
+}
+
+// Parses a hex string up to the specified number of digits.
+// Returns the first character after the string.
+const char* ParseHex(unsigned* val, unsigned digits, const char* str)
+{
+ *val = 0;
+
+ for(unsigned digitCount = 0; digitCount < digits; digitCount++, str++)
+ {
+ unsigned v = *str;
+
+ if ((v >= '0') && (v <= '9'))
+ v -= '0';
+ else if ((v >= 'a') && (v <= 'f'))
+ v = 10 + v - 'a';
+ else if ((v >= 'A') && (v <= 'F'))
+ v = 10 + v - 'A';
+ else
+ break;
+
+ *val = *val * 16 + v;
+ }
+
+ return str;
+}
+
+//-----------------------------------------------------------------------------
+// Parses the input text into a string item and returns the text position after
+// the parsed string
+const char* JSON::parseString(const char* str, const char** perror)
+{
+ const char* ptr = str+1;
+ const char* p;
+ char* ptr2;
+ char* out;
+ int len=0;
+ unsigned uc, uc2;
+
+ if (*str!='\"')
+ {
+ return AssignError(perror, "Syntax Error: Missing quote");
+ }
+
+ while (*ptr!='\"' && *ptr && ++len)
+ {
+ if (*ptr++ == '\\') ptr++; // Skip escaped quotes.
+ }
+
+ // This is how long we need for the string, roughly.
+ out=(char*)OVR_ALLOC(len+1);
+ if (!out)
+ return 0;
+
+ ptr = str+1;
+ ptr2= out;
+
+ while (*ptr!='\"' && *ptr)
+ {
+ if (*ptr!='\\')
+ {
+ *ptr2++ = *ptr++;
+ }
+ else
+ {
+ ptr++;
+ switch (*ptr)
+ {
+ case 'b': *ptr2++ = '\b'; break;
+ case 'f': *ptr2++ = '\f'; break;
+ case 'n': *ptr2++ = '\n'; break;
+ case 'r': *ptr2++ = '\r'; break;
+ case 't': *ptr2++ = '\t'; break;
+
+ // Transcode utf16 to utf8.
+ case 'u':
+
+ // Get the unicode char.
+ p = ParseHex(&uc, 4, ptr + 1);
+ if (ptr != p)
+ ptr = p - 1;
+
+ if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)
+ break; // Check for invalid.
+
+ // UTF16 surrogate pairs.
+ if (uc>=0xD800 && uc<=0xDBFF)
+ {
+ if (ptr[1]!='\\' || ptr[2]!='u')
+ break; // Missing second-half of surrogate.
+
+ p= ParseHex(&uc2, 4, ptr + 3);
+ if (ptr != p)
+ ptr = p - 1;
+
+ if (uc2<0xDC00 || uc2>0xDFFF)
+ break; // Invalid second-half of surrogate.
+
+ uc = 0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
+ }
+
+ len=4;
+
+ if (uc<0x80)
+ len=1;
+ else if (uc<0x800)
+ len=2;
+ else if (uc<0x10000)
+ len=3;
+
+ ptr2+=len;
+
+ switch (len)
+ {
+ case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+ case 1: *--ptr2 = (char)(uc | firstByteMark[len]);
+ }
+ ptr2+=len;
+ break;
+
+ default:
+ *ptr2++ = *ptr;
+ break;
+ }
+ ptr++;
+ }
+ }
+
+ *ptr2 = 0;
+ if (*ptr=='\"')
+ ptr++;
+
+ // Make a copy of the string
+ Value=out;
+ OVR_FREE(out);
+ Type=JSON_String;
+
+ return ptr;
+}
+
+//-----------------------------------------------------------------------------
+// Render the string provided to an escaped version that can be printed.
+char* PrintString(const char* str)
+{
+ const char *ptr;
+ char *ptr2,*out;
+ int len=0;
+ unsigned char token;
+
+ if (!str)
+ return JSON_strdup("");
+ ptr=str;
+
+ token=*ptr;
+ while (token && ++len)\
+ {
+ if (strchr("\"\\\b\f\n\r\t",token))
+ len++;
+ else if (token<32)
+ len+=5;
+ ptr++;
+ token=*ptr;
+ }
+
+ int buff_size = len+3;
+ out=(char*)OVR_ALLOC(buff_size);
+ if (!out)
+ return 0;
+
+ ptr2 = out;
+ ptr = str;
+ *ptr2++ = '\"';
+
+ while (*ptr)
+ {
+ if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\')
+ *ptr2++=*ptr++;
+ else
+ {
+ *ptr2++='\\';
+ switch (token=*ptr++)
+ {
+ case '\\': *ptr2++='\\'; break;
+ case '\"': *ptr2++='\"'; break;
+ case '\b': *ptr2++='b'; break;
+ case '\f': *ptr2++='f'; break;
+ case '\n': *ptr2++='n'; break;
+ case '\r': *ptr2++='r'; break;
+ case '\t': *ptr2++='t'; break;
+ default:
+ OVR_sprintf(ptr2, buff_size - (ptr2-out), "u%04x",token);
+ ptr2+=5;
+ break; // Escape and print.
+ }
+ }
+ }
+ *ptr2++='\"';
+ *ptr2++=0;
+ return out;
+}
+
+//-----------------------------------------------------------------------------
+// Utility to jump whitespace and cr/lf
+static const char* skip(const char* in)
+{
+ while (in && *in && (unsigned char)*in<=' ')
+ in++;
+ return in;
+}
+
+//-----------------------------------------------------------------------------
+// Parses the supplied buffer of JSON text and returns a JSON object tree
+// The returned object must be Released after use
+JSON* JSON::Parse(const char* buff, const char** perror)
+{
+ const char* end = 0;
+ JSON* json = new JSON();
+
+ if (!json)
+ {
+ AssignError(perror, "Error: Failed to allocate memory");
+ return 0;
+ }
+
+ end = json->parseValue(skip(buff), perror);
+ if (!end)
+ {
+ json->Release();
+ return NULL;
+ } // parse failure. ep is set.
+
+ return json;
+}
+
+//-----------------------------------------------------------------------------
+// This version works for buffers that are not null terminated strings.
+JSON* JSON::ParseBuffer(const char *buff, int len, const char** perror)
+{
+ // Our JSON parser does not support length-based parsing,
+ // so ensure it is null-terminated.
+ char *termStr = new char[len + 1];
+ memcpy(termStr, buff, len);
+ termStr[len] = '\0';
+
+ JSON *objJson = Parse(termStr, perror);
+
+ delete[]termStr;
+
+ return objJson;
+}
+
+//-----------------------------------------------------------------------------
+// Parser core - when encountering text, process appropriately.
+const char* JSON::parseValue(const char* buff, const char** perror)
+{
+ if (perror)
+ *perror = 0;
+
+ if (!buff)
+ return NULL; // Fail on null.
+
+ if (!strncmp(buff,"null",4))
+ {
+ Type = JSON_Null;
+ return buff+4;
+ }
+ if (!strncmp(buff,"false",5))
+ {
+ Type = JSON_Bool;
+ Value = "false";
+ dValue = 0;
+ return buff+5;
+ }
+ if (!strncmp(buff,"true",4))
+ {
+ Type = JSON_Bool;
+ Value = "true";
+ dValue = 1;
+ return buff+4;
+ }
+ if (*buff=='\"')
+ {
+ return parseString(buff, perror);
+ }
+ if (*buff=='-' || (*buff>='0' && *buff<='9'))
+ {
+ return parseNumber(buff);
+ }
+ if (*buff=='[')
+ {
+ return parseArray(buff, perror);
+ }
+ if (*buff=='{')
+ {
+ return parseObject(buff, perror);
+ }
+
+ return AssignError(perror, "Syntax Error: Invalid syntax");
+}
+
+
+//-----------------------------------------------------------------------------
+// Render a value to text.
+char* JSON::PrintValue(int depth, bool fmt)
+{
+ char *out=0;
+
+ switch (Type)
+ {
+ case JSON_Null: out = JSON_strdup("null"); break;
+ case JSON_Bool:
+ if (dValue == 0)
+ out = JSON_strdup("false");
+ else
+ out = JSON_strdup("true");
+ break;
+ case JSON_Number: out = PrintNumber(dValue); break;
+ case JSON_String: out = PrintString(Value); break;
+ case JSON_Array: out = PrintArray(depth, fmt); break;
+ case JSON_Object: out = PrintObject(depth, fmt); break;
+ case JSON_None: OVR_ASSERT_LOG(false, ("Bad JSON type.")); break;
+ }
+ return out;
+}
+
+//-----------------------------------------------------------------------------
+// Build an array object from input text and returns the text position after
+// the parsed array
+const char* JSON::parseArray(const char* buff, const char** perror)
+{
+ JSON *child;
+ if (*buff!='[')
+ {
+ return AssignError(perror, "Syntax Error: Missing opening bracket");
+ }
+
+ Type=JSON_Array;
+ buff=skip(buff+1);
+
+ if (*buff==']')
+ return buff+1; // empty array.
+
+ child = new JSON();
+ if (!child)
+ return 0; // memory fail
+ Children.PushBack(child);
+
+ buff=skip(child->parseValue(skip(buff), perror)); // skip any spacing, get the buff.
+ if (!buff)
+ return 0;
+
+ while (*buff==',')
+ {
+ JSON *new_item = new JSON();
+ if (!new_item)
+ return AssignError(perror, "Error: Failed to allocate memory");
+
+ Children.PushBack(new_item);
+
+ buff=skip(new_item->parseValue(skip(buff+1), perror));
+ if (!buff)
+ return AssignError(perror, "Error: Failed to allocate memory");
+ }
+
+ if (*buff==']')
+ return buff+1; // end of array
+
+ return AssignError(perror, "Syntax Error: Missing ending bracket");
+}
+
+//-----------------------------------------------------------------------------
+// Render an array to text. The returned text must be freed
+char* JSON::PrintArray(int depth, bool fmt)
+{
+ char **entries;
+ char * out = 0,*ptr,*ret;
+ SPInt len = 5;
+
+ bool fail = false;
+
+ // How many entries in the array?
+ int numentries = GetItemCount();
+ if (!numentries)
+ {
+ out=(char*)OVR_ALLOC(3);
+ if (out)
+ OVR_strcpy(out, 3, "[]");
+ return out;
+ }
+ // Allocate an array to hold the values for each
+ entries=(char**)OVR_ALLOC(numentries*sizeof(char*));
+ if (!entries)
+ return 0;
+ memset(entries,0,numentries*sizeof(char*));
+
+ //// Retrieve all the results:
+ JSON* child = Children.GetFirst();
+ for (int i=0; i<numentries; i++)
+ {
+ //JSON* child = Children[i];
+ ret=child->PrintValue(depth+1, fmt);
+ entries[i]=ret;
+ if (ret)
+ len+=OVR_strlen(ret)+2+(fmt?1:0);
+ else
+ {
+ fail = true;
+ break;
+ }
+ child = Children.GetNext(child);
+ }
+
+ // If we didn't fail, try to malloc the output string
+ if (!fail)
+ out=(char*)OVR_ALLOC(len);
+ // If that fails, we fail.
+ if (!out)
+ fail = true;
+
+ // Handle failure.
+ if (fail)
+ {
+ for (int i=0; i<numentries; i++)
+ {
+ if (entries[i])
+ OVR_FREE(entries[i]);
+ }
+ OVR_FREE(entries);
+ return 0;
+ }
+
+ // Compose the output array.
+ *out='[';
+ ptr=out+1;
+ *ptr=0;
+ for (int i=0; i<numentries; i++)
+ {
+ OVR_strcpy(ptr, len - (ptr-out), entries[i]);
+ ptr+=OVR_strlen(entries[i]);
+ if (i!=numentries-1)
+ {
+ *ptr++=',';
+ if (fmt)
+ *ptr++=' ';
+ *ptr=0;
+ }
+ OVR_FREE(entries[i]);
+ }
+ OVR_FREE(entries);
+ *ptr++=']';
+ *ptr++=0;
+ return out;
+}
+
+//-----------------------------------------------------------------------------
+// Build an object from the supplied text and returns the text position after
+// the parsed object
+const char* JSON::parseObject(const char* buff, const char** perror)
+{
+ if (*buff!='{')
+ {
+ return AssignError(perror, "Syntax Error: Missing opening brace");
+ }
+
+ Type=JSON_Object;
+ buff=skip(buff+1);
+ if (*buff=='}')
+ return buff+1; // empty array.
+
+ JSON* child = new JSON();
+ Children.PushBack(child);
+
+ buff=skip(child->parseString(skip(buff), perror));
+ if (!buff)
+ return 0;
+ child->Name = child->Value;
+ child->Value.Clear();
+
+ if (*buff!=':')
+ {
+ return AssignError(perror, "Syntax Error: Missing colon");
+ }
+
+ buff=skip(child->parseValue(skip(buff+1), perror)); // skip any spacing, get the value.
+ if (!buff)
+ return 0;
+
+ while (*buff==',')
+ {
+ child = new JSON();
+ if (!child)
+ return 0; // memory fail
+
+ Children.PushBack(child);
+
+ buff=skip(child->parseString(skip(buff+1), perror));
+ if (!buff)
+ return 0;
+
+ child->Name=child->Value;
+ child->Value.Clear();
+
+ if (*buff!=':')
+ {
+ return AssignError(perror, "Syntax Error: Missing colon");
+ } // fail!
+
+ // Skip any spacing, get the value.
+ buff=skip(child->parseValue(skip(buff+1), perror));
+ if (!buff)
+ return 0;
+ }
+
+ if (*buff=='}')
+ return buff+1; // end of array
+
+ return AssignError(perror, "Syntax Error: Missing closing brace");
+}
+
+//-----------------------------------------------------------------------------
+// Render an object to text. The returned string must be freed
+char* JSON::PrintObject(int depth, bool fmt)
+{
+ char** entries = 0, **names = 0;
+ char* out = 0;
+ char* ptr, *ret, *str;
+ SPInt len = 7, i = 0, j;
+ bool fail = false;
+
+ // Count the number of entries.
+ int numentries = GetItemCount();
+
+ // Explicitly handle empty object case
+ if (numentries == 0)
+ {
+ out=(char*)OVR_ALLOC(fmt?depth+3:3);
+ if (!out)
+ return 0;
+ ptr=out;
+ *ptr++='{';
+
+ if (fmt)
+ {
+ *ptr++='\n';
+ for (i=0;i<depth-1;i++)
+ *ptr++='\t';
+ }
+ *ptr++='}';
+ *ptr++=0;
+ return out;
+ }
+ // Allocate space for the names and the objects
+ entries=(char**)OVR_ALLOC(numentries*sizeof(char*));
+ if (!entries)
+ return 0;
+ names=(char**)OVR_ALLOC(numentries*sizeof(char*));
+
+ if (!names)
+ {
+ OVR_FREE(entries);
+ return 0;
+ }
+ memset(entries,0,sizeof(char*)*numentries);
+ memset(names,0,sizeof(char*)*numentries);
+
+ // Collect all the results into our arrays:
+ depth++;
+ if (fmt)
+ len+=depth;
+
+ JSON* child = Children.GetFirst();
+ while (!Children.IsNull(child))
+ {
+ names[i] = str = PrintString(child->Name);
+ entries[i++] = ret = child->PrintValue(depth, fmt);
+
+ if (str && ret)
+ {
+ len += OVR_strlen(ret)+OVR_strlen(str)+2+(fmt?2+depth:0);
+ }
+ else
+ {
+ fail = true;
+ break;
+ }
+
+ child = Children.GetNext(child);
+ }
+
+ // Try to allocate the output string
+ if (!fail)
+ out=(char*)OVR_ALLOC(len);
+ if (!out)
+ fail=true;
+
+ // Handle failure
+ if (fail)
+ {
+ for (i=0;i<numentries;i++)
+ {
+ if (names[i])
+ OVR_FREE(names[i]);
+
+ if (entries[i])
+ OVR_FREE(entries[i]);}
+
+ OVR_FREE(names);
+ OVR_FREE(entries);
+ return 0;
+ }
+
+ // Compose the output:
+ *out = '{';
+ ptr = out+1;
+ if (fmt)
+ *ptr++='\n';
+ *ptr = 0;
+
+ for (i=0; i<numentries; i++)
+ {
+ if (fmt)
+ {
+ for (j=0; j<depth; j++)
+ *ptr++ = '\t';
+ }
+ OVR_strcpy(ptr, len - (ptr-out), names[i]);
+ ptr += OVR_strlen(names[i]);
+ *ptr++ =':';
+
+ if (fmt)
+ *ptr++='\t';
+
+ OVR_strcpy(ptr, len - (ptr-out), entries[i]);
+ ptr+=OVR_strlen(entries[i]);
+
+ if (i!=numentries-1)
+ *ptr++ = ',';
+
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+
+ OVR_FREE(names[i]);
+ OVR_FREE(entries[i]);
+ }
+
+ OVR_FREE(names);
+ OVR_FREE(entries);
+
+ if (fmt)
+ {
+ for (i=0;i<depth-1;i++)
+ *ptr++='\t';
+ }
+ *ptr++='}';
+ *ptr++=0;
+
+ return out;
+}
+
+
+
+// Returns the number of child items in the object
+// Counts the number of items in the object.
+unsigned JSON::GetItemCount() const
+{
+ unsigned count = 0;
+ for(const JSON* p = Children.GetFirst(); !Children.IsNull(p); p = p->pNext)
+ count++;
+ return count;
+}
+
+JSON* JSON::GetItemByIndex(unsigned index)
+{
+ unsigned i = 0;
+ JSON* child = 0;
+
+ if (!Children.IsEmpty())
+ {
+ child = Children.GetFirst();
+
+ while (i < index)
+ {
+ if (Children.IsNull(child->pNext))
+ {
+ child = 0;
+ break;
+ }
+ child = child->pNext;
+ i++;
+ }
+ }
+
+ return child;
+}
+
+// Returns the child item with the given name or NULL if not found
+JSON* JSON::GetItemByName(const char* name)
+{
+ JSON* child = 0;
+
+ if (!Children.IsEmpty())
+ {
+ child = Children.GetFirst();
+
+ while (OVR_strcmp(child->Name, name) != 0)
+ {
+ if (Children.IsNull(child->pNext))
+ {
+ child = 0;
+ break;
+ }
+ child = child->pNext;
+ }
+ }
+
+ return child;
+}
+
+//-----------------------------------------------------------------------------
+// Adds a new item to the end of the child list
+void JSON::AddItem(const char *string, JSON *item)
+{
+ if (!item)
+ return;
+
+ item->Name = string;
+ Children.PushBack(item);
+}
+
+/*
+
+// Removes and frees the items at the given index
+void JSON::DeleteItem(unsigned int index)
+{
+ unsigned int num_items = 0;
+ JSON* child = Children.GetFirst();
+ while (!Children.IsNull(child) && num_items < index)
+ {
+ num_items++;
+ child = Children.GetNext(child);
+ }
+
+ if (!Children.IsNull(child))
+
+ child->RemoveNode();
+ child->Release();
+ }
+}
+
+// Replaces and frees the item at the give index with the new item
+void JSON::ReplaceItem(unsigned int index, JSON* new_item)
+{
+ unsigned int num_items = 0;
+ JSON* child = Children.GetFirst();
+ while (!Children.IsNull(child) && num_items < index)
+ {
+ num_items++;
+ child = Children.GetNext(child);
+ }
+
+ if (!Children.IsNull(child))
+ {
+ child->ReplaceNodeWith(new_item);
+ child->Release();
+ }
+}
+*/
+
+// Removes and frees the last child item
+void JSON::RemoveLast()
+{
+ JSON* child = Children.GetLast();
+ if (!Children.IsNull(child))
+ {
+ child->RemoveNode();
+ child->Release();
+ }
+}
+
+// Helper function to simplify creation of a typed object
+JSON* JSON::createHelper(JSONItemType itemType, double dval, const char* strVal)
+{
+ JSON *item = new JSON(itemType);
+ if (item)
+ {
+ item->dValue = dval;
+ if (strVal)
+ item->Value = strVal;
+ }
+ return item;
+}
+
+//-----------------------------------------------------------------------------
+// Get elements by name
+double JSON::GetNumberByName(const char *name, double defValue)
+{
+ JSON* item = GetItemByName(name);
+ if (!item || item->Type != JSON_Number) {
+ return defValue;
+ }
+ else {
+ return item->dValue;
+ }
+}
+
+int JSON::GetIntByName(const char *name, int defValue)
+{
+ JSON* item = GetItemByName(name);
+ if (!item || item->Type != JSON_Number) {
+ return defValue;
+ }
+ else {
+ return (int)item->dValue;
+ }
+}
+
+bool JSON::GetBoolByName(const char *name, bool defValue)
+{
+ JSON* item = GetItemByName(name);
+ if (!item || item->Type != JSON_Bool) {
+ return defValue;
+ }
+ else {
+ return (int)item->dValue != 0;
+ }
+}
+
+String JSON::GetStringByName(const char *name, const String &defValue)
+{
+ JSON* item = GetItemByName(name);
+ if (!item || item->Type != JSON_String) {
+ return defValue;
+ }
+ else {
+ return item->Value;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Adds an element to an array object type
+void JSON::AddArrayElement(JSON *item)
+{
+ if (!item)
+ return;
+
+ Children.PushBack(item);
+}
+
+// Inserts an element into a valid array position
+void JSON::InsertArrayElement(int index, JSON *item)
+{
+ if (!item)
+ return;
+
+ if (index == 0)
+ {
+ Children.PushFront(item);
+ return;
+ }
+
+ JSON* iter = Children.GetFirst();
+ int i=0;
+ while (iter && i<index)
+ {
+ iter = Children.GetNext(iter);
+ i++;
+ }
+
+ if (iter)
+ iter->InsertNodeBefore(item);
+ else
+ Children.PushBack(item);
+}
+
+// Returns the size of an array
+int JSON::GetArraySize()
+{
+ if (Type == JSON_Array)
+ return GetItemCount();
+ else
+ return 0;
+}
+
+// Returns the number value an the give array index
+double JSON::GetArrayNumber(int index)
+{
+ if (Type == JSON_Array)
+ {
+ JSON* number = GetItemByIndex(index);
+ return number ? number->dValue : 0.0;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Returns the string value at the given array index
+const char* JSON::GetArrayString(int index)
+{
+ if (Type == JSON_Array)
+ {
+ JSON* number = GetItemByIndex(index);
+ return number ? number->Value : 0;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+JSON* JSON::Copy()
+{
+ JSON* copy = new JSON(Type);
+ copy->Name = Name;
+ copy->Value = Value;
+ copy->dValue = dValue;
+
+ JSON* child = Children.GetFirst();
+ while (!Children.IsNull(child))
+ {
+ copy->Children.PushBack(child->Copy());
+ child = Children.GetNext(child);
+ }
+
+ return copy;
+}
+
+//-----------------------------------------------------------------------------
+// Loads and parses the given JSON file pathname and returns a JSON object tree.
+// The returned object must be Released after use.
+JSON* JSON::Load(const char* path, const char** perror)
+{
+ SysFile f;
+ if (!f.Open(path, File::Open_Read, File::Mode_Read))
+ {
+ AssignError(perror, "Failed to open file");
+ return NULL;
+ }
+
+ int len = f.GetLength();
+ UByte* buff = (UByte*)OVR_ALLOC(len + 1);
+ int bytes = f.Read(buff, len);
+ f.Close();
+
+ if (bytes == 0 || bytes != len)
+ {
+ OVR_FREE(buff);
+ return NULL;
+ }
+
+ // Ensure the result is null-terminated since Parse() expects null-terminated input.
+ buff[len] = '\0';
+
+ JSON* json = JSON::Parse((char*)buff, perror);
+ OVR_FREE(buff);
+ return json;
+}
+
+//-----------------------------------------------------------------------------
+// Serializes the JSON object and writes to the give file path
+bool JSON::Save(const char* path)
+{
+ SysFile f;
+ if (!f.Open(path, File::Open_Write | File::Open_Create | File::Open_Truncate, File::Mode_Write))
+ return false;
+
+ char* text = PrintValue(0, true);
+ if (text)
+ {
+ SPInt len = OVR_strlen(text);
+ OVR_ASSERT(len <= (SPInt)(int)len);
+
+ int bytes = f.Write((UByte*)text, (int)len);
+ f.Close();
+ OVR_FREE(text);
+ return (bytes == len);
+ }
+ else
+ {
+ return false;
+ }
+}
+
+}
diff --git a/LibOVR/Src/OVR_JSON.h b/LibOVR/Src/OVR_JSON.h
new file mode 100644
index 0000000..a2a603c
--- /dev/null
+++ b/LibOVR/Src/OVR_JSON.h
@@ -0,0 +1,164 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_JSON.h
+Content : JSON format reader and writer
+Created : April 9, 2013
+Author : Brant Lewis
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_JSON_H
+#define OVR_JSON_H
+
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_List.h"
+
+namespace OVR {
+
+// JSONItemType describes the type of JSON item, specifying the type of
+// data that can be obtained from it.
+enum JSONItemType
+{
+ JSON_None = 0,
+ JSON_Null = 1,
+ JSON_Bool = 2,
+ JSON_Number = 3,
+ JSON_String = 4,
+ JSON_Array = 5,
+ JSON_Object = 6
+};
+
+//-----------------------------------------------------------------------------
+// ***** JSON
+
+// JSON object represents a JSON node that can be either a root of the JSON tree
+// or a child item. Every node has a type that describes what is is.
+// New JSON trees are typically loaded JSON::Load or created with JSON::Parse.
+
+class JSON : public RefCountBase<JSON>, public ListNode<JSON>
+{
+protected:
+ List<JSON> Children;
+
+public:
+ JSONItemType Type; // Type of this JSON node.
+ String Name; // Name part of the {Name, Value} pair in a parent object.
+ String Value;
+ double dValue;
+
+public:
+ ~JSON();
+
+ // *** Creation of NEW JSON objects
+
+ static JSON* CreateObject() { return new JSON(JSON_Object);}
+ static JSON* CreateNull() { return new JSON(JSON_Null); }
+ static JSON* CreateArray() { return new JSON(JSON_Array); }
+ static JSON* CreateBool(bool b) { return createHelper(JSON_Bool, b ? 1.0 : 0.0); }
+ static JSON* CreateNumber(double num) { return createHelper(JSON_Number, num); }
+ static JSON* CreateString(const char *s) { return createHelper(JSON_String, 0.0, s); }
+
+ // Creates a new JSON object from parsing string.
+ // Returns null pointer and fills in *perror in case of parse error.
+ static JSON* Parse(const char* buff, const char** perror = 0);
+
+ // This version works for buffers that are not null terminated strings.
+ static JSON* ParseBuffer(const char *buff, int len, const char** perror = 0);
+
+ // Loads and parses a JSON object from a file.
+ // Returns 0 and assigns perror with error message on fail.
+ static JSON* Load(const char* path, const char** perror = 0);
+
+ // Saves a JSON object to a file.
+ bool Save(const char* path);
+
+ // *** Object Member Access
+
+ // These provide access to child items of the list.
+ bool HasItems() const { return Children.IsEmpty(); }
+ // Returns first/last child item, or null if child list is empty
+ JSON* GetFirstItem() { return (!Children.IsEmpty()) ? Children.GetFirst() : 0; }
+ JSON* GetLastItem() { return (!Children.IsEmpty()) ? Children.GetLast() : 0; }
+
+ // Counts the number of items in the object; these methods are inefficient.
+ unsigned GetItemCount() const;
+ JSON* GetItemByIndex(unsigned i);
+ JSON* GetItemByName(const char* name);
+
+ // Accessors by name
+ double GetNumberByName(const char *name, double defValue = 0.0);
+ int GetIntByName(const char *name, int defValue = 0);
+ bool GetBoolByName(const char *name, bool defValue = false);
+ String GetStringByName(const char *name, const String &defValue = "");
+
+ // Returns next item in a list of children; 0 if no more items exist.
+ JSON* GetNextItem(JSON* item) { return Children.IsNull(item->pNext) ? 0 : item->pNext; }
+ JSON* GetPrevItem(JSON* item) { return Children.IsNull(item->pPrev) ? 0 : item->pPrev; }
+
+
+ // Child item access functions
+ void AddItem(const char *string, JSON* item);
+ void AddNullItem(const char* name) { AddItem(name, CreateNull()); }
+ void AddBoolItem(const char* name, bool b) { AddItem(name, CreateBool(b)); }
+ void AddNumberItem(const char* name, double n) { AddItem(name, CreateNumber(n)); }
+ void AddStringItem(const char* name, const char* s) { AddItem(name, CreateString(s)); }
+// void ReplaceItem(unsigned index, JSON* new_item);
+// void DeleteItem(unsigned index);
+ void RemoveLast();
+
+ // *** Array Element Access
+
+ // Add new elements to the end of array.
+ void AddArrayElement(JSON *item);
+ void InsertArrayElement(int index, JSON* item);
+ void AddArrayNumber(double n) { AddArrayElement(CreateNumber(n)); }
+ void AddArrayString(const char* s) { AddArrayElement(CreateString(s)); }
+
+ // Accessed array elements; currently inefficient.
+ int GetArraySize();
+ double GetArrayNumber(int index);
+ const char* GetArrayString(int index);
+
+ JSON* Copy(); // Create a copy of this object
+
+protected:
+ JSON(JSONItemType itemType = JSON_Object);
+
+ static JSON* createHelper(JSONItemType itemType, double dval, const char* strVal = 0);
+
+ // JSON Parsing helper functions.
+ const char* parseValue(const char *buff, const char** perror);
+ const char* parseNumber(const char *num);
+ const char* parseArray(const char* value, const char** perror);
+ const char* parseObject(const char* value, const char** perror);
+ const char* parseString(const char* str, const char** perror);
+
+ char* PrintValue(int depth, bool fmt);
+ char* PrintObject(int depth, bool fmt);
+ char* PrintArray(int depth, bool fmt);
+};
+
+
+}
+
+#endif
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.cpp b/LibOVR/Src/OVR_LatencyTestImpl.cpp
new file mode 100644
index 0000000..015d9e4
--- /dev/null
+++ b/LibOVR/Src/OVR_LatencyTestImpl.cpp
@@ -0,0 +1,773 @@
+/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.cpp
+Content : Oculus Latency Tester device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+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_LatencyTestImpl.h"
+#include "Kernel/OVR_Alg.h"
+
+namespace OVR {
+
+using namespace Alg;
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Latency Tester specific packet data structures
+
+enum {
+ LatencyTester_VendorId = Oculus_VendorId,
+ LatencyTester_ProductId = 0x0101,
+};
+
+static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b)
+{
+ *r = buffer[0];
+ *g = buffer[1];
+ *b = buffer[2];
+}
+
+// Messages we handle.
+enum LatencyTestMessageType
+{
+ LatencyTestMessage_None = 0,
+ LatencyTestMessage_Samples = 1,
+ LatencyTestMessage_ColorDetected = 2,
+ LatencyTestMessage_TestStarted = 3,
+ LatencyTestMessage_Button = 4,
+ LatencyTestMessage_Unknown = 0x100,
+ LatencyTestMessage_SizeError = 0x101,
+};
+
+struct LatencyTestSample
+{
+ UByte Value[3];
+};
+
+struct LatencyTestSamples
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+
+ LatencyTestSample Samples[20];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 64)
+ {
+ return LatencyTestMessage_SizeError;
+ }
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+
+ for (UByte i = 0; i < SampleCount; i++)
+ {
+ UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]);
+ }
+
+ return LatencyTestMessage_Samples;
+ }
+};
+
+struct LatencyTestSamplesMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestSamples Samples;
+};
+
+bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestSamplesMessage));
+
+ if (size < 64)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Samples:
+ message->Type = message->Samples.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestColorDetected
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UInt16 Elapsed;
+ UByte TriggerValue[3];
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 13)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ Elapsed = DecodeUInt16(buffer + 5);
+ memcpy(TriggerValue, buffer + 7, 3);
+ memcpy(TargetValue, buffer + 10, 3);
+
+ return LatencyTestMessage_ColorDetected;
+ }
+};
+
+struct LatencyTestColorDetectedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestColorDetected ColorDetected;
+};
+
+bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestColorDetectedMessage));
+
+ if (size < 13)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_ColorDetected:
+ message->Type = message->ColorDetected.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestStarted
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+ UByte TargetValue[3];
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 8)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+ memcpy(TargetValue, buffer + 5, 3);
+
+ return LatencyTestMessage_TestStarted;
+ }
+};
+
+struct LatencyTestStartedMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestStarted TestStarted;
+};
+
+bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestStartedMessage));
+
+ if (size < 8)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_TestStarted:
+ message->Type = message->TestStarted.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestButton
+{
+ UInt16 CommandID;
+ UInt16 Timestamp;
+
+ LatencyTestMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 5)
+ return LatencyTestMessage_SizeError;
+
+ CommandID = DecodeUInt16(buffer + 1);
+ Timestamp = DecodeUInt16(buffer + 3);
+
+ return LatencyTestMessage_Button;
+ }
+};
+
+struct LatencyTestButtonMessage
+{
+ LatencyTestMessageType Type;
+ LatencyTestButton Button;
+};
+
+bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(LatencyTestButtonMessage));
+
+ if (size < 5)
+ {
+ message->Type = LatencyTestMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case LatencyTestMessage_Button:
+ message->Type = message->Button.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = LatencyTestMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None);
+}
+
+struct LatencyTestConfigurationImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestConfiguration Configuration;
+
+ LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration)
+ : Configuration(configuration)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 5;
+ Buffer[1] = UByte(Configuration.SendSamples);
+ Buffer[2] = Configuration.Threshold.R;
+ Buffer[3] = Configuration.Threshold.G;
+ Buffer[4] = Configuration.Threshold.B;
+ }
+
+ void Unpack()
+ {
+ Configuration.SendSamples = Buffer[1] != 0 ? true : false;
+ Configuration.Threshold.R = Buffer[2];
+ Configuration.Threshold.G = Buffer[3];
+ Configuration.Threshold.B = Buffer[4];
+ }
+};
+
+struct LatencyTestCalibrateImpl
+{
+ enum { PacketSize = 4 };
+ UByte Buffer[PacketSize];
+
+ Color CalibrationColor;
+
+ LatencyTestCalibrateImpl(const Color& calibrationColor)
+ : CalibrationColor(calibrationColor)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 7;
+ Buffer[1] = CalibrationColor.R;
+ Buffer[2] = CalibrationColor.G;
+ Buffer[3] = CalibrationColor.B;
+ }
+
+ void Unpack()
+ {
+ CalibrationColor.R = Buffer[1];
+ CalibrationColor.G = Buffer[2];
+ CalibrationColor.B = Buffer[3];
+ }
+};
+
+struct LatencyTestStartTestImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ Color TargetColor;
+
+ LatencyTestStartTestImpl(const Color& targetColor)
+ : TargetColor(targetColor)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ UInt16 commandID = 1;
+
+ Buffer[0] = 8;
+ EncodeUInt16(Buffer+1, commandID);
+ Buffer[3] = TargetColor.R;
+ Buffer[4] = TargetColor.G;
+ Buffer[5] = TargetColor.B;
+ }
+
+ void Unpack()
+ {
+// UInt16 commandID = DecodeUInt16(Buffer+1);
+ TargetColor.R = Buffer[3];
+ TargetColor.G = Buffer[4];
+ TargetColor.B = Buffer[5];
+ }
+};
+
+struct LatencyTestDisplayImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ OVR::LatencyTestDisplay Display;
+
+ LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display)
+ : Display(display)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 9;
+ Buffer[1] = Display.Mode;
+ EncodeUInt32(Buffer+2, Display.Value);
+ }
+
+ void Unpack()
+ {
+ Display.Mode = Buffer[1];
+ Display.Value = DecodeUInt32(Buffer+2);
+ }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceFactory
+
+LatencyTestDeviceFactory &LatencyTestDeviceFactory::GetInstance()
+{
+ static LatencyTestDeviceFactory instance;
+ return instance;
+}
+
+void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class LatencyTestEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const LatencyTestEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+ OVR_UNUSED(device);
+
+ LatencyTestDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+ }
+ };
+
+ LatencyTestEnumerator latencyTestEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator);
+}
+
+bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId));
+}
+
+bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr,
+ const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ LatencyTestDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDeviceCreateDesc
+
+DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance()
+{
+ return new LatencyTestDeviceImpl(this);
+}
+
+bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_LatencyTester) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_LatencyTester;
+ info->ProductName = HIDDesc.Product;
+ info->Manufacturer = HIDDesc.Manufacturer;
+ info->Version = HIDDesc.VersionNumber;
+
+ if (info->InfoClassType == Device_LatencyTester)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ sinfo->SerialNumber = HIDDesc.SerialNumber;
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTestDevice
+
+LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::LatencyTestDevice>(createDesc, 0)
+{
+}
+
+LatencyTestDeviceImpl::~LatencyTestDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+// Internal creation APIs.
+bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::LatencyTestDevice>::Initialize(parent))
+ {
+ LogText("OVR::LatencyTestDevice initialized.\n");
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTestDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::LatencyTestDevice>::Shutdown();
+
+ LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+ LatencyTestSamplesMessage message;
+ if (DecodeLatencyTestSamplesMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestSamplesMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestColorDetectedMessage message;
+ if (DecodeLatencyTestColorDetectedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestColorDetectedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestStartedMessage message;
+ if (DecodeLatencyTestStartedMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestStartedMessage(&message);
+ }
+ }
+
+ if (!processed)
+ {
+ LatencyTestButtonMessage message;
+ if (DecodeLatencyTestButtonMessage(&message, pData, length))
+ {
+ processed = true;
+ onLatencyTestButtonMessage(&message);
+ }
+ }
+}
+
+bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId())
+ {
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setConfiguration,
+ &result,
+ configuration))
+ {
+ return false;
+ }
+ }
+ else
+ return setConfiguration(configuration);
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration)
+{
+ LatencyTestConfigurationImpl ltc(configuration);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ bool result = false;
+
+ ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue();
+ if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration))
+ return false;
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration)
+{
+ LatencyTestConfigurationImpl ltc(*configuration);
+ if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize))
+ {
+ ltc.Unpack();
+ *configuration = ltc.Configuration;
+ return true;
+ }
+
+ return false;
+}
+
+bool LatencyTestDeviceImpl::SetCalibrate(const Color& calibrationColor, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrationColor);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setCalibrate,
+ &result,
+ calibrationColor))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setCalibrate(const Color& calibrationColor)
+{
+ LatencyTestCalibrateImpl ltc(calibrationColor);
+ return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::SetStartTest(const Color& targetColor, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, targetColor);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setStartTest,
+ &result,
+ targetColor))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setStartTest(const Color& targetColor)
+{
+ LatencyTestStartTestImpl ltst(targetColor);
+ return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize);
+}
+
+bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag)
+{
+ bool result = false;
+ ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display);
+ }
+
+ if (!queue->PushCallAndWaitResult( this,
+ &LatencyTestDeviceImpl::setDisplay,
+ &result,
+ display))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display)
+{
+ LatencyTestDisplayImpl ltd(display);
+ return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize);
+}
+
+void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message)
+{
+
+ if (message->Type != LatencyTestMessage_Samples)
+ return;
+
+ LatencyTestSamples& s = message->Samples;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageLatencyTestSamples samples(this);
+ for (UByte i = 0; i < s.SampleCount; i++)
+ {
+ samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2]));
+ }
+
+ HandlerRef.Call(samples);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_ColorDetected)
+ return;
+
+ LatencyTestColorDetected& s = message->ColorDetected;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageLatencyTestColorDetected detected(this);
+ detected.Elapsed = s.Elapsed;
+ detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]);
+ detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]);
+
+ HandlerRef.Call(detected);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message)
+{
+ if (message->Type != LatencyTestMessage_TestStarted)
+ return;
+
+ LatencyTestStarted& ts = message->TestStarted;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageLatencyTestStarted started(this);
+ started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]);
+
+ HandlerRef.Call(started);
+ }
+}
+
+void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message)
+{
+ if (message->Type != LatencyTestMessage_Button)
+ return;
+
+// LatencyTestButton& s = message->Button;
+
+ // Call OnMessage() within a lock to avoid conflicts with handlers.
+ Lock::Locker scopeLock(HandlerRef.GetLock());
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageLatencyTestButton button(this);
+
+ HandlerRef.Call(button);
+ }
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_LatencyTestImpl.h b/LibOVR/Src/OVR_LatencyTestImpl.h
new file mode 100644
index 0000000..34faec2
--- /dev/null
+++ b/LibOVR/Src/OVR_LatencyTestImpl.h
@@ -0,0 +1,144 @@
+/************************************************************************************
+
+Filename : OVR_LatencyTestImpl.h
+Content : Latency Tester specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_LatencyTestImpl_h
+#define OVR_LatencyTestImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR {
+
+struct LatencyTestSamplesMessage;
+struct LatencyTestButtonMessage;
+struct LatencyTestStartedMessage;
+struct LatencyTestColorDetectedMessage;
+
+//-------------------------------------------------------------------------------------
+// LatencyTestDeviceFactory enumerates Oculus Latency Tester devices.
+class LatencyTestDeviceFactory : public DeviceFactory
+{
+public:
+ static LatencyTestDeviceFactory &GetInstance();
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Latency Tester device and supports creating its instance.
+class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new LatencyTestDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory))
+ {
+ const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::LatencyTestDeviceImpl
+
+// Oculus Latency Tester interface.
+
+class LatencyTestDeviceImpl : public HIDDeviceImpl<OVR::LatencyTestDevice>
+{
+public:
+ LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc);
+ ~LatencyTestDeviceImpl();
+
+ // DeviceCommon interface.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // DeviceManagerThread::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+
+ // LatencyTesterDevice interface
+ virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false);
+ virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration);
+
+ virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false);
+
+ virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false);
+ virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false);
+
+protected:
+ bool openDevice(const char** errorFormatString);
+ void closeDevice();
+ void closeDeviceOnIOError();
+
+ bool initializeRead();
+ bool processReadResult();
+
+ bool setConfiguration(const OVR::LatencyTestConfiguration& configuration);
+ bool getConfiguration(OVR::LatencyTestConfiguration* configuration);
+ bool setCalibrate(const Color& calibrationColor);
+ bool setStartTest(const Color& targetColor);
+ bool setDisplay(const OVR::LatencyTestDisplay& display);
+
+ // Called for decoded messages
+ void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message);
+ void onLatencyTestButtonMessage(LatencyTestButtonMessage* message);
+ void onLatencyTestStartedMessage(LatencyTestStartedMessage* message);
+ void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message);
+
+};
+
+} // namespace OVR
+
+#endif // OVR_LatencyTestImpl_h
diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.cpp b/LibOVR/Src/OVR_Linux_DeviceManager.cpp
new file mode 100644
index 0000000..f1c4278
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_DeviceManager.cpp
@@ -0,0 +1,331 @@
+/************************************************************************************
+
+Filename : OVR_Linux_DeviceManager.h
+Content : Linux implementation of DeviceManager.
+Created :
+Authors :
+
+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_Linux_DeviceManager.h"
+
+// Sensor & HMD Factories
+#include "OVR_LatencyTestImpl.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_Linux_HIDDevice.h"
+#include "OVR_Linux_HMDDevice.h"
+
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Std.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace Linux {
+
+
+//-------------------------------------------------------------------------------------
+// **** Linux::DeviceManager
+
+DeviceManager::DeviceManager()
+{
+}
+
+DeviceManager::~DeviceManager()
+{
+}
+
+bool DeviceManager::Initialize(DeviceBase*)
+{
+ if (!DeviceManagerImpl::Initialize(0))
+ return false;
+
+ pThread = *new DeviceManagerThread();
+ if (!pThread || !pThread->Start())
+ return false;
+
+ // Wait for the thread to be fully up and running.
+ pThread->StartupEvent.Wait();
+
+ // Do this now that we know the thread's run loop.
+ HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
+
+ pCreateDesc->pDevice = this;
+ LogText("OVR::DeviceManager - initialized.\n");
+ return true;
+}
+
+void DeviceManager::Shutdown()
+{
+ LogText("OVR::DeviceManager - shutting down.\n");
+
+ // Set Manager shutdown marker variable; this prevents
+ // any existing DeviceHandle objects from accessing device.
+ pCreateDesc->pLock->pManager = 0;
+
+ // Push for thread shutdown *WITH NO WAIT*.
+ // This will have the following effect:
+ // - Exit command will get enqueued, which will be executed later on the thread itself.
+ // - Beyond this point, this DeviceManager object may be deleted by our caller.
+ // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
+ // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
+ // after pManager is null.
+ // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
+ // reference to the thread object.
+ pThread->PushExitCommand(false);
+ pThread.Clear();
+
+ DeviceManagerImpl::Shutdown();
+}
+
+ThreadCommandQueue* DeviceManager::GetThreadQueue()
+{
+ return pThread;
+}
+
+ThreadId DeviceManager::GetThreadId() const
+{
+ return pThread->GetThreadId();
+}
+
+bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Manager) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Manager;
+ info->Version = 0;
+ info->ProductName = "DeviceManager";
+ info->Manufacturer = "Oculus VR, Inc.";
+ return true;
+}
+
+DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
+{
+ // TBD: Can this be avoided in the future, once proper device notification is in place?
+ pThread->PushCall((DeviceManagerImpl*)this,
+ &DeviceManager::EnumerateAllFactoryDevices, true);
+
+ return DeviceManagerImpl::EnumerateDevicesEx(args);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** DeviceManager Thread
+
+DeviceManagerThread::DeviceManagerThread()
+ : Thread(ThreadStackSize)
+{
+ int result = pipe(CommandFd);
+ OVR_ASSERT(!result);
+ OVR_UNUSED(result);
+
+ AddSelectFd(NULL, CommandFd[0]);
+}
+
+DeviceManagerThread::~DeviceManagerThread()
+{
+ if (CommandFd[0])
+ {
+ RemoveSelectFd(NULL, CommandFd[0]);
+ close(CommandFd[0]);
+ close(CommandFd[1]);
+ }
+}
+
+bool DeviceManagerThread::AddSelectFd(Notifier* notify, int fd)
+{
+ struct pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = POLLIN|POLLHUP|POLLERR;
+ pfd.revents = 0;
+
+ FdNotifiers.PushBack(notify);
+ PollFds.PushBack(pfd);
+
+ OVR_ASSERT(FdNotifiers.GetSize() == PollFds.GetSize());
+ return true;
+}
+
+bool DeviceManagerThread::RemoveSelectFd(Notifier* notify, int fd)
+{
+ // [0] is reserved for thread commands with notify of null, but we still
+ // can use this function to remove it.
+ for (UPInt i = 0; i < FdNotifiers.GetSize(); i++)
+ {
+ if ((FdNotifiers[i] == notify) && (PollFds[i].fd == fd))
+ {
+ FdNotifiers.RemoveAt(i);
+ PollFds.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+int DeviceManagerThread::Run()
+{
+ ThreadCommand::PopBuffer command;
+
+ SetThreadName("OVR::DeviceManagerThread");
+ LogText("OVR::DeviceManagerThread - running (ThreadId=%p).\n", GetThreadId());
+
+ // Signal to the parent thread that initialization has finished.
+ StartupEvent.SetEvent();
+
+ while(!IsExiting())
+ {
+ // PopCommand will reset event on empty queue.
+ if (PopCommand(&command))
+ {
+ command.Execute();
+ }
+ else
+ {
+ bool commands = 0;
+ do
+ {
+ int waitMs = -1;
+
+ // If devices have time-dependent logic registered, get the longest wait
+ // allowed based on current ticks.
+ if (!TicksNotifiers.IsEmpty())
+ {
+ double timeSeconds = Timer::GetSeconds();
+ unsigned waitAllowed;
+
+ for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
+ {
+ waitAllowed = (unsigned)(TicksNotifiers[j]->OnTicks(timeSeconds) * Timer::MsPerSecond);
+ if (waitAllowed < (unsigned)waitMs)
+ waitMs = waitAllowed;
+ }
+ }
+
+ // wait until there is data available on one of the devices or the timeout expires
+ int n = poll(&PollFds[0], PollFds.GetSize(), waitMs);
+
+ if (n > 0)
+ {
+ // Iterate backwards through the list so the ordering will not be
+ // affected if the called object gets removed during the callback
+ // Also, the HID data streams are located toward the back of the list
+ // and servicing them first will allow a disconnect to be handled
+ // and cleaned directly at the device first instead of the general HID monitor
+ for (int i=PollFds.GetSize()-1; i>=0; i--)
+ {
+ if (PollFds[i].revents & POLLERR)
+ {
+ OVR_DEBUG_LOG(("poll: error on [%d]: %d", i, PollFds[i].fd));
+ }
+ else if (PollFds[i].revents & POLLIN)
+ {
+ if (FdNotifiers[i])
+ FdNotifiers[i]->OnEvent(i, PollFds[i].fd);
+ else if (i == 0) // command
+ {
+ char dummy[128];
+ read(PollFds[i].fd, dummy, 128);
+ commands = 1;
+ }
+ }
+
+ if (PollFds[i].revents & POLLHUP)
+ PollFds[i].events = 0;
+
+ if (PollFds[i].revents != 0)
+ {
+ n--;
+ if (n == 0)
+ break;
+ }
+ }
+ }
+ } while (PollFds.GetSize() > 0 && !commands);
+ }
+ }
+
+ LogText("OVR::DeviceManagerThread - exiting (ThreadId=%p).\n", GetThreadId());
+ return 0;
+}
+
+bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
+{
+ TicksNotifiers.PushBack(notify);
+ return true;
+}
+
+bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
+{
+ for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
+ {
+ if (TicksNotifiers[i] == notify)
+ {
+ TicksNotifiers.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace Linux
+
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+
+// Creates a new DeviceManager and initializes OVR.
+DeviceManager* DeviceManager::Create()
+{
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::DeviceManager> manager = *new Linux::DeviceManager;
+
+ if (manager)
+ {
+ if (manager->Initialize(0))
+ {
+ manager->AddFactory(&LatencyTestDeviceFactory::GetInstance());
+ manager->AddFactory(&SensorDeviceFactory::GetInstance());
+ manager->AddFactory(&Linux::HMDDeviceFactory::GetInstance());
+
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+
+ }
+
+ return manager.GetPtr();
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.h b/LibOVR/Src/OVR_Linux_DeviceManager.h
new file mode 100644
index 0000000..6532103
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_DeviceManager.h
@@ -0,0 +1,122 @@
+/************************************************************************************
+
+Filename : OVR_Linux_DeviceManager.h
+Content : Linux-specific DeviceManager header.
+Created :
+Authors :
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Linux_DeviceManager_h
+#define OVR_Linux_DeviceManager_h
+
+#include "OVR_DeviceImpl.h"
+
+#include <unistd.h>
+#include <sys/poll.h>
+
+
+namespace OVR { namespace Linux {
+
+class DeviceManagerThread;
+
+//-------------------------------------------------------------------------------------
+// ***** Linux DeviceManager
+
+class DeviceManager : public DeviceManagerImpl
+{
+public:
+ DeviceManager();
+ ~DeviceManager();
+
+ // Initialize/Shutdowncreate and shutdown manger thread.
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual ThreadCommandQueue* GetThreadQueue();
+ virtual ThreadId GetThreadId() const;
+
+ virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ Ptr<DeviceManagerThread> pThread;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** Device Manager Background Thread
+
+class DeviceManagerThread : public Thread, public ThreadCommandQueue
+{
+ friend class DeviceManager;
+ enum { ThreadStackSize = 64 * 1024 };
+public:
+ DeviceManagerThread();
+ ~DeviceManagerThread();
+
+ virtual int Run();
+
+ // ThreadCommandQueue notifications for CommandEvent handling.
+ virtual void OnPushNonEmpty_Locked() { write(CommandFd[1], this, 1); }
+ virtual void OnPopEmpty_Locked() { }
+
+ class Notifier
+ {
+ public:
+ // Called when I/O is received
+ virtual void OnEvent(int i, int fd) = 0;
+
+ // Called when timing ticks are updated.
+ // Returns the largest number of seconds this function can
+ // wait till next call.
+ virtual double OnTicks(double tickSeconds)
+ {
+ OVR_UNUSED1(tickSeconds);
+ return 1000.0;
+ }
+ };
+
+ // Add I/O notifier
+ bool AddSelectFd(Notifier* notify, int fd);
+ bool RemoveSelectFd(Notifier* notify, int fd);
+
+ // Add notifier that will be called at regular intervals.
+ bool AddTicksNotifier(Notifier* notify);
+ bool RemoveTicksNotifier(Notifier* notify);
+
+private:
+
+ bool threadInitialized() { return CommandFd[0] != 0; }
+
+ // pipe used to signal commands
+ int CommandFd[2];
+
+ Array<struct pollfd> PollFds;
+ Array<Notifier*> FdNotifiers;
+
+ Event StartupEvent;
+
+ // Ticks notifiers - used for time-dependent events such as keep-alive.
+ Array<Notifier*> TicksNotifiers;
+};
+
+}} // namespace Linux::OVR
+
+#endif // OVR_Linux_DeviceManager_h
diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.cpp b/LibOVR/Src/OVR_Linux_HIDDevice.cpp
new file mode 100644
index 0000000..133e5c3
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HIDDevice.cpp
@@ -0,0 +1,819 @@
+/************************************************************************************
+Filename : OVR_Linux_HIDDevice.cpp
+Content : Linux HID device implementation.
+Created : February 26, 2013
+Authors : Lee Cooper
+
+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_Linux_HIDDevice.h"
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <linux/hidraw.h>
+#include "OVR_HIDDeviceImpl.h"
+
+namespace OVR { namespace Linux {
+
+static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
+
+//-------------------------------------------------------------------------------------
+// **** Linux::DeviceManager
+//-----------------------------------------------------------------------------
+HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager)
+{
+ UdevInstance = NULL;
+ HIDMonitor = NULL;
+ HIDMonHandle = -1;
+}
+
+//-----------------------------------------------------------------------------
+HIDDeviceManager::~HIDDeviceManager()
+{
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::initializeManager()
+{
+ if (HIDMonitor)
+ {
+ return true;
+ }
+
+ // Create a udev_monitor handle to watch for device changes (hot-plug detection)
+ HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev");
+ if (HIDMonitor == NULL)
+ {
+ return false;
+ }
+
+ udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only
+
+ int err = udev_monitor_enable_receiving(HIDMonitor);
+ if (err)
+ {
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ // Get the file descriptor (fd) for the monitor.
+ HIDMonHandle = udev_monitor_get_fd(HIDMonitor);
+ if (HIDMonHandle < 0)
+ {
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ // This file handle will be polled along-side with the device hid handles for changes
+ // Add the handle to the polling list
+ if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle))
+ {
+ close(HIDMonHandle);
+ HIDMonHandle = -1;
+
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::Initialize()
+{
+ // Get a udev library handle. This handle must stay active during the
+ // duration the lifetime of device monitoring handles
+ UdevInstance = udev_new();
+ if (!UdevInstance)
+ return false;
+
+ return initializeManager();
+}
+
+//-----------------------------------------------------------------------------
+void HIDDeviceManager::Shutdown()
+{
+ OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'."));
+
+ if (HIDMonitor)
+ {
+ DevManager->pThread->RemoveSelectFd(this, HIDMonHandle);
+ close(HIDMonHandle);
+ HIDMonHandle = -1;
+
+ udev_monitor_unref(HIDMonitor);
+ HIDMonitor = NULL;
+ }
+
+ udev_unref(UdevInstance); // release the library
+
+ LogText("OVR::Linux::HIDDeviceManager - shutting down.\n");
+}
+
+//-------------------------------------------------------------------------------
+bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device)
+{
+ NotificationDevices.PushBack(device);
+ return true;
+}
+
+//-------------------------------------------------------------------------------
+bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device)
+{
+ for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
+ {
+ if (NotificationDevices[i] == device)
+ {
+ NotificationDevices.RemoveAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getIntProperty(udev_device* device,
+ const char* propertyName,
+ SInt32* pResult)
+{
+ const char* str = udev_device_get_sysattr_value(device, propertyName);
+ if (str)
+ {
+ *pResult = strtol(str, NULL, 16);
+ return true;
+ }
+ else
+ {
+ *pResult = 0;
+ return true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc)
+{
+ SInt32 result;
+ if (getIntProperty(device, "idVendor", &result))
+ pDevDesc->VendorId = result;
+ else
+ return false;
+
+ if (getIntProperty(device, "idProduct", &result))
+ pDevDesc->ProductId = result;
+ else
+ return false;
+
+ if (getIntProperty(device, "bcdDevice", &result))
+ pDevDesc->VersionNumber = result;
+ else
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getStringProperty(udev_device* device,
+ const char* propertyName,
+ OVR::String* pResult)
+{
+ // Get the attribute in UTF8
+ const char* str = udev_device_get_sysattr_value(device, propertyName);
+ if (str)
+ { // Copy the string into the return value
+ *pResult = String(str);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
+{
+
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+ // Get a list of hid devices
+ udev_enumerate* devices = udev_enumerate_new(UdevInstance);
+ udev_enumerate_add_match_subsystem(devices, "hidraw");
+ udev_enumerate_scan_devices(devices);
+
+ udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
+
+ // Search each device for the matching vid/pid
+ while (entry != NULL)
+ {
+ // Get the device file name
+ const char* sysfs_path = udev_list_entry_get_name(entry);
+ udev_device* hid; // The device's HID udev node.
+ hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
+ const char* dev_path = udev_device_get_devnode(hid);
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (hid)
+ {
+ HIDDeviceDesc devDesc;
+
+ // Check the VID/PID for a match
+ if (dev_path &&
+ initVendorProductVersion(hid, &devDesc) &&
+ enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId))
+ {
+ devDesc.Path = dev_path;
+ getFullDesc(hid, &devDesc);
+
+ // Look for the device to check if it is already opened.
+ Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc, true);
+ // if device exists and it is opened then most likely the device open()
+ // will fail; therefore, we just set Enumerated to 'true' and continue.
+ if (existingDevice && existingDevice->pDevice)
+ {
+ existingDevice->Enumerated = true;
+ }
+ else
+ { // open the device temporarily for startup communication
+ int device_handle = open(dev_path, O_RDWR);
+ if (device_handle >= 0)
+ {
+ // Construct minimal device that the visitor callback can get feature reports from
+ Linux::HIDDevice device(this, device_handle);
+ enumVisitor->Visit(device, devDesc);
+
+ close(device_handle); // close the file handle
+ }
+ }
+ }
+
+ udev_device_unref(hid);
+ entry = udev_list_entry_get_next(entry);
+ }
+ }
+
+ // Free the enumerator and udev objects
+ udev_enumerate_unref(devices);
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
+{
+ Ptr<Linux::HIDDevice> device = *new Linux::HIDDevice(this);
+
+ if (device->HIDInitialize(path))
+ {
+ device->AddRef();
+ return device;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc)
+{
+
+ if (!initVendorProductVersion(device, desc))
+ {
+ return false;
+ }
+
+ if (!getStringProperty(device, "serial", &(desc->SerialNumber)))
+ {
+ return false;
+ }
+
+ getStringProperty(device, "manufacturer", &(desc->Manufacturer));
+ getStringProperty(device, "product", &(desc->Product));
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc)
+{
+ if (!initializeManager())
+ {
+ return false;
+ }
+
+ // Search for the udev device from the given pathname so we can
+ // have a handle to query device properties
+
+ udev_enumerate* devices = udev_enumerate_new(UdevInstance);
+ udev_enumerate_add_match_subsystem(devices, "hidraw");
+ udev_enumerate_scan_devices(devices);
+
+ udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
+
+ bool success = false;
+ // Search for the device with the matching path
+ while (entry != NULL)
+ {
+ // Get the device file name
+ const char* sysfs_path = udev_list_entry_get_name(entry);
+ udev_device* hid; // The device's HID udev node.
+ hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
+ const char* path = udev_device_get_devnode(hid);
+
+ if (OVR_strcmp(dev_path, path) == 0)
+ { // Found the device so lets collect the device descriptor
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (hid)
+ {
+ desc->Path = dev_path;
+ success = getFullDesc(hid, desc);
+ }
+
+ }
+
+ udev_device_unref(hid);
+ entry = udev_list_entry_get_next(entry);
+ }
+
+ // Free the enumerator
+ udev_enumerate_unref(devices);
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+void HIDDeviceManager::OnEvent(int i, int fd)
+{
+ OVR_UNUSED(i);
+ OVR_UNUSED(fd);
+
+ // There is a device status change
+ udev_device* hid = udev_monitor_receive_device(HIDMonitor);
+ if (hid)
+ {
+ const char* dev_path = udev_device_get_devnode(hid);
+ const char* action = udev_device_get_action(hid);
+
+ HIDDeviceDesc device_info;
+ device_info.Path = dev_path;
+
+ MessageType notify_type;
+ if (OVR_strcmp(action, "add") == 0)
+ {
+ notify_type = Message_DeviceAdded;
+
+ // Retrieve the device info. This can only be done on a connected
+ // device and is invalid for a disconnected device
+
+ // Get the USB device
+ hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
+ if (!hid)
+ {
+ return;
+ }
+
+ getFullDesc(hid, &device_info);
+ }
+ else if (OVR_strcmp(action, "remove") == 0)
+ {
+ notify_type = Message_DeviceRemoved;
+ }
+ else
+ {
+ return;
+ }
+
+ bool error = false;
+ bool deviceFound = false;
+ for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
+ {
+ if (NotificationDevices[i] &&
+ NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error))
+ {
+ // The notification was for an existing device
+ deviceFound = true;
+ break;
+ }
+ }
+
+ if (notify_type == Message_DeviceAdded && !deviceFound)
+ {
+ DevManager->DetectHIDDevice(device_info);
+ }
+
+ udev_device_unref(hid);
+ }
+}
+
+//=============================================================================
+// Linux::HIDDevice
+//=============================================================================
+HIDDevice::HIDDevice(HIDDeviceManager* manager)
+ : InMinimalMode(false), HIDManager(manager)
+{
+ DeviceHandle = -1;
+}
+
+//-----------------------------------------------------------------------------
+// This is a minimal constructor used during enumeration for us to pass
+// a HIDDevice to the visit function (so that it can query feature reports).
+HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle)
+: InMinimalMode(true), HIDManager(manager), DeviceHandle(device_handle)
+{
+}
+
+//-----------------------------------------------------------------------------
+HIDDevice::~HIDDevice()
+{
+ if (!InMinimalMode)
+ {
+ HIDShutdown();
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::HIDInitialize(const String& path)
+{
+ const char* hid_path = path.ToCStr();
+ if (!openDevice(hid_path))
+ {
+ LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path);
+ return false;
+ }
+
+ HIDManager->DevManager->pThread->AddTicksNotifier(this);
+ HIDManager->AddNotificationDevice(this);
+
+ LogText("OVR::Linux::HIDDevice - Opened '%s'\n"
+ " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
+ DevDesc.Path.ToCStr(),
+ DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
+ DevDesc.SerialNumber.ToCStr());
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::initInfo()
+{
+ // Device must have been successfully opened.
+ OVR_ASSERT(DeviceHandle >= 0);
+
+ int desc_size = 0;
+ hidraw_report_descriptor rpt_desc;
+ memset(&rpt_desc, 0, sizeof(rpt_desc));
+
+ // get report descriptor size
+ int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size);
+ if (r < 0)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get report descriptor size."));
+ return false;
+ }
+
+ // Get the report descriptor
+ rpt_desc.size = desc_size;
+ r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc);
+ if (r < 0)
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get report descriptor."));
+ return false;
+ }
+
+ /*
+ // Get report lengths.
+ SInt32 bufferLength;
+ bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ InputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ OutputReportBufferLength = (UInt16) bufferLength;
+
+ getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
+ OVR_ASSERT(getResult);
+ FeatureReportBufferLength = (UInt16) bufferLength;
+
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ // Get device desc.
+ if (!HIDManager->getFullDesc(Device, &DevDesc))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
+ return false;
+ }
+
+ return true;
+ */
+
+ // Get report lengths.
+// TODO: hard-coded for now. Need to interpret these values from the report descriptor
+ InputReportBufferLength = 62;
+ OutputReportBufferLength = 0;
+ FeatureReportBufferLength = 69;
+
+ if (ReadBufferSize < InputReportBufferLength)
+ {
+ OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::openDevice(const char* device_path)
+{
+ // First fill out the device descriptor
+ if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc))
+ {
+ return false;
+ }
+
+ // Now open the device
+ DeviceHandle = open(device_path, O_RDWR);
+ if (DeviceHandle < 0)
+ {
+ OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno));
+ DeviceHandle = -1;
+ return false;
+ }
+
+ // fill out some values from the feature report descriptor
+ if (!initInfo())
+ {
+ OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info."));
+
+ close(DeviceHandle);
+ DeviceHandle = -1;
+ return false;
+ }
+
+ // Add the device to the polling list
+ if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle))
+ {
+ OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice."));
+
+ close(DeviceHandle);
+ DeviceHandle = -1;
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::HIDShutdown()
+{
+
+ HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
+ HIDManager->RemoveNotificationDevice(this);
+
+ if (DeviceHandle >= 0) // Device may already have been closed if unplugged.
+ {
+ closeDevice(false);
+ }
+
+ LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::closeDevice(bool wasUnplugged)
+{
+ OVR_UNUSED(wasUnplugged);
+ OVR_ASSERT(DeviceHandle >= 0);
+
+
+ HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle);
+
+ close(DeviceHandle); // close the file handle
+ DeviceHandle = -1;
+
+ LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::closeDeviceOnIOError()
+{
+ LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr());
+ closeDevice(false);
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
+{
+
+ if (DeviceHandle < 0)
+ return false;
+
+ UByte reportID = data[0];
+
+ if (reportID == 0)
+ {
+ // Not using reports so remove from data packet.
+ data++;
+ length--;
+ }
+
+ int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data);
+ return (r >= 0);
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
+{
+ if (DeviceHandle < 0)
+ return false;
+
+ int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data);
+ return (r >= 0);
+}
+
+//-----------------------------------------------------------------------------
+double HIDDevice::OnTicks(double tickSeconds)
+{
+ if (Handler)
+ {
+ return Handler->OnTicks(tickSeconds);
+ }
+
+ return DeviceManagerThread::Notifier::OnTicks(tickSeconds);
+}
+
+//-----------------------------------------------------------------------------
+void HIDDevice::OnEvent(int i, int fd)
+{
+ OVR_UNUSED(i);
+ // We have data to read from the device
+ int bytes = read(fd, ReadBuffer, ReadBufferSize);
+ if (bytes >= 0)
+ {
+// TODO: I need to handle partial messages and package reconstruction
+ if (Handler)
+ {
+ Handler->OnInputReport(ReadBuffer, bytes);
+ }
+ }
+ else
+ { // Close the device on read error.
+ closeDeviceOnIOError();
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool HIDDevice::OnDeviceNotification(MessageType messageType,
+ HIDDeviceDesc* device_info,
+ bool* error)
+{
+ const char* device_path = device_info->Path.ToCStr();
+
+ if (messageType == Message_DeviceAdded && DeviceHandle < 0)
+ {
+ // Is this the correct device?
+ if (!(device_info->VendorId == DevDesc.VendorId
+ && device_info->ProductId == DevDesc.ProductId
+ && device_info->SerialNumber == DevDesc.SerialNumber))
+ {
+ return false;
+ }
+
+ // A closed device has been re-added. Try to reopen.
+ if (!openDevice(device_path))
+ {
+ LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n",
+ device_path);
+ *error = true;
+ return true;
+ }
+
+ LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path);
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded);
+ }
+ }
+ else if (messageType == Message_DeviceRemoved)
+ {
+ // Is this the correct device?
+ // For disconnected device, the device description will be invalid so
+ // checking the path is the only way to match them
+ if (DevDesc.Path.CompareNoCase(device_path) != 0)
+ {
+ return false;
+ }
+
+ if (DeviceHandle >= 0)
+ {
+ closeDevice(true);
+ }
+
+ if (Handler)
+ {
+ Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(0);
+ }
+
+ *error = false;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::HIDDeviceManager> manager = *new Linux::HIDDeviceManager(devManager);
+
+ if (manager)
+ {
+ if (manager->Initialize())
+ {
+ manager->AddRef();
+ }
+ else
+ {
+ manager.Clear();
+ }
+ }
+
+ return manager.GetPtr();
+}
+
+} // namespace Linux
+
+//-------------------------------------------------------------------------------------
+// ***** Creation
+
+// Creates a new HIDDeviceManager and initializes OVR.
+HIDDeviceManager* HIDDeviceManager::Create(Ptr<OVR::DeviceManager>& deviceManager)
+{
+
+ if (!System::IsInitialized())
+ {
+ // Use custom message, since Log is not yet installed.
+ OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
+ LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
+ return 0;
+ }
+
+ Ptr<Linux::DeviceManager> deviceManagerLinux = *new Linux::DeviceManager;
+
+ if (!deviceManagerLinux)
+ {
+ return NULL;
+ }
+
+ if (!deviceManagerLinux->Initialize(NULL))
+ {
+ return NULL;
+ }
+
+ deviceManager = deviceManagerLinux;
+
+ return deviceManagerLinux->GetHIDDeviceManager();
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.h b/LibOVR/Src/OVR_Linux_HIDDevice.h
new file mode 100644
index 0000000..52f2d69
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HIDDevice.h
@@ -0,0 +1,135 @@
+/************************************************************************************
+Filename : OVR_Linux_HIDDevice.h
+Content : Linux HID device implementation.
+Created : June 13, 2013
+Authors : Brant Lewis
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_LINUX_HIDDevice_h
+#define OVR_LINUX_HIDDevice_h
+
+#include "OVR_HIDDevice.h"
+#include "OVR_Linux_DeviceManager.h"
+#include <libudev.h>
+
+namespace OVR { namespace Linux {
+
+class HIDDeviceManager;
+
+//-------------------------------------------------------------------------------------
+// ***** Linux HIDDevice
+
+class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
+{
+private:
+ friend class HIDDeviceManager;
+
+public:
+ HIDDevice(HIDDeviceManager* manager);
+
+ // This is a minimal constructor used during enumeration for us to pass
+ // a HIDDevice to the visit function (so that it can query feature reports).
+ HIDDevice(HIDDeviceManager* manager, int device_handle);
+
+ virtual ~HIDDevice();
+
+ bool HIDInitialize(const String& path);
+ void HIDShutdown();
+
+ virtual bool SetFeatureReport(UByte* data, UInt32 length);
+ virtual bool GetFeatureReport(UByte* data, UInt32 length);
+
+ // DeviceManagerThread::Notifier
+ void OnEvent(int i, int fd);
+ double OnTicks(double tickSeconds);
+
+ bool OnDeviceNotification(MessageType messageType,
+ HIDDeviceDesc* device_info,
+ bool* error);
+
+private:
+ bool initInfo();
+ bool openDevice(const char* dev_path);
+ void closeDevice(bool wasUnplugged);
+ void closeDeviceOnIOError();
+ bool setupDevicePluggedInNotification();
+
+ bool InMinimalMode;
+ HIDDeviceManager* HIDManager;
+ int DeviceHandle; // file handle to the device
+ HIDDeviceDesc DevDesc;
+
+ enum { ReadBufferSize = 96 };
+ UByte ReadBuffer[ReadBufferSize];
+
+ UInt16 InputReportBufferLength;
+ UInt16 OutputReportBufferLength;
+ UInt16 FeatureReportBufferLength;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Linux HIDDeviceManager
+
+class HIDDeviceManager : public OVR::HIDDeviceManager, public DeviceManagerThread::Notifier
+{
+ friend class HIDDevice;
+
+public:
+ HIDDeviceManager(Linux::DeviceManager* Manager);
+ virtual ~HIDDeviceManager();
+
+ virtual bool Initialize();
+ virtual void Shutdown();
+
+ virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
+ virtual OVR::HIDDevice* Open(const String& path);
+
+ static HIDDeviceManager* CreateInternal(DeviceManager* manager);
+
+ void OnEvent(int i, int fd);
+
+private:
+ bool initializeManager();
+ bool initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc);
+ bool getPath(udev_device* device, String* pPath);
+ bool getIntProperty(udev_device* device, const char* key, int32_t* pResult);
+ bool getStringProperty(udev_device* device,
+ const char* propertyName,
+ OVR::String* pResult);
+ bool getFullDesc(udev_device* device, HIDDeviceDesc* desc);
+ bool GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc);
+
+ bool AddNotificationDevice(HIDDevice* device);
+ bool RemoveNotificationDevice(HIDDevice* device);
+
+ DeviceManager* DevManager;
+
+ udev* UdevInstance; // a handle to the udev library instance
+ udev_monitor* HIDMonitor;
+ int HIDMonHandle; // the udev_monitor file handle
+
+ Array<HIDDevice*> NotificationDevices;
+};
+
+}} // namespace OVR::Linux
+
+#endif // OVR_Linux_HIDDevice_h
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.cpp b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
new file mode 100644
index 0000000..98143d3
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
@@ -0,0 +1,291 @@
+/************************************************************************************
+
+Filename : OVR_Linux_HMDDevice.h
+Content : Linux HMDDevice implementation
+Created : June 17, 2013
+Authors : Brant Lewis
+
+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_Linux_HMDDevice.h"
+
+#include "OVR_Linux_DeviceManager.h"
+
+#include "OVR_Profile.h"
+
+#include "../../3rdParty/EDID/edid.h"
+
+namespace OVR { namespace Linux {
+
+//-------------------------------------------------------------------------------------
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId)
+ : DeviceCreateDesc(factory, Device_HMD),
+ DisplayDeviceName(displayDeviceName),
+ Contents(0),
+ DisplayId(dispId)
+{
+ DeviceId = DisplayDeviceName;
+
+ Desktop.X = 0;
+ Desktop.Y = 0;
+ ResolutionInPixels = Sizei(0);
+ ScreenSizeInMeters = Sizef(0.0f);
+ VCenterFromTopInMeters = 0.0f;
+ LensSeparationInMeters = 0.0f;
+}
+
+HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other)
+ : DeviceCreateDesc(other.pFactory, Device_HMD),
+ DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName),
+ Contents(other.Contents),
+ DisplayId(other.DisplayId)
+{
+ Desktop.X = other.Desktop.X;
+ Desktop.Y = other.Desktop.Y;
+ ResolutionInPixels = other.ResolutionInPixels;
+ ScreenSizeInMeters = other.ScreenSizeInMeters;
+ VCenterFromTopInMeters = other.VCenterFromTopInMeters;
+ LensSeparationInMeters = other.LensSeparationInMeters;
+}
+
+HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc** pcandidate) const
+{
+ if ((other.Type != Device_HMD) || (other.pFactory != pFactory))
+ return Match_None;
+
+ // There are several reasons we can come in here:
+ // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc
+ // - Require exact device DeviceId/DeviceName match
+ // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc
+ // - This DeviceId is empty; becomes candidate
+ // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc
+ // - This other.DeviceId is empty; becomes candidate
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ if ((DeviceId == s2.DeviceId) &&
+ (DisplayId == s2.DisplayId))
+ {
+ // Non-null DeviceId may match while size is different if screen size was overwritten
+ // by SensorDisplayInfo in prior iteration.
+ if (!DeviceId.IsEmpty() ||
+ (ScreenSizeInMeters == s2.ScreenSizeInMeters) )
+ {
+ *pcandidate = 0;
+ return Match_Found;
+ }
+ }
+
+
+ // DisplayInfo takes precedence, although we try to match it first.
+ if ((ResolutionInPixels == s2.ResolutionInPixels) &&
+ (ScreenSizeInMeters == s2.ScreenSizeInMeters))
+ {
+ if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ *pcandidate = 0;
+ return Match_Found;
+ }
+
+ // SensorDisplayInfo may override resolution settings, so store as candidate.
+ if (s2.DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+ // OTHER HMD Monitor desc may initialize DeviceName/Id
+ else if (DeviceId.IsEmpty())
+ {
+ *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this);
+ return Match_Candidate;
+ }
+
+ return Match_None;
+}
+
+
+bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other,
+ bool* newDeviceFlag)
+{
+ // This candidate was the the "best fit" to apply sensor DisplayInfo to.
+ OVR_ASSERT(other.Type == Device_HMD);
+
+ const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other;
+
+ // Force screen size on resolution from SensorDisplayInfo.
+ // We do this because USB detection is more reliable as compared to HDMI EDID,
+ // which may be corrupted by splitter reporting wrong monitor
+ if (s2.DeviceId.IsEmpty())
+ {
+ ScreenSizeInMeters = s2.ScreenSizeInMeters;
+ Contents |= Contents_Screen;
+
+ if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion)
+ {
+ memcpy(DistortionK, s2.DistortionK, sizeof(float)*4);
+ // TODO: DistortionEqn
+ Contents |= Contents_Distortion;
+ }
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ Desktop.X = s2.Desktop.X;
+ Desktop.Y = s2.Desktop.Y;
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else if (DeviceId.IsEmpty())
+ {
+ // This branch is executed when 'fake' HMD descriptor is being replaced by
+ // the real one.
+ DeviceId = s2.DeviceId;
+ DisplayId = s2.DisplayId;
+ DisplayDeviceName = s2.DisplayDeviceName;
+ Desktop.X = s2.Desktop.X;
+ Desktop.Y = s2.Desktop.Y;
+
+ // ScreenSize and Resolution are NOT assigned here, since they may have
+ // come from a sensor DisplayInfo (which has precedence over HDMI).
+
+ if (newDeviceFlag) *newDeviceFlag = true;
+ }
+ else
+ {
+ if (newDeviceFlag) *newDeviceFlag = false;
+ }
+
+ return true;
+}
+
+bool HMDDeviceCreateDesc::MatchDevice(const String& path)
+{
+ return DeviceId.CompareNoCase(path) == 0;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** HMDDeviceFactory
+
+HMDDeviceFactory &HMDDeviceFactory::GetInstance()
+{
+ static HMDDeviceFactory instance;
+ return instance;
+}
+
+void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+ // For now we'll assume the Rift DK1 is attached in extended monitor mode. Ultimately we need to
+ // use XFree86 to enumerate X11 screens in case the Rift is attached as a separate screen.
+
+ bool foundHMD = false;
+ Display* display = XOpenDisplay(NULL);
+ XRRScreenResources *screen = XRRGetScreenResources(display, DefaultRootWindow(display));
+ for (int iscres = screen->noutput - 1; iscres >= 0; --iscres) {
+ RROutput output = screen->outputs[iscres];
+ MonitorInfo * mi = read_edid_data(display, output);
+ if (mi == NULL) {
+ continue;
+ }
+
+ XRROutputInfo * info = XRRGetOutputInfo (display, screen, output);
+ if (info && (0 == memcmp(mi->manufacturer_code, "OVR", 3))) {
+
+ // Generate a device ID string similar to the way Windows does it
+ char device_id[32];
+ OVR_sprintf(device_id, 32, "%s%04d", mi->manufacturer_code, mi->product_code);
+
+ // The default monitor coordinates
+ int mx = 0;
+ int my = 0;
+ int mwidth = 1280;
+ int mheight = 800;
+
+ if (info->connection == RR_Connected && info->crtc) {
+ XRRCrtcInfo * crtc_info = XRRGetCrtcInfo (display, screen, info->crtc);
+ if (crtc_info)
+ {
+ mx = crtc_info->x;
+ my = crtc_info->y;
+ //mwidth = crtc_info->width;
+ //mheight = crtc_info->height;
+ XRRFreeCrtcInfo(crtc_info);
+ }
+ }
+
+ String deviceID = device_id;
+ HMDDeviceCreateDesc hmdCreateDesc(this, deviceID, iscres);
+
+ // Hard-coded defaults in case the device doesn't have the data itself.
+ if (strstr(device_id, "OVR0003"))
+ { // DK2 prototypes and variants (default to HmdType_DK2)
+ hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12576f, 0.07074f, 0.12576f*0.5f, 0.0635f );
+ }
+ else if (strstr(device_id, "OVR0002"))
+ { // HD Prototypes (default to HmdType_DKHDProto)
+ hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12096f, 0.06804f, 0.06804f*0.5f, 0.0635f );
+ }
+ else if (strstr(device_id, "OVR0001"))
+ { // DK1
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f);
+ }
+ else if (strstr(device_id, "OVR00"))
+ { // Future Oculus HMD devices (default to DK1 dimensions)
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f);
+ }
+ else
+ { // Duct-tape prototype
+ hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f, 0.0756f*0.5f, 0.0635f);
+ }
+
+ OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", device_id, mi->dsc_product_name));
+
+ // Notify caller about detected device. This will call EnumerateAddDevice
+ // if the this is the first time device was detected.
+ visitor.Visit(hmdCreateDesc);
+ foundHMD = true;
+ break;
+ } // if
+
+ XRRFreeOutputInfo(info);
+ delete mi;
+ } // for
+ XRRFreeScreenResources(screen);
+
+
+ // Real HMD device is not found; however, we still may have a 'fake' HMD
+ // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo.
+ // Need to find it and set 'Enumerated' to true to avoid Removal notification.
+ if (!foundHMD)
+ {
+ Ptr<DeviceCreateDesc> hmdDevDesc = getManager()->FindDevice("", Device_HMD);
+ if (hmdDevDesc)
+ hmdDevDesc->Enumerated = true;
+ }
+}
+
+#include "OVR_Common_HMDDevice.cpp"
+
+}} // namespace OVR::Linux
+
+
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.h b/LibOVR/Src/OVR_Linux_HMDDevice.h
new file mode 100644
index 0000000..a8c044f
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.h
@@ -0,0 +1,154 @@
+/************************************************************************************
+
+Filename : OVR_Linux_HMDDevice.h
+Content : Linux HMDDevice implementation
+Created : June 17, 2013
+Authors : Brant Lewis
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Linux_HMDDevice_h
+#define OVR_Linux_HMDDevice_h
+
+#include "OVR_Linux_DeviceManager.h"
+#include "OVR_Profile.h"
+
+namespace OVR { namespace Linux {
+
+class HMDDevice;
+
+//-------------------------------------------------------------------------------------
+
+// HMDDeviceFactory enumerates attached Oculus HMD devices.
+//
+// This is currently done by matching monitor device strings.
+
+class HMDDeviceFactory : public DeviceFactory
+{
+public:
+ static HMDDeviceFactory &GetInstance();
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+class HMDDeviceCreateDesc : public DeviceCreateDesc
+{
+ friend class HMDDevice;
+
+protected:
+ enum
+ {
+ Contents_Screen = 1,
+ Contents_Distortion = 2,
+ };
+ String DeviceId;
+ String DisplayDeviceName;
+ struct
+ {
+ int X, Y;
+ } Desktop;
+ unsigned int Contents;
+
+ Sizei ResolutionInPixels;
+ Sizef ScreenSizeInMeters;
+ float VCenterFromTopInMeters;
+ float LensSeparationInMeters;
+
+ // TODO: update these to splines.
+ DistortionEqnType DistortionEqn;
+ float DistortionK[4];
+
+ long DisplayId;
+
+public:
+ HMDDeviceCreateDesc(DeviceFactory* factory,
+ const String& displayDeviceName, long dispId);
+ HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new HMDDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const;
+
+ // Matches device by path.
+ virtual bool MatchDevice(const String& path);
+
+ virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+
+ void SetScreenParameters(int x, int y,
+ int hres, int vres,
+ float hsize, float vsize,
+ float vCenterFromTopInMeters, float lensSeparationInMeters);
+ void SetDistortion(const float* dks);
+
+ HmdTypeEnum GetHmdType() const;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// HMDDevice represents an Oculus HMD device unit. An instance of this class
+// is typically created from the DeviceManager.
+// After HMD device is created, we its sensor data can be obtained by
+// first creating a Sensor object and then wrappig it in SensorFusion.
+
+class HMDDevice : public DeviceImpl<OVR::HMDDevice>
+{
+public:
+ HMDDevice(HMDDeviceCreateDesc* createDesc);
+ ~HMDDevice();
+
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ // Requests the currently used default profile. This profile affects the
+ // settings reported by HMDInfo.
+ virtual Profile* GetProfile();
+ virtual const char* GetProfileName();
+ virtual bool SetProfileName(const char* name);
+
+ // Query associated sensor.
+ virtual OVR::SensorDevice* GetSensor();
+
+protected:
+ HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
+
+ // User name for the profile used with this device.
+ String ProfileName;
+ mutable Ptr<Profile> pCachedProfile;
+};
+
+
+}} // namespace OVR::Linux
+
+#endif // OVR_Linux_HMDDevice_h
+
diff --git a/LibOVR/Src/OVR_Linux_SensorDevice.cpp b/LibOVR/Src/OVR_Linux_SensorDevice.cpp
new file mode 100644
index 0000000..5b671a6
--- /dev/null
+++ b/LibOVR/Src/OVR_Linux_SensorDevice.cpp
@@ -0,0 +1,57 @@
+/************************************************************************************
+
+Filename : OVR_Linux_SensorDevice.cpp
+Content : Linux SensorDevice implementation
+Created : June 13, 2013
+Authors : Brant Lewis
+
+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_Linux_HMDDevice.h"
+#include "OVR_SensorImpl.h"
+#include "OVR_DeviceImpl.h"
+
+namespace OVR { namespace Linux {
+
+} // namespace Linux
+
+//-------------------------------------------------------------------------------------
+void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor)
+{
+ Linux::HMDDeviceCreateDesc hmdCreateDesc(&Linux::HMDDeviceFactory::GetInstance(), String(), 0);
+
+ hmdCreateDesc.SetScreenParameters( 0, 0,
+ displayInfo.HResolution, displayInfo.VResolution,
+ displayInfo.HScreenSize, displayInfo.VScreenSize,
+ displayInfo.VCenter, displayInfo.LensSeparation);
+
+ if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion)
+ {
+ // TODO: update to spline system.
+ hmdCreateDesc.SetDistortion(displayInfo.DistortionK);
+ }
+
+ visitor.Visit(hmdCreateDesc);
+}
+
+} // namespace OVR
+
+
diff --git a/LibOVR/Src/OVR_Profile.cpp b/LibOVR/Src/OVR_Profile.cpp
new file mode 100644
index 0000000..4844c29
--- /dev/null
+++ b/LibOVR/Src/OVR_Profile.cpp
@@ -0,0 +1,1517 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_Profile.cpp
+Content : Structs and functions for loading and storing device profile settings
+Created : February 14, 2013
+Notes :
+
+ Profiles are used to store per-user settings that can be transferred and used
+ across multiple applications. For example, player IPD can be configured once
+ and reused for a unified experience across games. Configuration and saving of profiles
+ can be accomplished in game via the Profile API or by the official Oculus Configuration
+ Utility.
+
+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_Profile.h"
+#include "OVR_Device.h"
+#include "OVR_JSON.h"
+#include "Kernel/OVR_Types.h"
+#include "Kernel/OVR_SysFile.h"
+#include "Kernel/OVR_Allocator.h"
+#include "Kernel/OVR_Array.h"
+
+#ifdef OVR_OS_WIN32
+#include <Shlobj.h>
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+
+#ifdef OVR_OS_LINUX
+#include <unistd.h>
+#include <pwd.h>
+#endif
+
+#endif
+
+
+#define PROFILE_VERSION 2.0
+#define MAX_PROFILE_MAJOR_VERSION 2
+#define MAX_DEVICE_PROFILE_MAJOR_VERSION 1
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------
+// Returns the pathname of the JSON file containing the stored profiles
+String GetBaseOVRPath(bool create_dir)
+{
+ String path;
+
+#if defined(OVR_OS_WIN32)
+
+ TCHAR data_path[MAX_PATH];
+ SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path);
+ path = String(data_path);
+
+ path += "/Oculus";
+
+ if (create_dir)
+ { // Create the Oculus directory if it doesn't exist
+ WCHAR wpath[128];
+ OVR::UTF8Util::DecodeString(wpath, path.ToCStr());
+
+ DWORD attrib = GetFileAttributes(wpath);
+ bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
+ if (!exists)
+ {
+ CreateDirectory(wpath, NULL);
+ }
+ }
+
+#elif defined(OVR_OS_MAC)
+
+ const char* home = getenv("HOME");
+ path = home;
+ path += "/Library/Preferences/Oculus";
+
+ if (create_dir)
+ { // Create the Oculus directory if it doesn't exist
+ DIR* dir = opendir(path);
+ if (dir == NULL)
+ {
+ mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ else
+ {
+ closedir(dir);
+ }
+ }
+
+#else
+
+ passwd* pwd = getpwuid(getuid());
+ const char* home = pwd->pw_dir;
+ path = home;
+ path += "/.config/Oculus";
+
+ if (create_dir)
+ { // Create the Oculus directory if it doesn't exist
+ DIR* dir = opendir(path);
+ if (dir == NULL)
+ {
+ mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ else
+ {
+ closedir(dir);
+ }
+ }
+
+#endif
+
+ return path;
+}
+
+String ProfileManager::GetProfilePath(bool create_dir)
+{
+ String path = GetBaseOVRPath(create_dir);
+ path += "/ProfileDB.json";
+ return path;
+}
+
+bool ProfileManager::GetDeviceTags(const DeviceBase* device, String& product, String& serial)
+{
+ product = "";
+ serial = "";
+
+ if (device && device->GetType() == Device_HMD)
+ {
+ HMDDevice* hmd = (HMDDevice*)device;
+
+ Ptr<SensorDevice> sensor = *(hmd->GetSensor());
+ if (sensor)
+ {
+ SensorInfo sinfo;
+ sensor->GetDeviceInfo(&sinfo);
+ serial = sinfo.SerialNumber; // get the serial number
+
+ // Derive the product tag from the HMD product name
+ HMDInfo hmdinfo;
+ hmd->GetDeviceInfo(&hmdinfo);
+
+ const char* product_name = NULL;
+
+ // If the HMD is unrecognized then use the name stamped into the
+ // sensor firmware
+ if (hmdinfo.HmdType == HmdType_None || hmdinfo.HmdType == HmdType_Unknown)
+ product_name = sinfo.ProductName.ToCStr();
+ else
+ product_name = hmdinfo.ProductName.ToCStr();
+
+ // First strip off "Oculus"
+ const char* oculus = strstr(product_name, "Oculus ");
+ if (oculus)
+ product_name = oculus + OVR_strlen("Oculus ");
+ // And remove spaces from the name
+ for (const char* s=product_name; *s != 0; s++)
+ {
+ if (*s != ' ')
+ product.AppendChar(*s);
+ }
+ }
+ }
+
+ return (!product.IsEmpty() && !serial.IsEmpty());
+}
+
+static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags)
+{
+ if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
+ return NULL;
+
+ JSON* tagged_item = data->GetFirstItem();
+ while (tagged_item)
+ {
+ JSON* tags = tagged_item->GetItemByName("tags");
+ if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize())
+ { // Check for a full tag match on each item
+ int num_matches = 0;
+
+ for (int k=0; k<num_qtags; k++)
+ {
+ JSON* tag = tags->GetFirstItem();
+ while (tag)
+ {
+ JSON* tagval = tag->GetFirstItem();
+ if (tagval && tagval->Name == tag_names[k])
+ {
+ if (tagval->Value == qtags[k])
+ num_matches++;
+ break;
+ }
+ tag = tags->GetNextItem(tag);
+ }
+ }
+
+ // if all tags were matched then copy the values into this Profile
+ if (num_matches == num_qtags)
+ {
+ JSON* vals = tagged_item->GetItemByName("vals");
+ return vals;
+ }
+ }
+
+ tagged_item = data->GetNextItem(tagged_item);
+ }
+
+ return NULL;
+}
+
+static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array<JSON*>& items)
+{
+ if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array)
+ return;
+
+ JSON* tagged_item = data->GetFirstItem();
+ while (tagged_item)
+ {
+ JSON* tags = tagged_item->GetItemByName("tags");
+ if (tags->Type == JSON_Array)
+ { // Check for a tag match on the requested tag
+
+ JSON* tag = tags->GetFirstItem();
+ while (tag)
+ {
+ JSON* tagval = tag->GetFirstItem();
+ if (tagval && tagval->Name == tag_name)
+ {
+ if (tagval->Value == qtag)
+ { // Add this item to the output list
+ items.PushBack(tagged_item);
+ }
+ break;
+ }
+ tag = tags->GetNextItem(tag);
+ }
+ }
+
+ tagged_item = data->GetNextItem(tagged_item);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ***** ProfileManager
+
+ProfileManager::ProfileManager()
+{
+ Changed = false;
+}
+
+ProfileManager::~ProfileManager()
+{
+ ClearCache();
+}
+
+ProfileManager* ProfileManager::Create()
+{
+ return new ProfileManager();
+}
+
+// Clear the local profile cache
+void ProfileManager::ClearCache()
+{
+ Lock::Locker lockScope(&ProfileLock);
+ //ProfileCache.Clear();
+ if (ProfileCache)
+ {
+ //ProfileCache->Release();
+ ProfileCache = NULL;
+ }
+ Changed = false;
+}
+
+// Returns a profile with all system default values
+Profile* ProfileManager::GetDefaultProfile(const DeviceBase* device)
+{
+ // In the absence of any data, set some reasonable profile defaults.
+ // However, this is not future proof and developers should still
+ // provide reasonable default values for queried fields.
+ Profile* profile = CreateProfile();
+ profile->SetValue(OVR_KEY_USER, "default");
+ profile->SetValue(OVR_KEY_NAME, "Default");
+ profile->SetValue(OVR_KEY_GENDER, OVR_DEFAULT_GENDER);
+ profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT);
+ profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, 1.675f);
+ profile->SetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD);
+ float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL};
+ profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2);
+ //profile->SetFloatValue(OVR_KEY_NECK_TO_EYE_VERTICAL, 0.12f);
+
+ // TODO: Provide device specific defaults
+ OVR_UNUSED(device);
+
+ // DK1 default
+ //profile->SetValue("EyeCup", "A");
+
+ return profile;
+}
+
+// Poplulates the local profile cache. This occurs on the first access of the profile
+// data. All profile operations are performed against the local cache until the
+// ProfileManager is released or goes out of scope at which time the cache is serialized
+// to disk.
+void ProfileManager::LoadCache(bool create)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ ClearCache();
+
+ String path = GetProfilePath(false);
+
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root == NULL)
+ {
+ path = GetBaseOVRPath(false) + "/Profiles.json"; // look for legacy profile
+ root = *JSON::Load(path);
+
+ if (root == NULL)
+ {
+ if (create)
+ { // Generate a skeleton profile database
+ root = *JSON::CreateObject();
+ root->AddNumberItem("Oculus Profile Version", 2.0);
+ root->AddItem("Users", JSON::CreateArray());
+ root->AddItem("TaggedData", JSON::CreateArray());
+ ProfileCache = root;
+ }
+
+ return;
+ }
+
+ // Verify the legacy version
+ JSON* version_item = root->GetFirstItem();
+ if (version_item->Name == "Oculus Profile Version")
+ {
+ int major = atoi(version_item->Value.ToCStr());
+ if (major != 1)
+ return; // don't use the file on unsupported major version number
+ }
+ else
+ {
+ return; // invalid file
+ }
+
+ // Convert the legacy format to the new database format
+ LoadV1Profiles(root);
+ }
+ else
+ {
+ // Verify the file format and version
+ JSON* version_item = root->GetFirstItem();
+ if (version_item->Name == "Oculus Profile Version")
+ {
+ int major = atoi(version_item->Value.ToCStr());
+ if (major != 2)
+ return; // don't use the file on unsupported major version number
+ }
+ else
+ {
+ return; // invalid file
+ }
+
+ ProfileCache = root; // store the database contents for traversal
+ }
+}
+
+void ProfileManager::LoadV1Profiles(JSON* v1)
+{
+ JSON* item0 = v1->GetFirstItem();
+ JSON* item1 = v1->GetNextItem(item0);
+ JSON* item2 = v1->GetNextItem(item1);
+
+ // Create the new profile database
+ Ptr<JSON> root = *JSON::CreateObject();
+ root->AddNumberItem("Oculus Profile Version", 2.0);
+ root->AddItem("Users", JSON::CreateArray());
+ root->AddItem("TaggedData", JSON::CreateArray());
+ ProfileCache = root;
+
+ const char* default_dk1_user = item1->Value;
+
+ // Read the number of profiles
+ int profileCount = (int)item2->dValue;
+ JSON* profileItem = item2;
+
+ for (int p=0; p<profileCount; p++)
+ {
+ profileItem = root->GetNextItem(profileItem);
+ if (profileItem == NULL)
+ break;
+
+ if (profileItem->Name == "Profile")
+ {
+ // Read the required Name field
+ const char* profileName;
+ JSON* item = profileItem->GetFirstItem();
+
+ if (item && (item->Name == "Name"))
+ {
+ profileName = item->Value;
+ }
+ else
+ {
+ return; // invalid field
+ }
+
+ // Read the user profile fields
+ if (CreateUser(profileName, profileName))
+ {
+ const char* tag_names[2] = {"User", "Product"};
+ const char* tags[2];
+ tags[0] = profileName;
+
+ Ptr<Profile> user_profile = *CreateProfile();
+ user_profile->SetValue(OVR_KEY_NAME, profileName);
+
+ float neckeye[2] = { 0, 0 };
+
+ item = profileItem->GetNextItem(item);
+ while (item)
+ {
+ if (item->Type != JSON_Object)
+ {
+ if (item->Name == OVR_KEY_PLAYER_HEIGHT)
+ { // Add an explicit eye height
+
+ }
+ if (item->Name == "NeckEyeHori")
+ neckeye[0] = (float)item->dValue;
+ else if (item->Name == "NeckEyeVert")
+ neckeye[1] = (float)item->dValue;
+ else
+ user_profile->SetValue(item);
+ }
+ else
+ {
+ // Add the user/device tag values
+ const char* device_name = item->Name.ToCStr();
+ Ptr<Profile> device_profile = *CreateProfile();
+
+ JSON* device_item = item->GetFirstItem();
+ while (device_item)
+ {
+ device_profile->SetValue(device_item);
+ device_item = item->GetNextItem(device_item);
+ }
+
+ tags[1] = device_name;
+ SetTaggedProfile(tag_names, tags, 2, device_profile);
+ }
+
+ item = profileItem->GetNextItem(item);
+ }
+
+ // Add an explicit eye-height field
+ float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT,
+ OVR_DEFAULT_PLAYER_HEIGHT);
+ if (player_height > 0)
+ {
+ char gender[16];
+ user_profile->GetValue(OVR_KEY_GENDER, gender, 16);
+
+ const float EYE_TO_HEADTOP_RATIO = 0.44538f;
+ const float MALE_AVG_HEAD_HEIGHT = 0.232f;
+ const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
+
+ // compute distance from top of skull to the eye
+ float head_height;
+ if (OVR_strcmp(gender, "Female") == 0)
+ head_height = FEMALE_AVG_HEAD_HEIGHT;
+ else
+ head_height = MALE_AVG_HEAD_HEIGHT;
+
+ float skull = EYE_TO_HEADTOP_RATIO * head_height;
+ float eye_height = player_height - skull;
+
+ user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height);
+ }
+
+ // Convert NeckEye values to an array
+ if (neckeye[0] > 0 && neckeye[1] > 0)
+ user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);
+
+ // Add the user tag values
+ SetTaggedProfile(tag_names, tags, 1, user_profile);
+ }
+ }
+ }
+
+ // since V1 profiles were only for DK1, the assign the user to all DK1's
+ const char* tag_names[1] = { "Product" };
+ const char* tags[1] = { "RiftDK1" };
+ Ptr<Profile> product_profile = *CreateProfile();
+ product_profile->SetValue("DefaultUser", default_dk1_user);
+ SetTaggedProfile(tag_names, tags, 1, product_profile);
+}
+
+// Returns the number of stored profiles for this device type
+int ProfileManager::GetUserCount()
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(false);
+ if (ProfileCache == NULL)
+ return 0;
+ }
+
+ JSON* users = ProfileCache->GetItemByName("Users");
+ if (users == NULL)
+ return 0;
+
+ return users->GetItemCount();
+}
+
+bool ProfileManager::CreateUser(const char* user, const char* name)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(true);
+ if (ProfileCache == NULL)
+ return false;
+ }
+
+ JSON* users = ProfileCache->GetItemByName("Users");
+ if (users == NULL)
+ { // Generate the User section
+ users = JSON::CreateArray();
+ ProfileCache->AddItem("Users", users);
+//TODO: Insert this before the TaggedData
+ }
+
+ // Search for the pre-existence of this user
+ JSON* user_item = users->GetFirstItem();
+ int index = 0;
+ while (user_item)
+ {
+ JSON* userid = user_item->GetItemByName("User");
+ int compare = OVR_strcmp(user, userid->Value);
+ if (compare == 0)
+ { // The user already exists so simply update the fields
+ JSON* name_item = user_item->GetItemByName("Name");
+ if (name_item && OVR_strcmp(name, name_item->Value) != 0)
+ {
+ name_item->Value = name;
+ Changed = true;
+ }
+ return true;
+ }
+ else if (compare < 0)
+ { // A new user should be placed before this item
+ break;
+ }
+
+ user_item = users->GetNextItem(user_item);
+ index++;
+ }
+
+ // Create and fill the user struct
+ JSON* new_user = JSON::CreateObject();
+ new_user->AddStringItem(OVR_KEY_USER, user);
+ new_user->AddStringItem(OVR_KEY_NAME, name);
+ // user_item->AddStringItem("Password", password);
+
+ if (user_item == NULL)
+ users->AddArrayElement(new_user);
+ else
+ users->InsertArrayElement(index, new_user);
+
+ Changed = true;
+ return true;
+}
+
+// Returns the user id of a specific user in the list. The returned
+// memory is locally allocated and should not be stored or deleted. Returns NULL
+// if the index is invalid
+const char* ProfileManager::GetUser(unsigned int index)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(false);
+ if (ProfileCache == NULL)
+ return NULL;
+ }
+
+ JSON* users = ProfileCache->GetItemByName("Users");
+
+ if (users && index < users->GetItemCount())
+ {
+ JSON* user_item = users->GetItemByIndex(index);
+ if (user_item)
+ {
+ JSON* user = user_item->GetFirstItem();
+ if (user)
+ {
+ JSON* userid = user_item->GetItemByName(OVR_KEY_USER);
+ if (userid)
+ return userid->Value.ToCStr();
+ }
+ }
+ }
+
+
+ return NULL;
+}
+
+bool ProfileManager::RemoveUser(const char* user)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(false);
+ if (ProfileCache == NULL)
+ return true;
+ }
+
+ JSON* users = ProfileCache->GetItemByName("Users");
+ if (users == NULL)
+ return true;
+
+ // Remove this user from the User table
+ JSON* user_item = users->GetFirstItem();
+ while (user_item)
+ {
+ JSON* userid = user_item->GetItemByName("User");
+ if (OVR_strcmp(user, userid->Value) == 0)
+ { // Delete the user entry
+ user_item->RemoveNode();
+ user_item->Release();
+ Changed = true;
+ break;
+ }
+
+ user_item = users->GetNextItem(user_item);
+ }
+
+ // Now remove all data entries with this user tag
+ JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
+ Array<JSON*> user_items;
+ FilterTaggedData(tagged_data, "User", user, user_items);
+ for (unsigned int i=0; i<user_items.GetSize(); i++)
+ {
+ user_items[i]->RemoveNode();
+ user_items[i]->Release();
+ Changed = true;
+ }
+
+ return Changed;
+}
+
+Profile* ProfileManager::CreateProfile()
+{
+ Profile* profile = new Profile();
+ return profile;
+}
+
+// Returns the name of the profile that is marked as the current default user.
+const char* ProfileManager::GetDefaultUser(const DeviceBase* device)
+{
+ const char* tag_names[2] = {"Product", "Serial"};
+ const char* tags[2];
+
+ String product;
+ String serial;
+ if (!GetDeviceTags(device, product, serial))
+ return NULL;
+
+ const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
+ const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
+
+ if (product_str && serial_str)
+ {
+ tags[0] = product_str;
+ tags[1] = serial_str;
+ // Look for a default user on this specific device
+ Ptr<Profile> p = *GetTaggedProfile(tag_names, tags, 2);
+ if (p == NULL)
+ { // Look for a default user on this product
+ p = *GetTaggedProfile(tag_names, tags, 1);
+ }
+
+ if (p)
+ {
+ const char* user = p->GetValue("DefaultUser");
+ if (user != NULL && user[0] != 0)
+ {
+ TempBuff = user;
+ return TempBuff.ToCStr();
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+bool ProfileManager::SetDefaultUser(const DeviceBase* device, const char* user)
+{
+ const char* tag_names[2] = {"Product", "Serial"};
+ const char* tags[2];
+
+ String product;
+ String serial;
+ if (!GetDeviceTags(device, product, serial))
+ return NULL;
+
+ const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
+ const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
+
+ if (product_str && serial_str)
+ {
+ tags[0] = product_str;
+ tags[1] = serial_str;
+
+ Ptr<Profile> p = *CreateProfile();
+ p->SetValue("DefaultUser", user);
+ return SetTaggedProfile(tag_names, tags, 2, p);
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(false);
+ if (ProfileCache == NULL)
+ return NULL;
+ }
+
+ JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
+ OVR_ASSERT(tagged_data);
+ if (tagged_data == NULL)
+ return NULL;
+
+ Profile* profile = new Profile();
+
+ JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
+ if (vals)
+ {
+ JSON* item = vals->GetFirstItem();
+ while (item)
+ {
+ //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
+ //profile->Settings.Set(item->Name, item->Value);
+ profile->SetValue(item);
+ item = vals->GetNextItem(item);
+ }
+
+ return profile;
+ }
+ else
+ {
+ profile->Release();
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(true);
+ if (ProfileCache == NULL)
+ return false; // TODO: Generate a new profile DB
+ }
+
+ JSON* tagged_data = ProfileCache->GetItemByName("TaggedData");
+ OVR_ASSERT(tagged_data);
+ if (tagged_data == NULL)
+ return false;
+
+ // Get the cached tagged data section
+ JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags);
+ if (vals == NULL)
+ {
+ JSON* tagged_item = JSON::CreateObject();
+ JSON* taglist = JSON::CreateArray();
+ for (int i=0; i<num_tags; i++)
+ {
+ JSON* k = JSON::CreateObject();
+ k->AddStringItem(tag_names[i], tags[i]);
+ taglist->AddArrayElement(k);
+ }
+
+ vals = JSON::CreateObject();
+
+ tagged_item->AddItem("tags", taglist);
+ tagged_item->AddItem("vals", vals);
+ tagged_data->AddArrayElement(tagged_item);
+ }
+
+ // Now add or update each profile setting in cache
+ for (unsigned int i=0; i<profile->Values.GetSize(); i++)
+ {
+ JSON* value = profile->Values[i];
+
+ bool found = false;
+ JSON* item = vals->GetFirstItem();
+ while (item)
+ {
+ if (value->Name == item->Name)
+ {
+ // Don't allow a pre-existing type to be overridden
+ OVR_ASSERT(value->Type == item->Type);
+
+ if (value->Type == item->Type)
+ { // Check for the same value
+ if (value->Type == JSON_Array)
+ { // Update each array item
+ if (item->GetArraySize() == value->GetArraySize())
+ { // Update each value (assumed to be basic types and not array of objects)
+ JSON* value_element = value->GetFirstItem();
+ JSON* item_element = item->GetFirstItem();
+ while (item_element && value_element)
+ {
+ if (value_element->Type == JSON_String)
+ {
+ if (item_element->Value != value_element->Value)
+ { // Overwrite the changed value and mark for file update
+ item_element->Value = value_element->Value;
+ Changed = true;
+ }
+ }
+ else {
+ if (item_element->dValue != value_element->dValue)
+ { // Overwrite the changed value and mark for file update
+ item_element->dValue = value_element->dValue;
+ Changed = true;
+ }
+ }
+
+ value_element = value->GetNextItem(value_element);
+ item_element = item->GetNextItem(item_element);
+ }
+ }
+ else
+ { // if the array size changed, simply create a new one
+// TODO: Create the new array
+ }
+ }
+ else if (value->Type == JSON_String)
+ {
+ if (item->Value != value->Value)
+ { // Overwrite the changed value and mark for file update
+ item->Value = value->Value;
+ Changed = true;
+ }
+ }
+ else {
+ if (item->dValue != value->dValue)
+ { // Overwrite the changed value and mark for file update
+ item->dValue = value->dValue;
+ Changed = true;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ found = true;
+ break;
+ }
+
+ item = vals->GetNextItem(item);
+ }
+
+ if (!found)
+ { // Add the new value
+ if (value->Type == JSON_String)
+ vals->AddStringItem(value->Name, value->Value);
+ else if (value->Type == JSON_Bool)
+ vals->AddBoolItem(value->Name, (value->dValue != 0));
+ else if (value->Type == JSON_Array)
+ vals->AddItem(value->Name, value->Copy());
+ else
+ vals->AddNumberItem(value->Name, value->dValue);
+
+ Changed = true;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+Profile* ProfileManager::GetProfile(const DeviceBase* device, const char* user)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (ProfileCache == NULL)
+ { // Load the cache
+ LoadCache(false);
+ if (ProfileCache == NULL)
+ return NULL;
+ }
+
+ Profile* profile = new Profile();
+
+ if (device)
+ {
+ if (!profile->LoadDeviceProfile(device) && (user == NULL))
+ {
+ profile->Release();
+ return NULL;
+ }
+ }
+
+ if (user)
+ {
+ String product;
+ String serial;
+ GetDeviceTags(device, product, serial);
+
+ const char* product_str = product.IsEmpty() ? NULL : product.ToCStr();
+ const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr();
+
+ if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str))
+ {
+ profile->Release();
+ return NULL;
+ }
+ }
+
+ return profile;
+}
+
+//-----------------------------------------------------------------------------
+// ***** Profile
+
+Profile::~Profile()
+{
+ ValMap.Clear();
+ for (unsigned int i=0; i<Values.GetSize(); i++)
+ Values[i]->Release();
+
+ Values.Clear();
+}
+
+bool Profile::Close()
+{
+ // TODO:
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void Profile::CopyItems(JSON* root, String prefix)
+{
+ JSON* item = root->GetFirstItem();
+ while (item)
+ {
+ String item_name;
+ if (prefix.IsEmpty())
+ item_name = item->Name;
+ else
+ item_name = prefix + "." + item->Name;
+
+ if (item->Type == JSON_Object)
+ { // recursively copy the children
+
+ CopyItems(item, item_name);
+ }
+ else
+ {
+ //Settings.Set(item_name, item->Value);
+ SetValue(item);
+ }
+
+ item = root->GetNextItem(item);
+ }
+}
+
+//-----------------------------------------------------------------------------
+bool Profile::LoadDeviceFile(unsigned int device_id, const char* serial)
+{
+ if (serial[0] == 0)
+ return false;
+
+ String path = GetBaseOVRPath(false);
+ path += "/Devices.json";
+
+ // Load the device profiles
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root == NULL)
+ return false;
+
+ // Quick sanity check of the file type and format before we parse it
+ JSON* version = root->GetFirstItem();
+ if (version && version->Name == "Oculus Device Profile Version")
+ {
+ int major = atoi(version->Value.ToCStr());
+ if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
+ return false; // don't parse the file on unsupported major version number
+ }
+ else
+ {
+ return false;
+ }
+
+
+ JSON* device = root->GetNextItem(version);
+ while (device)
+ {
+ if (device->Name == "Device")
+ {
+ JSON* product_item = device->GetItemByName("ProductID");
+ JSON* serial_item = device->GetItemByName("Serial");
+ if (product_item && serial_item
+ && (product_item->dValue == device_id) && (serial_item->Value == serial))
+ {
+ // found the entry for this device so recursively copy all the settings to the profile
+ CopyItems(device, "");
+ return true;
+ }
+ }
+
+ device = root->GetNextItem(device);
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+static int BCDByte(unsigned int byte)
+{
+ int digit1 = (byte >> 4) & 0x000f;
+ int digit2 = byte & 0x000f;
+ int decimal = digit1 * 10 + digit2;
+ return decimal;
+}
+
+//-----------------------------------------------------------------------------
+bool Profile::LoadDeviceProfile(const DeviceBase* device)
+{
+ bool success = false;
+ if (device == NULL)
+ return false;
+
+ SensorDevice* sensor = NULL;
+
+ if (device->GetType() == Device_HMD)
+ {
+ // Convert the HMD device to Sensor
+ sensor = ((HMDDevice*)device)->GetSensor();
+ device = sensor;
+ if (device == NULL)
+ return false;
+ }
+
+ if (device->GetType() == Device_Sensor)
+ {
+ SensorDevice* sensor = (SensorDevice*)device;
+
+ SensorInfo sinfo;
+ sensor->GetDeviceInfo(&sinfo);
+
+ int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff);
+ OVR_UNUSED(dev_major);
+ int dev_minor = BCDByte(sinfo.Version & 0xff);
+
+ if (dev_minor > 18)
+ { // If the firmware supports hardware stored profiles then grab the device profile
+ // from the sensor
+ // TBD: Implement this
+ }
+ else
+ {
+ // Grab the model and serial number from the device and use it to access the device
+ // profile file stored on the local machine
+ success = LoadDeviceFile(sinfo.ProductId, sinfo.SerialNumber);
+ }
+ }
+
+ if (sensor)
+ sensor->Release(); // release the sensor handle
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+bool Profile::LoadUser(JSON* root,
+ const char* user,
+ const char* model_name,
+ const char* device_serial)
+{
+ if (user == NULL)
+ return false;
+
+ // For legacy files, convert to old style names
+ //if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0)
+ // model_name = "RiftDK1";
+
+ bool user_found = false;
+ JSON* data = root->GetItemByName("TaggedData");
+ if (data)
+ {
+ const char* tag_names[3];
+ const char* tags[3];
+ tag_names[0] = "User";
+ tags[0] = user;
+ int num_tags = 1;
+
+ if (model_name)
+ {
+ tag_names[num_tags] = "Product";
+ tags[num_tags] = model_name;
+ num_tags++;
+ }
+
+ if (device_serial)
+ {
+ tag_names[num_tags] = "Serial";
+ tags[num_tags] = device_serial;
+ num_tags++;
+ }
+
+ // Retrieve all tag permutations
+ for (int combos=1; combos<=num_tags; combos++)
+ {
+ for (int i=0; i<(num_tags - combos + 1); i++)
+ {
+ JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos);
+ if (vals)
+ {
+ if (i==0) // This tag-combination contains a user match
+ user_found = true;
+
+ // Add the values to the Profile. More specialized multi-tag values
+ // will take precedence over and overwrite generalized ones
+ // For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD
+ JSON* item = vals->GetFirstItem();
+ while (item)
+ {
+ //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr());
+ //Settings.Set(item->Name, item->Value);
+ SetValue(item);
+ item = vals->GetNextItem(item);
+ }
+ }
+ }
+ }
+ }
+
+ if (user_found)
+ SetValue(OVR_KEY_USER, user);
+
+ return user_found;
+}
+
+
+//-----------------------------------------------------------------------------
+bool Profile::LoadProfile(JSON* root,
+ const char* user,
+ const char* device_model,
+ const char* device_serial)
+{
+ if (!LoadUser(root, user, device_model, device_serial))
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+char* Profile::GetValue(const char* key, char* val, int val_length) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ OVR_strcpy(val, val_length, value->Value.ToCStr());
+ return val;
+ }
+ else
+ {
+ val[0] = 0;
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+const char* Profile::GetValue(const char* key)
+{
+ // Non-reentrant query. The returned buffer can only be used until the next call
+ // to GetValue()
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ TempVal = value->Value;
+ return TempVal.ToCStr();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+int Profile::GetNumValues(const char* key) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ if (value->Type == JSON_Array)
+ return value->GetArraySize();
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+bool Profile::GetBoolValue(const char* key, bool default_val) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Bool)
+ return (value->dValue != 0);
+ else
+ return default_val;
+}
+
+//-----------------------------------------------------------------------------
+int Profile::GetIntValue(const char* key, int default_val) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Number)
+ return (int)(value->dValue);
+ else
+ return default_val;
+}
+
+//-----------------------------------------------------------------------------
+float Profile::GetFloatValue(const char* key, float default_val) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Number)
+ return (float)(value->dValue);
+ else
+ return default_val;
+}
+
+//-----------------------------------------------------------------------------
+int Profile::GetFloatValues(const char* key, float* values, int num_vals) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Array)
+ {
+ int val_count = Alg::Min(value->GetArraySize(), num_vals);
+ JSON* item = value->GetFirstItem();
+ int count=0;
+ while (item && count < val_count)
+ {
+ if (item->Type == JSON_Number)
+ values[count] = (float)item->dValue;
+ else
+ break;
+
+ count++;
+ item = value->GetNextItem(item);
+ }
+
+ return count;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+double Profile::GetDoubleValue(const char* key, double default_val) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Number)
+ return value->dValue;
+ else
+ return default_val;
+}
+
+//-----------------------------------------------------------------------------
+int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value) && value->Type == JSON_Array)
+ {
+ int val_count = Alg::Min(value->GetArraySize(), num_vals);
+ JSON* item = value->GetFirstItem();
+ int count=0;
+ while (item && count < val_count)
+ {
+ if (item->Type == JSON_Number)
+ values[count] = item->dValue;
+ else
+ break;
+
+ count++;
+ item = value->GetNextItem(item);
+ }
+
+ return count;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetValue(JSON* val)
+{
+ if (val->Type == JSON_Number)
+ SetDoubleValue(val->Name, val->dValue);
+ else if (val->Type == JSON_Bool)
+ SetBoolValue(val->Name, (val->dValue != 0));
+ else if (val->Type == JSON_String)
+ SetValue(val->Name, val->Value);
+ else if (val->Type == JSON_Array)
+ {
+ if (val == NULL)
+ return;
+
+ // Create a copy of the array
+ JSON* value = val->Copy();
+ Values.PushBack(value);
+ ValMap.Set(value->Name, value);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetValue(const char* key, const char* val)
+{
+ if (key == NULL || val == NULL)
+ return;
+
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ value->Value = val;
+ }
+ else
+ {
+ value = JSON::CreateString(val);
+ value->Name = key;
+
+ Values.PushBack(value);
+ ValMap.Set(key, value);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetBoolValue(const char* key, bool val)
+{
+ if (key == NULL)
+ return;
+
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ value->dValue = val;
+ }
+ else
+ {
+ value = JSON::CreateBool(val);
+ value->Name = key;
+
+ Values.PushBack(value);
+ ValMap.Set(key, value);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetIntValue(const char* key, int val)
+{
+ SetDoubleValue(key, val);
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetFloatValue(const char* key, float val)
+{
+ SetDoubleValue(key, val);
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetFloatValues(const char* key, const float* vals, int num_vals)
+{
+ JSON* value = NULL;
+ int val_count = 0;
+ if (ValMap.Get(key, &value))
+ {
+ if (value->Type == JSON_Array)
+ {
+ // truncate the existing array if fewer entries provided
+ int num_existing_vals = value->GetArraySize();
+ for (int i=num_vals; i<num_existing_vals; i++)
+ value->RemoveLast();
+
+ JSON* item = value->GetFirstItem();
+ while (item && val_count < num_vals)
+ {
+ if (item->Type == JSON_Number)
+ item->dValue = vals[val_count];
+
+ item = value->GetNextItem(item);
+ val_count++;
+ }
+ }
+ else
+ {
+ return; // Maybe we should change the data type?
+ }
+ }
+ else
+ {
+ value = JSON::CreateArray();
+ value->Name = key;
+
+ Values.PushBack(value);
+ ValMap.Set(key, value);
+ }
+
+ for (; val_count < num_vals; val_count++)
+ value->AddArrayNumber(vals[val_count]);
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetDoubleValue(const char* key, double val)
+{
+ JSON* value = NULL;
+ if (ValMap.Get(key, &value))
+ {
+ value->dValue = val;
+ }
+ else
+ {
+ value = JSON::CreateNumber(val);
+ value->Name = key;
+
+ Values.PushBack(value);
+ ValMap.Set(key, value);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals)
+{
+ JSON* value = NULL;
+ int val_count = 0;
+ if (ValMap.Get(key, &value))
+ {
+ if (value->Type == JSON_Array)
+ {
+ // truncate the existing array if fewer entries provided
+ int num_existing_vals = value->GetArraySize();
+ for (int i=num_vals; i<num_existing_vals; i++)
+ value->RemoveLast();
+
+ JSON* item = value->GetFirstItem();
+ while (item && val_count < num_vals)
+ {
+ if (item->Type == JSON_Number)
+ item->dValue = vals[val_count];
+
+ item = value->GetNextItem(item);
+ val_count++;
+ }
+ }
+ else
+ {
+ return; // Maybe we should change the data type?
+ }
+ }
+ else
+ {
+ value = JSON::CreateArray();
+ value->Name = key;
+
+ Values.PushBack(value);
+ ValMap.Set(key, value);
+ }
+
+ for (; val_count < num_vals; val_count++)
+ value->AddArrayNumber(vals[val_count]);
+}
+
+} // OVR
diff --git a/LibOVR/Src/OVR_Profile.h b/LibOVR/Src/OVR_Profile.h
new file mode 100644
index 0000000..e34820a
--- /dev/null
+++ b/LibOVR/Src/OVR_Profile.h
@@ -0,0 +1,203 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Profile.h
+Content : Structs and functions for loading and storing device profile settings
+Created : February 14, 2013
+Notes :
+ Profiles are used to store per-user settings that can be transferred and used
+ across multiple applications. For example, player IPD can be configured once
+ and reused for a unified experience across games. Configuration and saving of profiles
+ can be accomplished in game via the Profile API or by the official Oculus Configuration
+ Utility.
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Profile_h
+#define OVR_Profile_h
+
+#include "OVR_DeviceConstants.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_StringHash.h"
+
+namespace OVR {
+
+class Profile;
+class DeviceBase;
+class JSON;
+
+// -----------------------------------------------------------------------------
+// ***** ProfileManager
+
+// Profiles are interfaced through a ProfileManager object. Applications should
+// create a ProfileManager each time they intend to read or write user profile data.
+// The scope of the ProfileManager object defines when disk I/O is performed. Disk
+// reads are performed on the first profile access and disk writes are performed when
+// the ProfileManager goes out of scope. All profile interactions between these times
+// are performed in local memory and are fast. A typical profile interaction might
+// look like this:
+//
+// {
+// Ptr<ProfileManager> pm = *ProfileManager::Create();
+// Ptr<Profile> profile = pm->LoadProfile(Profile_RiftDK1,
+// pm->GetDefaultProfileName(Profile_RiftDK1));
+// if (profile)
+// { // Retrieve the current profile settings
+// }
+// } // Profile will be destroyed and any disk I/O completed when going out of scope
+class ProfileManager : public RefCountBase<ProfileManager>
+{
+protected:
+ // Synchronize ProfileManager access since it may be accessed from multiple threads,
+ // as it's shared through DeviceManager.
+ Lock ProfileLock;
+ Ptr<JSON> ProfileCache;
+ bool Changed;
+ String TempBuff;
+
+public:
+ static ProfileManager* Create();
+
+ int GetUserCount();
+ const char* GetUser(unsigned int index);
+ bool CreateUser(const char* user, const char* name);
+ bool RemoveUser(const char* user);
+ const char* GetDefaultUser(const DeviceBase* device);
+ bool SetDefaultUser(const DeviceBase* device, const char* user);
+
+ virtual Profile* CreateProfile();
+ Profile* GetProfile(const DeviceBase* device, const char* user);
+ Profile* GetDefaultProfile(const DeviceBase* device);
+ Profile* GetTaggedProfile(const char** key_names, const char** keys, int num_keys);
+ bool SetTaggedProfile(const char** key_names, const char** keys, int num_keys, Profile* profile);
+
+ bool GetDeviceTags(const DeviceBase* device, String& product, String& serial);
+
+protected:
+ ProfileManager();
+ ~ProfileManager();
+ String GetProfilePath(bool create_dir);
+ void LoadCache(bool create);
+ void ClearCache();
+ void LoadV1Profiles(JSON* v1);
+
+
+};
+
+
+//-------------------------------------------------------------------
+// ***** Profile
+
+// The base profile for all users. This object is not created directly.
+// Instead derived device objects provide add specific device members to
+// the base profile
+class Profile : public RefCountBase<Profile>
+{
+protected:
+ OVR::Hash<String, JSON*, String::HashFunctor> ValMap;
+ OVR::Array<JSON*> Values;
+ OVR::String TempVal;
+
+public:
+ ~Profile();
+
+ int GetNumValues(const char* key) const;
+ const char* GetValue(const char* key);
+ char* GetValue(const char* key, char* val, int val_length) const;
+ bool GetBoolValue(const char* key, bool default_val) const;
+ int GetIntValue(const char* key, int default_val) const;
+ float GetFloatValue(const char* key, float default_val) const;
+ int GetFloatValues(const char* key, float* values, int num_vals) const;
+ double GetDoubleValue(const char* key, double default_val) const;
+ int GetDoubleValues(const char* key, double* values, int num_vals) const;
+
+ void SetValue(const char* key, const char* val);
+ void SetBoolValue(const char* key, bool val);
+ void SetIntValue(const char* key, int val);
+ void SetFloatValue(const char* key, float val);
+ void SetFloatValues(const char* key, const float* vals, int num_vals);
+ void SetDoubleValue(const char* key, double val);
+ void SetDoubleValues(const char* key, const double* vals, int num_vals);
+
+ bool Close();
+
+protected:
+ Profile() {};
+
+
+ void SetValue(JSON* val);
+
+
+ static bool LoadProfile(const DeviceBase* device,
+ const char* user,
+ Profile** profile);
+ void CopyItems(JSON* root, String prefix);
+
+ bool LoadDeviceFile(unsigned int device_id, const char* serial);
+ bool LoadDeviceProfile(const DeviceBase* device);
+
+ bool LoadProfile(JSON* root,
+ const char* user,
+ const char* device_model,
+ const char* device_serial);
+
+ bool LoadUser(JSON* root,
+ const char* user,
+ const char* device_name,
+ const char* device_serial);
+
+
+ friend class ProfileManager;
+};
+
+// # defined() check for CAPI compatibility near term that re-defines these
+// for now. To be unified.
+#if !defined(OVR_KEY_USER)
+
+#define OVR_KEY_USER "User"
+#define OVR_KEY_NAME "Name"
+#define OVR_KEY_GENDER "Gender"
+#define OVR_KEY_PLAYER_HEIGHT "PlayerHeight"
+#define OVR_KEY_EYE_HEIGHT "EyeHeight"
+#define OVR_KEY_IPD "IPD"
+#define OVR_KEY_NECK_TO_EYE_DISTANCE "NeckEyeDistance"
+#define OVR_KEY_EYE_RELIEF_DIAL "EyeReliefDial"
+#define OVR_KEY_EYE_TO_NOSE_DISTANCE "EyeToNoseDist"
+#define OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE "MaxEyeToPlateDist"
+#define OVR_KEY_EYE_CUP "EyeCup"
+#define OVR_KEY_CUSTOM_EYE_RENDER "CustomEyeRender"
+
+#define OVR_DEFAULT_GENDER "Male"
+#define OVR_DEFAULT_PLAYER_HEIGHT 1.778f
+#define OVR_DEFAULT_EYE_HEIGHT 1.675f
+#define OVR_DEFAULT_IPD 0.064f
+#define OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL 0.09f
+#define OVR_DEFAULT_NECK_TO_EYE_VERTICAL 0.15f
+#define OVR_DEFAULT_EYE_RELIEF_DIAL 3
+
+#endif // OVR_KEY_USER
+
+String GetBaseOVRPath(bool create_dir);
+
+}
+
+#endif // OVR_Profile_h \ No newline at end of file
diff --git a/LibOVR/Src/OVR_Recording.cpp b/LibOVR/Src/OVR_Recording.cpp
new file mode 100644
index 0000000..a2006c8
--- /dev/null
+++ b/LibOVR/Src/OVR_Recording.cpp
@@ -0,0 +1,38 @@
+/************************************************************************************
+
+Filename : Recording.h
+Content : Support for recording sensor + camera data
+Created : May 12, 2014
+Notes :
+
+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 "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "OVR_DeviceMessages.h"
+#include "OVR_Recording.h"
+
+namespace OVR { namespace Recording {
+
+// global instance that doesn't do anything
+Recorder r;
+
+}} // OVR::Recording
+
diff --git a/LibOVR/Src/OVR_Recording.h b/LibOVR/Src/OVR_Recording.h
new file mode 100644
index 0000000..fc83270
--- /dev/null
+++ b/LibOVR/Src/OVR_Recording.h
@@ -0,0 +1,83 @@
+/************************************************************************************
+
+Filename : Recording.h
+Content : Support for recording sensor + camera data
+Created : March 14, 2014
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_Recording_h
+#define OVR_Recording_h
+
+namespace OVR { namespace Recording {
+
+enum RecordingMode
+{
+ RecordingOff = 0x0,
+ RecordForPlayback = 0x1,
+ RecordForLogging = 0x2
+};
+
+}} // OVR::Recording
+
+#ifdef ENABLE_RECORDING
+
+#include "Recording/Recording_Recorder.h"
+
+#else
+// If Recording is not enabled, then stub it out
+
+namespace OVR {
+
+struct PositionCalibrationReport;
+namespace Vision {
+ class CameraIntrinsics;
+ class DistortionCoefficients;
+ class Blob;
+};
+
+namespace Recording {
+
+class Recorder
+{
+public:
+ OVR_FORCE_INLINE void RecordCameraParams(const Vision::CameraIntrinsics&,
+ const Vision::DistortionCoefficients&) { }
+ OVR_FORCE_INLINE void RecordLedPositions(const Array<PositionCalibrationReport>&) { }
+ OVR_FORCE_INLINE void RecordUserParams(const Vector3f&, float) { }
+ OVR_FORCE_INLINE void RecordDeviceIfcVersion(UByte) { }
+ OVR_FORCE_INLINE void RecordMessage(const Message&) { }
+ OVR_FORCE_INLINE void RecordCameraFrameUsed(UInt32) { }
+ OVR_FORCE_INLINE void RecordVisionSuccess(UInt32) { }
+ template<typename T> OVR_FORCE_INLINE void LogData(const char*, const T&) { }
+ OVR_FORCE_INLINE void SetRecordingMode(RecordingMode) { }
+ OVR_FORCE_INLINE RecordingMode GetRecordingMode() { return RecordingOff; }
+};
+
+extern Recorder r;
+
+OVR_FORCE_INLINE Recorder& GetRecorder() { return r; }
+
+}} // namespace OVR::Recording
+
+#endif // ENABLE_RECORDING
+
+#endif // OVR_Recording_h \ No newline at end of file
diff --git a/LibOVR/Src/OVR_Sensor2Impl.cpp b/LibOVR/Src/OVR_Sensor2Impl.cpp
new file mode 100644
index 0000000..95d486c
--- /dev/null
+++ b/LibOVR/Src/OVR_Sensor2Impl.cpp
@@ -0,0 +1,1124 @@
+/************************************************************************************
+
+Filename : OVR_Sensor2Impl.cpp
+Content : DK2 sensor device specific implementation.
+Created : January 21, 2013
+Authors : Lee Cooper
+
+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_Sensor2Impl.h"
+#include "OVR_SensorImpl_Common.h"
+#include "OVR_Sensor2ImplUtil.h"
+#include "Kernel/OVR_Alg.h"
+
+//extern FILE *SF_LOG_fp;
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor2-specific packet data structures
+
+enum {
+ Sensor2_VendorId = Oculus_VendorId,
+ Sensor2_ProductId = 0x0021,
+
+ Sensor2_BootLoader = 0x1001,
+
+ Sensor2_DefaultReportRate = 1000, // Hz
+};
+
+
+// Messages we care for
+enum Tracker2MessageType
+{
+ Tracker2Message_None = 0,
+ Tracker2Message_Sensors = 11,
+ Tracker2Message_Unknown = 0x100,
+ Tracker2Message_SizeError = 0x101,
+};
+
+
+struct Tracker2Sensors
+{
+ UInt16 LastCommandID;
+ UByte NumSamples;
+ UInt16 RunningSampleCount; // Named 'SampleCount' in the firmware docs.
+ SInt16 Temperature;
+ UInt32 SampleTimestamp;
+ TrackerSample Samples[2];
+ SInt16 MagX, MagY, MagZ;
+ UInt16 FrameCount;
+ UInt32 FrameTimestamp;
+ UByte FrameID;
+ UByte CameraPattern;
+ UInt16 CameraFrameCount; // Named 'CameraCount' in the firmware docs.
+ UInt32 CameraTimestamp;
+
+ Tracker2MessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 64)
+ return Tracker2Message_SizeError;
+
+ LastCommandID = DecodeUInt16(buffer + 1);
+ NumSamples = buffer[3];
+ RunningSampleCount = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+ SampleTimestamp = DecodeUInt32(buffer + 8);
+
+ // Only unpack as many samples as there actually are.
+ UByte iterationCount = (NumSamples > 1) ? 2 : NumSamples;
+
+ for (UByte i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 12 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 20 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 44);
+ MagY = DecodeSInt16(buffer + 46);
+ MagZ = DecodeSInt16(buffer + 48);
+
+ FrameCount = DecodeUInt16(buffer + 50);
+
+ FrameTimestamp = DecodeUInt32(buffer + 52);
+ FrameID = buffer[56];
+ CameraPattern = buffer[57];
+ CameraFrameCount = DecodeUInt16(buffer + 58);
+ CameraTimestamp = DecodeUInt32(buffer + 60);
+
+ return Tracker2Message_Sensors;
+ }
+};
+
+struct Tracker2Message
+{
+ Tracker2MessageType Type;
+ Tracker2Sensors Sensors;
+};
+
+// Sensor reports data in the following coordinate system:
+// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down.
+// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right.
+
+
+// We need to convert it to the following RHS coordinate system:
+// X right, Y Up, Z Back (out of screen)
+//
+Vector3f AccelFromBodyFrameUpdate(const Tracker2Sensors& update, UByte sampleNumber)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ return Vector3f(ax, ay, az) * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const Tracker2Sensors& update)
+{
+ return Vector3f( (float)update.MagX, (float)update.MagY, (float)update.MagZ) * 0.0001f;
+}
+
+Vector3f EulerFromBodyFrameUpdate(const Tracker2Sensors& update, UByte sampleNumber)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ return Vector3f(gx, gy, gz) * 0.0001f;
+}
+
+bool Sensor2DeviceImpl::decodeTracker2Message(Tracker2Message* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(Tracker2Message));
+
+ if (size < 4)
+ {
+ message->Type = Tracker2Message_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case Tracker2Message_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = Tracker2Message_Unknown;
+ break;
+ }
+
+ return (message->Type < Tracker2Message_Unknown) && (message->Type != Tracker2Message_None);
+}
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor2Device
+
+Sensor2DeviceImpl::Sensor2DeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : SensorDeviceImpl(createDesc),
+ LastNumSamples(0),
+ LastRunningSampleCount(0),
+ FullCameraFrameCount(0),
+ LastCameraTime("C"),
+ LastFrameTime("F"),
+ LastSensorTime("S"),
+ LastFrameTimestamp(0)
+{
+ // 15 samples ok in min-window for DK2 since it uses microsecond clock.
+ TimeFilter = SensorTimeFilter(SensorTimeFilter::Settings(15));
+
+ pCalibration = new SensorCalibration(this);
+}
+
+Sensor2DeviceImpl::~Sensor2DeviceImpl()
+{
+ delete pCalibration;
+}
+
+void Sensor2DeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&CurrentRange);
+ }
+
+ // Read the currently configured calibration from sensor.
+ SensorFactoryCalibrationImpl sc;
+ if (GetInternalDevice()->GetFeatureReport(sc.Buffer, SensorFactoryCalibrationImpl::PacketSize))
+ {
+ sc.Unpack();
+ AccelCalibrationOffset = sc.AccelOffset;
+ GyroCalibrationOffset = sc.GyroOffset;
+ AccelCalibrationMatrix = sc.AccelMatrix;
+ GyroCalibrationMatrix = sc.GyroMatrix;
+ CalibrationTemperature = sc.Temperature;
+ }
+
+ // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default.
+ SensorDisplayInfoImpl displayInfo;
+ if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+ Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ?
+ Coord_HMD : Coord_Sensor;
+ }
+ Coordinates = Coord_HMD; // TODO temporary to force it behave
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor2_DefaultReportRate);
+ setOnboardCalibrationEnabled(false);
+
+ // Must send DK2 keep-alive. Set Keep-alive at 10 seconds.
+ KeepAliveMuxReport keepAlive;
+ keepAlive.CommandId = 0;
+ keepAlive.INReport = 11;
+ keepAlive.Interval = 10 * 1000;
+
+ // Device creation is done from background thread so we don't need to add this to the command queue.
+ KeepAliveMuxImpl keepAliveImpl(keepAlive);
+ GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize);
+
+ // Read the temperature data from the device
+ pCalibration->Initialize();
+}
+
+bool Sensor2DeviceImpl::SetTrackingReport(const TrackingReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTrackingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setTrackingReport(const TrackingReport& data)
+{
+ TrackingImpl ci(data);
+ return GetInternalDevice()->SetFeatureReport(ci.Buffer, TrackingImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetTrackingReport(TrackingReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getTrackingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getTrackingReport(TrackingReport* data)
+{
+ TrackingImpl ci;
+ if (GetInternalDevice()->GetFeatureReport(ci.Buffer, TrackingImpl::PacketSize))
+ {
+ ci.Unpack();
+ *data = ci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetDisplayReport(const DisplayReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setDisplayReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setDisplayReport(const DisplayReport& data)
+{
+ DisplayImpl di(data);
+ return GetInternalDevice()->SetFeatureReport(di.Buffer, DisplayImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetDisplayReport(DisplayReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getDisplayReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getDisplayReport(DisplayReport* data)
+{
+ DisplayImpl di;
+ if (GetInternalDevice()->GetFeatureReport(di.Buffer, DisplayImpl::PacketSize))
+ {
+ di.Unpack();
+ *data = di.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetMagCalibrationReport(const MagCalibrationReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setMagCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setMagCalibrationReport(const MagCalibrationReport& data)
+{
+ MagCalibrationImpl mci(data);
+ return GetInternalDevice()->SetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getMagCalibrationReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getMagCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getMagCalibrationReport(MagCalibrationReport* data)
+{
+ MagCalibrationImpl mci;
+ if (GetInternalDevice()->GetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize))
+ {
+ mci.Unpack();
+ *data = mci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetPositionCalibrationReport(const PositionCalibrationReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setPositionCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setPositionCalibrationReport(const PositionCalibrationReport& data)
+{
+ UByte version = GetDeviceInterfaceVersion();
+ if (version < 5)
+ {
+ PositionCalibrationImpl_Pre5 pci(data);
+ return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize);
+ }
+
+ PositionCalibrationImpl pci(data);
+ return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::getPositionCalibrationReport(PositionCalibrationReport* data)
+{
+ UByte version = GetDeviceInterfaceVersion();
+ if (version < 5)
+ {
+ PositionCalibrationImpl_Pre5 pci;
+ if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize))
+ {
+ pci.Unpack();
+ *data = pci.Settings;
+ return true;
+ }
+
+ return false;
+ }
+
+ PositionCalibrationImpl pci;
+ if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize))
+ {
+ pci.Unpack();
+ *data = pci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::GetAllPositionCalibrationReports(Array<PositionCalibrationReport>* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getAllPositionCalibrationReports, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getAllPositionCalibrationReports(Array<PositionCalibrationReport>* data)
+{
+ PositionCalibrationReport pc;
+ bool result = getPositionCalibrationReport(&pc);
+ if (!result)
+ return false;
+
+ int positions = pc.NumPositions;
+ data->Clear();
+ data->Resize(positions);
+
+ for (int i = 0; i < positions; i++)
+ {
+ result = getPositionCalibrationReport(&pc);
+ if (!result)
+ return false;
+ OVR_ASSERT(pc.NumPositions == positions);
+
+ (*data)[pc.PositionIndex] = pc;
+ // IMU should be the last one
+ OVR_ASSERT(pc.PositionType == (pc.PositionIndex == positions - 1) ?
+ PositionCalibrationReport::PositionType_IMU : PositionCalibrationReport::PositionType_LED);
+ }
+ return true;
+}
+
+bool Sensor2DeviceImpl::SetCustomPatternReport(const CustomPatternReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setCustomPatternReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setCustomPatternReport(const CustomPatternReport& data)
+{
+ CustomPatternImpl cpi(data);
+ return GetInternalDevice()->SetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetCustomPatternReport(CustomPatternReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getCustomPatternReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getCustomPatternReport(CustomPatternReport* data)
+{
+ CustomPatternImpl cpi;
+ if (GetInternalDevice()->GetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize))
+ {
+ cpi.Unpack();
+ *data = cpi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetManufacturingReport(const ManufacturingReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setManufacturingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setManufacturingReport(const ManufacturingReport& data)
+{
+ ManufacturingImpl mi(data);
+ return GetInternalDevice()->SetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetManufacturingReport(ManufacturingReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getManufacturingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getManufacturingReport(ManufacturingReport* data)
+{
+ ManufacturingImpl mi;
+ if (GetInternalDevice()->GetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize))
+ {
+ mi.Unpack();
+ *data = mi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetLensDistortionReport(const LensDistortionReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setLensDistortionReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setLensDistortionReport(const LensDistortionReport& data)
+{
+ LensDistortionImpl ui(data);
+ return GetInternalDevice()->SetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetLensDistortionReport(LensDistortionReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getLensDistortionReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getLensDistortionReport(LensDistortionReport* data)
+{
+ LensDistortionImpl ui;
+ if (GetInternalDevice()->GetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize))
+ {
+ ui.Unpack();
+ *data = ui.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetUUIDReport(const UUIDReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setUUIDReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setUUIDReport(const UUIDReport& data)
+{
+ UUIDImpl ui(data);
+ return GetInternalDevice()->SetFeatureReport(ui.Buffer, UUIDImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetUUIDReport(UUIDReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getUUIDReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getUUIDReport(UUIDReport* data)
+{
+ UUIDImpl ui;
+ if (GetInternalDevice()->GetFeatureReport(ui.Buffer, UUIDImpl::PacketSize))
+ {
+ ui.Unpack();
+ *data = ui.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetKeepAliveMuxReport(const KeepAliveMuxReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setKeepAliveMuxReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setKeepAliveMuxReport(const KeepAliveMuxReport& data)
+{
+ KeepAliveMuxImpl kami(data);
+ return GetInternalDevice()->SetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetKeepAliveMuxReport(KeepAliveMuxReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getKeepAliveMuxReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getKeepAliveMuxReport(KeepAliveMuxReport* data)
+{
+ KeepAliveMuxImpl kami;
+ if (GetInternalDevice()->GetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize))
+ {
+ kami.Unpack();
+ *data = kami.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetTemperatureReport(const TemperatureReport& data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return setTemperatureReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTemperatureReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setTemperatureReport(const TemperatureReport& data)
+{
+ TemperatureImpl ti(data);
+ return GetInternalDevice()->SetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::getTemperatureReport(TemperatureReport* data)
+{
+ TemperatureImpl ti;
+ if (GetInternalDevice()->GetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize))
+ {
+ ti.Unpack();
+ *data = ti.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::GetAllTemperatureReports(Array<Array<TemperatureReport> >* data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getAllTemperatureReports(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getAllTemperatureReports, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getAllTemperatureReports(Array<Array<TemperatureReport> >* data)
+{
+ TemperatureReport t;
+ bool result = getTemperatureReport(&t);
+ if (!result)
+ return false;
+
+ int bins = t.NumBins, samples = t.NumSamples;
+ data->Clear();
+ data->Resize(bins);
+ for (int i = 0; i < bins; i++)
+ (*data)[i].Resize(samples);
+
+ for (int i = 0; i < bins; i++)
+ for (int j = 0; j < samples; j++)
+ {
+ result = getTemperatureReport(&t);
+ if (!result)
+ return false;
+ OVR_ASSERT(t.NumBins == bins && t.NumSamples == samples);
+
+ (*data)[t.Bin][t.Sample] = t;
+ }
+ return true;
+}
+
+bool Sensor2DeviceImpl::GetGyroOffsetReport(GyroOffsetReport* data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getGyroOffsetReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getGyroOffsetReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getGyroOffsetReport(GyroOffsetReport* data)
+{
+ GyroOffsetImpl goi;
+ if (GetInternalDevice()->GetFeatureReport(goi.Buffer, GyroOffsetImpl::PacketSize))
+ {
+ goi.Unpack();
+ *data = goi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+void Sensor2DeviceImpl::onTrackerMessage(Tracker2Message* message)
+{
+ if (message->Type != Tracker2Message_Sensors)
+ return;
+
+ const float sampleIntervalTimeUnit = (1.0f / 1000.f);
+ double scaledSampleIntervalTimeUnit = sampleIntervalTimeUnit;
+ Tracker2Sensors& s = message->Sensors;
+
+ double absoluteTimeSeconds = 0.0;
+
+ if (SequenceValid)
+ {
+ UInt32 runningSampleCountDelta;
+
+ if (s.RunningSampleCount < LastRunningSampleCount)
+ {
+ // The running sample count on the device rolled around the 16 bit counter
+ // (expect to happen about once per minute), so RunningSampleCount
+ // needs a high word increment.
+ runningSampleCountDelta = ((((int)s.RunningSampleCount) + 0x10000) - (int)LastRunningSampleCount);
+ }
+ else
+ {
+ runningSampleCountDelta = (s.RunningSampleCount - LastRunningSampleCount);
+ }
+
+ absoluteTimeSeconds = LastSensorTime.TimeSeconds;
+ scaledSampleIntervalTimeUnit = TimeFilter.ScaleTimeUnit(sampleIntervalTimeUnit);
+
+ // If we missed a small number of samples, replicate the last sample.
+ if ((runningSampleCountDelta > LastNumSamples) && (runningSampleCountDelta <= 254))
+ {
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.NumSamples * scaledSampleIntervalTimeUnit;
+ sensors.TimeDelta = (float) ((runningSampleCountDelta - LastNumSamples) * scaledSampleIntervalTimeUnit);
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ pCalibration->Apply(sensors);
+ HandlerRef.Call(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+ }
+
+ LastNumSamples = s.NumSamples;
+ LastRunningSampleCount = s.RunningSampleCount;
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.NumSamples;
+
+ if (s.NumSamples > 2)
+ {
+ iterations = 2;
+ sensors.TimeDelta = (float) ((s.NumSamples - 1) * scaledSampleIntervalTimeUnit);
+ }
+ else
+ {
+ sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledSampleIntervalTimeUnit;
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i);
+ sensors.MagneticField= MagFromBodyFrameUpdate(s);
+ sensors.Temperature = s.Temperature * 0.01f;
+
+ pCalibration->Apply(sensors);
+ HandlerRef.Call(sensors);
+
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit;
+ }
+
+ // Send pixel read only when frame timestamp changes.
+ if (LastFrameTimestamp != s.FrameTimestamp)
+ {
+ MessagePixelRead pixelRead(this);
+ // Prepare message for pixel read
+ pixelRead.PixelReadValue = s.FrameID;
+ pixelRead.RawFrameTime = s.FrameTimestamp;
+ pixelRead.RawSensorTime = s.SampleTimestamp;
+ pixelRead.SensorTimeSeconds = LastSensorTime.TimeSeconds;
+ pixelRead.FrameTimeSeconds = LastFrameTime.TimeSeconds;
+
+ HandlerRef.Call(pixelRead);
+ LastFrameTimestamp = s.FrameTimestamp;
+ }
+
+ UInt16 lowFrameCount = (UInt16) FullCameraFrameCount;
+ // Send message only when frame counter changes
+ if (lowFrameCount != s.CameraFrameCount)
+ {
+ // check for the rollover in the counter
+ if (s.CameraFrameCount < lowFrameCount)
+ FullCameraFrameCount += 0x10000;
+ // update the low bits
+ FullCameraFrameCount = (FullCameraFrameCount & ~0xFFFF) | s.CameraFrameCount;
+
+ MessageExposureFrame vision(this);
+ vision.CameraPattern = s.CameraPattern;
+ vision.CameraFrameCount = FullCameraFrameCount;
+ vision.CameraTimeSeconds = LastCameraTime.TimeSeconds;
+
+ HandlerRef.Call(vision);
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+
+ //LastPixelRead = pixelRead.PixelReadValue;
+ //LastPixelReadTimeStamp = LastFrameTime;
+ }
+ else
+ {
+ if (s.NumSamples != 0)
+ {
+ UByte i = (s.NumSamples > 1) ? 1 : 0;
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i);
+ LastMagneticField = MagFromBodyFrameUpdate(s);
+ LastTemperature = s.Temperature * 0.01f;
+ }
+ }
+}
+
+// Helper function to handle wrap-around of timestamps from Tracker2Message and convert them
+// to system time.
+// - Any timestamps that didn't increment keep their old system time.
+// - This is a bit tricky since we don't know which one of timestamps has most recent time.
+// - The first timestamp must be the IMU one; we assume that others can't be too much ahead of it
+
+void UpdateDK2Timestamps(SensorTimeFilter& tf,
+ SensorTimestampMapping** timestamps, UInt32 *rawValues, int count)
+{
+ int updateIndices[4];
+ int updateCount = 0;
+ int i;
+ double now = Timer::GetSeconds();
+
+ OVR_ASSERT(count <= sizeof(updateIndices)/sizeof(int));
+
+ // Update timestamp wrapping for any values that changed.
+ for (i = 0; i < count; i++)
+ {
+ UInt32 lowMks = (UInt32)timestamps[i]->TimestampMks; // Low 32-bits are raw old timestamp.
+
+ if (rawValues[i] != lowMks)
+ {
+ if (i == 0)
+ {
+ // Only check for rollover in the IMU timestamp
+ if (rawValues[i] < lowMks)
+ {
+ LogText("Timestamp %d rollover, was: %u, now: %u\n", i, lowMks, rawValues[i]);
+ timestamps[i]->TimestampMks += 0x100000000;
+ }
+ // Update the low bits
+ timestamps[i]->TimestampMks = (timestamps[i]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i];
+ }
+ else
+ {
+ // Take the high bits from the main timestamp first (not a typo in the first argument!)
+ timestamps[i]->TimestampMks =
+ (timestamps[0]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i];
+ // Now force it into the reasonable range around the expanded main timestamp
+ if (timestamps[i]->TimestampMks > timestamps[0]->TimestampMks + 0x1000000)
+ timestamps[i]->TimestampMks -= 0x100000000;
+ else if (timestamps[i]->TimestampMks + 0x100000000 < timestamps[0]->TimestampMks + 0x1000000)
+ timestamps[i]->TimestampMks += 0x100000000;
+ }
+
+ updateIndices[updateCount] = i;
+ updateCount++;
+ }
+ }
+
+
+ // TBD: Simplify. Update indices should no longer be needed with new TimeFilter accepting
+ // previous values.
+ // We might want to have multi-element checking time roll-over.
+
+ static const double mksToSec = 1.0 / 1000000.0;
+
+ for (int i = 0; i < updateCount; i++)
+ {
+ SensorTimestampMapping& ts = *timestamps[updateIndices[i]];
+
+ ts.TimeSeconds = tf.SampleToSystemTime(((double)ts.TimestampMks) * mksToSec,
+ now, ts.TimeSeconds, ts.DebugTag);
+ }
+}
+
+
+void Sensor2DeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+ bool processed = false;
+ if (!processed)
+ {
+ Tracker2Message message;
+ if (decodeTracker2Message(&message, pData, length))
+ {
+ processed = true;
+
+ // Process microsecond timestamps from DK2 tracker.
+ // Mapped and raw values must correspond to one another in each array.
+ // IMU timestamp must be the first one!
+ SensorTimestampMapping* tsMaps[3] =
+ {
+ &LastSensorTime,
+ &LastCameraTime,
+ &LastFrameTime
+ };
+ UInt32 tsRawMks[3] =
+ {
+ message.Sensors.SampleTimestamp,
+ message.Sensors.CameraTimestamp,
+ message.Sensors.FrameTimestamp
+ };
+ // Handle wrap-around and convert samples to system time for any samples that changed.
+ UpdateDK2Timestamps(TimeFilter, tsMaps, tsRawMks, sizeof(tsRawMks)/sizeof(tsRawMks[0]));
+
+ onTrackerMessage(&message);
+
+ /*
+ if (SF_LOG_fp)
+ {
+ static UInt32 lastFrameTs = 0;
+ static UInt32 lastCameraTs = 0;
+
+ if ((lastFrameTs != message.Sensors.FrameTimestamp) ||
+ (lastCameraTs = message.Sensors.CameraTimestamp))
+ fprintf(SF_LOG_fp, "msg cameraTs: 0x%X frameTs: 0x%X sensorTs: 0x%X\n",
+ message.Sensors.CameraTimestamp, message.Sensors.FrameTimestamp,
+ message.Sensors.SampleTimestamp);
+
+ lastFrameTs = message.Sensors.FrameTimestamp;
+ lastCameraTs = message.Sensors.CameraTimestamp;
+ }
+ */
+
+#if 0
+ // Checks for DK2 firmware bug.
+ static unsigned SLastSampleTime = 0;
+ if ((SLastSampleTime > message.Sensors.SampleTimestamp) && message.Sensors.SampleTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Sample Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastSampleTime <= message.Sensors.SampleTimestamp);
+ }
+ SLastSampleTime = message.Sensors.SampleTimestamp;
+
+ static unsigned SLastCameraTime = 0;
+ if ((SLastCameraTime > message.Sensors.CameraTimestamp) && message.Sensors.CameraTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Camera Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastCameraTime <= message.Sensors.CameraTimestamp);
+ }
+ SLastCameraTime = message.Sensors.CameraTimestamp;
+
+ static unsigned SLastFrameTime = 0;
+ if ((SLastFrameTime > message.Sensors.FrameTimestamp) && message.Sensors.FrameTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Frame Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastFrameTime <= message.Sensors.FrameTimestamp);
+ }
+ SLastFrameTime = message.Sensors.FrameTimestamp;
+#endif
+ }
+ }
+}
+
+double Sensor2DeviceImpl::OnTicks(double tickSeconds)
+{
+
+ if (tickSeconds >= NextKeepAliveTickSeconds)
+ {
+ // Must send DK2 keep-alive. Set Keep-alive at 10 seconds.
+ KeepAliveMuxReport keepAlive;
+ keepAlive.CommandId = 0;
+ keepAlive.INReport = 11;
+ keepAlive.Interval = 10 * 1000;
+
+ // Device creation is done from background thread so we don't need to add this to the command queue.
+ KeepAliveMuxImpl keepAliveImpl(keepAlive);
+ GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ double keepAliveDelta = 3.0; // Use 3-second interval.
+ NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta;
+ }
+ return NextKeepAliveTickSeconds - tickSeconds;
+}
+
+/*
+// TBD: don't report calibration for now, until we figure out the logic between camera and mag yaw correction
+bool Sensor2DeviceImpl::IsMagCalibrated()
+{
+ return pCalibration->IsMagCalibrated();
+}
+*/
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_Sensor2Impl.h b/LibOVR/Src/OVR_Sensor2Impl.h
new file mode 100644
index 0000000..4555eed
--- /dev/null
+++ b/LibOVR/Src/OVR_Sensor2Impl.h
@@ -0,0 +1,153 @@
+/************************************************************************************
+
+Filename : OVR_Sensor2Impl.h
+Content : DK2 sensor device specific implementation.
+Created : January 21, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Sensor2Impl_h
+#define OVR_Sensor2Impl_h
+
+#include "OVR_SensorImpl.h"
+#include "OVR_SensorCalibration.h"
+
+namespace OVR {
+
+struct Tracker2Message;
+
+//-------------------------------------------------------------------------------------
+// Used to convert DK2 Mks timestamps to system TimeSeconds
+struct SensorTimestampMapping
+{
+ UInt64 TimestampMks;
+ double TimeSeconds;
+ const char* DebugTag;
+
+ SensorTimestampMapping(const char* debugTag)
+ : TimestampMks(0), TimeSeconds(0.0), DebugTag(debugTag) { }
+};
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::Sensor2DeviceImpl
+
+// Oculus Sensor2 interface.
+class Sensor2DeviceImpl : public SensorDeviceImpl
+{
+public:
+ Sensor2DeviceImpl(SensorDeviceCreateDesc* createDesc);
+ ~Sensor2DeviceImpl();
+
+ // HIDDevice::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+ virtual double OnTicks(double tickSeconds);
+
+ // Get/set feature reports added for DK2. See 'DK2 Firmware Specification' document details.
+ virtual bool SetTrackingReport(const TrackingReport& data);
+ virtual bool GetTrackingReport(TrackingReport* data);
+
+ virtual bool SetDisplayReport(const DisplayReport& data);
+ virtual bool GetDisplayReport(DisplayReport* data);
+
+ virtual bool SetMagCalibrationReport(const MagCalibrationReport& data);
+ virtual bool GetMagCalibrationReport(MagCalibrationReport* data);
+
+ virtual bool SetPositionCalibrationReport(const PositionCalibrationReport& data);
+ virtual bool GetAllPositionCalibrationReports(Array<PositionCalibrationReport>* data);
+
+ virtual bool SetCustomPatternReport(const CustomPatternReport& data);
+ virtual bool GetCustomPatternReport(CustomPatternReport* data);
+
+ virtual bool SetKeepAliveMuxReport(const KeepAliveMuxReport& data);
+ virtual bool GetKeepAliveMuxReport(KeepAliveMuxReport* data);
+
+ virtual bool SetManufacturingReport(const ManufacturingReport& data);
+ virtual bool GetManufacturingReport(ManufacturingReport* data);
+
+ virtual bool SetUUIDReport(const UUIDReport& data);
+ virtual bool GetUUIDReport(UUIDReport* data);
+
+ virtual bool SetTemperatureReport(const TemperatureReport& data);
+ virtual bool GetAllTemperatureReports(Array<Array<TemperatureReport> >*);
+
+ virtual bool GetGyroOffsetReport(GyroOffsetReport* data);
+
+ virtual bool SetLensDistortionReport(const LensDistortionReport& data);
+ virtual bool GetLensDistortionReport(LensDistortionReport* data);
+
+protected:
+ virtual void openDevice();
+
+ bool decodeTracker2Message(Tracker2Message* message, UByte* buffer, int size);
+
+ bool setTrackingReport(const TrackingReport& data);
+ bool getTrackingReport(TrackingReport* data);
+
+ bool setDisplayReport(const DisplayReport& data);
+ bool getDisplayReport(DisplayReport* data);
+
+ bool setMagCalibrationReport(const MagCalibrationReport& data);
+ bool getMagCalibrationReport(MagCalibrationReport* data);
+
+ bool setPositionCalibrationReport(const PositionCalibrationReport& data);
+ bool getPositionCalibrationReport(PositionCalibrationReport* data);
+ bool getAllPositionCalibrationReports(Array<PositionCalibrationReport>* data);
+
+ bool setCustomPatternReport(const CustomPatternReport& data);
+ bool getCustomPatternReport(CustomPatternReport* data);
+
+ bool setKeepAliveMuxReport(const KeepAliveMuxReport& data);
+ bool getKeepAliveMuxReport(KeepAliveMuxReport* data);
+
+ bool setManufacturingReport(const ManufacturingReport& data);
+ bool getManufacturingReport(ManufacturingReport* data);
+
+ bool setUUIDReport(const UUIDReport& data);
+ bool getUUIDReport(UUIDReport* data);
+
+ bool setTemperatureReport(const TemperatureReport& data);
+ bool getTemperatureReport(TemperatureReport* data);
+ bool getAllTemperatureReports(Array<Array<TemperatureReport> >*);
+
+ bool getGyroOffsetReport(GyroOffsetReport* data);
+
+ bool setLensDistortionReport(const LensDistortionReport& data);
+ bool getLensDistortionReport(LensDistortionReport* data);
+
+ // Called for decoded messages
+ void onTrackerMessage(Tracker2Message* message);
+
+ UByte LastNumSamples;
+ UInt16 LastRunningSampleCount;
+ UInt32 FullCameraFrameCount;
+
+ SensorTimestampMapping LastCameraTime;
+ SensorTimestampMapping LastFrameTime;
+ SensorTimestampMapping LastSensorTime;
+ // Record last frame timestamp to know when to send pixelRead messages.
+ UInt32 LastFrameTimestamp;
+
+ SensorCalibration *pCalibration;
+};
+
+} // namespace OVR
+
+#endif // OVR_Sensor2Impl_h
diff --git a/LibOVR/Src/OVR_Sensor2ImplUtil.h b/LibOVR/Src/OVR_Sensor2ImplUtil.h
new file mode 100644
index 0000000..91b2195
--- /dev/null
+++ b/LibOVR/Src/OVR_Sensor2ImplUtil.h
@@ -0,0 +1,676 @@
+/************************************************************************************
+
+Filename : OVR_Sensor2ImplUtil.h
+Content : DK2 sensor device feature report utils.
+Created : January 27, 2014
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Sensor2ImplUtil_h
+#define OVR_Sensor2ImplUtil_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorImpl_Common.h"
+#include "Kernel/OVR_Alg.h"
+
+namespace OVR {
+
+using namespace Alg;
+
+// Tracking feature report.
+struct TrackingImpl
+{
+ enum { PacketSize = 13 };
+ UByte Buffer[PacketSize];
+
+ TrackingReport Settings;
+
+ TrackingImpl()
+ {
+ for (int i=0; i<PacketSize; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ Buffer[0] = 12;
+ }
+
+ TrackingImpl(const TrackingReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 12;
+ EncodeUInt16 ( Buffer+1, Settings.CommandId );
+ Buffer[3] = Settings.Pattern;
+ Buffer[4] = UByte(Settings.Enable << 0 |
+ Settings.Autoincrement << 1 |
+ Settings.UseCarrier << 2 |
+ Settings.SyncInput << 3 |
+ Settings.VsyncLock << 4 |
+ Settings.CustomPattern << 5);
+ Buffer[5] = 0;
+ EncodeUInt16 ( Buffer+6, Settings.ExposureLength );
+ EncodeUInt16 ( Buffer+8, Settings.FrameInterval );
+ EncodeUInt16 ( Buffer+10, Settings.VsyncOffset );
+ Buffer[12] = Settings.DutyCycle;
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.Pattern = Buffer[3];
+ Settings.Enable = (Buffer[4] & 0x01) != 0;
+ Settings.Autoincrement = (Buffer[4] & 0x02) != 0;
+ Settings.UseCarrier = (Buffer[4] & 0x04) != 0;
+ Settings.SyncInput = (Buffer[4] & 0x08) != 0;
+ Settings.VsyncLock = (Buffer[4] & 0x10) != 0;
+ Settings.CustomPattern = (Buffer[4] & 0x20) != 0;
+ Settings.ExposureLength = DecodeUInt16(Buffer+6);
+ Settings.FrameInterval = DecodeUInt16(Buffer+8);
+ Settings.VsyncOffset = DecodeUInt16(Buffer+10);
+ Settings.DutyCycle = Buffer[12];
+ }
+};
+
+// Display feature report.
+struct DisplayImpl
+{
+ enum { PacketSize = 16 };
+ UByte Buffer[PacketSize];
+
+ DisplayReport Settings;
+
+ DisplayImpl()
+ {
+ for (int i=0; i<PacketSize; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ Buffer[0] = 13;
+ }
+
+ DisplayImpl(const DisplayReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 13;
+ EncodeUInt16 ( Buffer+1, Settings.CommandId );
+ Buffer[3] = Settings.Brightness;
+ Buffer[4] = UByte( (Settings.ShutterType & 0x0F) |
+ (Settings.CurrentLimit & 0x03) << 4 |
+ (Settings.UseRolling ? 0x40 : 0) |
+ (Settings.ReverseRolling ? 0x80 : 0));
+ Buffer[5] = UByte( (Settings.HighBrightness ? 0x01 : 0) |
+ (Settings.SelfRefresh ? 0x02 : 0) |
+ (Settings.ReadPixel ? 0x04 : 0) |
+ (Settings.DirectPentile ? 0x08 : 0));
+ EncodeUInt16 ( Buffer+8, Settings.Persistence );
+ EncodeUInt16 ( Buffer+10, Settings.LightingOffset );
+ EncodeUInt16 ( Buffer+12, Settings.PixelSettle );
+ EncodeUInt16 ( Buffer+14, Settings.TotalRows );
+ }
+
+ void Unpack()
+ {
+
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.Brightness = Buffer[3];
+ Settings.ShutterType = DisplayReport::ShutterTypeEnum(Buffer[4] & 0x0F);
+ Settings.CurrentLimit = DisplayReport::CurrentLimitEnum((Buffer[4] >> 4) & 0x02);
+ Settings.UseRolling = (Buffer[4] & 0x40) != 0;
+ Settings.ReverseRolling = (Buffer[4] & 0x80) != 0;
+ Settings.HighBrightness = (Buffer[5] & 0x01) != 0;
+ Settings.SelfRefresh = (Buffer[5] & 0x02) != 0;
+ Settings.ReadPixel = (Buffer[5] & 0x04) != 0;
+ Settings.DirectPentile = (Buffer[5] & 0x08) != 0;
+ Settings.Persistence = DecodeUInt16(Buffer+8);
+ Settings.LightingOffset = DecodeUInt16(Buffer+10);
+ Settings.PixelSettle = DecodeUInt16(Buffer+12);
+ Settings.TotalRows = DecodeUInt16(Buffer+14);
+ }
+};
+
+// MagCalibration feature report.
+struct MagCalibrationImpl
+{
+ enum { PacketSize = 52 };
+ UByte Buffer[PacketSize];
+
+ MagCalibrationReport Settings;
+
+ MagCalibrationImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 14;
+ }
+
+ MagCalibrationImpl(const MagCalibrationReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 14;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.Version;
+
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 4; j++)
+ {
+ SInt32 value = SInt32(Settings.Calibration.M[i][j] * 1e4f);
+ EncodeSInt32(Buffer + 4 + 4 * (4 * i + j), value);
+ }
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.Version = Buffer[3];
+
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 4; j++)
+ {
+ SInt32 value = DecodeSInt32(Buffer + 4 + 4 * (4 * i + j));
+ Settings.Calibration.M[i][j] = (float)value * 1e-4f;
+ }
+ }
+};
+
+//-------------------------------------------------------------------------------------
+// PositionCalibration feature report.
+// - Sensor interface versions before 5 do not support Normal and Rotation.
+
+struct PositionCalibrationImpl
+{
+ enum { PacketSize = 30 };
+ UByte Buffer[PacketSize];
+
+ PositionCalibrationReport Settings;
+
+ PositionCalibrationImpl()
+ {
+ for (int i=0; i<PacketSize; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ Buffer[0] = 15;
+ }
+
+ PositionCalibrationImpl(const PositionCalibrationReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 15;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.Version;
+
+ Vector3d position = Settings.Position * 1e6;
+ EncodeSInt32(Buffer+4, (SInt32) position.x);
+ EncodeSInt32(Buffer+8, (SInt32) position.y);
+ EncodeSInt32(Buffer+12, (SInt32) position.z);
+
+ Vector3d normal = Settings.Normal * 1e6;
+ EncodeSInt16(Buffer+16, (SInt16) normal.x);
+ EncodeSInt16(Buffer+18, (SInt16) normal.y);
+ EncodeSInt16(Buffer+20, (SInt16) normal.z);
+
+ double rotation = Settings.Angle * 1e4;
+ EncodeSInt16(Buffer+22, (SInt16) rotation);
+
+ EncodeUInt16(Buffer+24, Settings.PositionIndex);
+ EncodeUInt16(Buffer+26, Settings.NumPositions);
+ EncodeUInt16(Buffer+28, UInt16(Settings.PositionType));
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.Version = Buffer[3];
+
+ Settings.Position.x = DecodeSInt32(Buffer + 4) * 1e-6;
+ Settings.Position.y = DecodeSInt32(Buffer + 8) * 1e-6;
+ Settings.Position.z = DecodeSInt32(Buffer + 12) * 1e-6;
+
+ Settings.Normal.x = DecodeSInt16(Buffer + 16) * 1e-6;
+ Settings.Normal.y = DecodeSInt16(Buffer + 18) * 1e-6;
+ Settings.Normal.z = DecodeSInt16(Buffer + 20) * 1e-6;
+
+ Settings.Angle = DecodeSInt16(Buffer + 22) * 1e-4;
+
+ Settings.PositionIndex = DecodeUInt16(Buffer + 24);
+ Settings.NumPositions = DecodeUInt16(Buffer + 26);
+
+ Settings.PositionType = PositionCalibrationReport::PositionTypeEnum(DecodeUInt16(Buffer + 28));
+ }
+};
+
+struct PositionCalibrationImpl_Pre5
+{
+ enum { PacketSize = 22 };
+ UByte Buffer[PacketSize];
+
+ PositionCalibrationReport Settings;
+
+ PositionCalibrationImpl_Pre5()
+ {
+ for (int i=0; i<PacketSize; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ Buffer[0] = 15;
+ }
+
+ PositionCalibrationImpl_Pre5(const PositionCalibrationReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 15;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.Version;
+
+ Vector3d position = Settings.Position * 1e6;
+ EncodeSInt32(Buffer+4 , (SInt32) position.x);
+ EncodeSInt32(Buffer+8 , (SInt32) position.y);
+ EncodeSInt32(Buffer+12, (SInt32) position.z);
+
+ EncodeUInt16(Buffer+16, Settings.PositionIndex);
+ EncodeUInt16(Buffer+18, Settings.NumPositions);
+ EncodeUInt16(Buffer+20, UInt16(Settings.PositionType));
+ }
+
+ void Unpack()
+ {
+
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.Version = Buffer[3];
+
+ Settings.Position.x = DecodeSInt32(Buffer + 4) * 1e-6;
+ Settings.Position.y = DecodeSInt32(Buffer + 8) * 1e-6;
+ Settings.Position.z = DecodeSInt32(Buffer + 12) * 1e-6;
+
+ Settings.PositionIndex = DecodeUInt16(Buffer + 16);
+ Settings.NumPositions = DecodeUInt16(Buffer + 18);
+ Settings.PositionType = PositionCalibrationReport::PositionTypeEnum(DecodeUInt16(Buffer + 20));
+ }
+};
+
+// CustomPattern feature report.
+struct CustomPatternImpl
+{
+ enum { PacketSize = 12 };
+ UByte Buffer[PacketSize];
+
+ CustomPatternReport Settings;
+
+ CustomPatternImpl()
+ {
+ for (int i=0; i<PacketSize; i++)
+ {
+ Buffer[i] = 0;
+ }
+
+ Buffer[0] = 16;
+ }
+
+ CustomPatternImpl(const CustomPatternReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 16;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.SequenceLength;
+ EncodeUInt32(Buffer+4 , Settings.Sequence);
+ EncodeUInt16(Buffer+8 , Settings.LEDIndex);
+ EncodeUInt16(Buffer+10, Settings.NumLEDs);
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.SequenceLength = Buffer[3];
+ Settings.Sequence = DecodeUInt32(Buffer+4);
+ Settings.LEDIndex = DecodeUInt16(Buffer+8);
+ Settings.NumLEDs = DecodeUInt16(Buffer+10);
+ }
+};
+
+// Manufacturing feature report.
+struct ManufacturingImpl
+{
+ enum { PacketSize = 16 };
+ UByte Buffer[PacketSize];
+
+ ManufacturingReport Settings;
+
+ ManufacturingImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 18;
+ }
+
+ ManufacturingImpl(const ManufacturingReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 18;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.NumStages;
+ Buffer[4] = Settings.Stage;
+ Buffer[5] = Settings.StageVersion;
+ EncodeUInt16(Buffer+6, Settings.StageLocation);
+ EncodeUInt32(Buffer+8, Settings.StageTime);
+ EncodeUInt32(Buffer+12, Settings.Result);
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.NumStages = Buffer[3];
+ Settings.Stage = Buffer[4];
+ Settings.StageVersion = Buffer[5];
+ Settings.StageLocation = DecodeUInt16(Buffer+6);
+ Settings.StageTime = DecodeUInt32(Buffer+8);
+ Settings.Result = DecodeUInt32(Buffer+12);
+ }
+};
+
+// UUID feature report.
+struct UUIDImpl
+{
+ enum { PacketSize = 23 };
+ UByte Buffer[PacketSize];
+
+ UUIDReport Settings;
+
+ UUIDImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 19;
+ }
+
+ UUIDImpl(const UUIDReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 19;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ for (int i = 0; i < 20; ++i)
+ Buffer[3 + i] = Settings.UUIDValue[i];
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ for (int i = 0; i < 20; ++i)
+ Settings.UUIDValue[i] = Buffer[3 + i];
+ }
+};
+
+// LensDistortion feature report.
+struct LensDistortionImpl
+{
+ enum { PacketSize = 64 };
+ UByte Buffer[PacketSize];
+
+ LensDistortionReport Settings;
+
+ LensDistortionImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 22;
+ }
+
+ LensDistortionImpl(const LensDistortionReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 19;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+
+ Buffer[3] = Settings.NumDistortions;
+ Buffer[4] = Settings.DistortionIndex;
+ Buffer[5] = Settings.Bitmask;
+ EncodeUInt16(Buffer+6, Settings.LensType);
+ EncodeUInt16(Buffer+8, Settings.Version);
+ EncodeUInt16(Buffer+10, Settings.EyeRelief);
+
+ for (int i = 0; i < 11; ++i)
+ EncodeUInt16(Buffer+12+2*i, Settings.KCoefficients[i]);
+
+ EncodeUInt16(Buffer+34, Settings.MaxR);
+ EncodeUInt16(Buffer+36, Settings.MetersPerTanAngleAtCenter);
+
+ for (int i = 0; i < 4; ++i)
+ EncodeUInt16(Buffer+38+2*i, Settings.ChromaticAberration[i]);
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+
+ Settings.NumDistortions = Buffer[3];
+ Settings.DistortionIndex = Buffer[4];
+ Settings.Bitmask = Buffer[5];
+ Settings.LensType = DecodeUInt16(Buffer+6);
+ Settings.Version = DecodeUInt16(Buffer+8);
+ Settings.EyeRelief = DecodeUInt16(Buffer+10);
+
+ for (int i = 0; i < 11; ++i)
+ Settings.KCoefficients[i] = DecodeUInt16(Buffer+12+2*i);
+
+ Settings.MaxR = DecodeUInt16(Buffer+34);
+ Settings.MetersPerTanAngleAtCenter = DecodeUInt16(Buffer+36);
+
+ for (int i = 0; i < 4; ++i)
+ Settings.ChromaticAberration[i] = DecodeUInt16(Buffer+38+2*i);
+ }
+};
+
+// KeepAliveMux feature report.
+struct KeepAliveMuxImpl
+{
+ enum { PacketSize = 6 };
+ UByte Buffer[PacketSize];
+
+ KeepAliveMuxReport Settings;
+
+ KeepAliveMuxImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 17;
+ }
+
+ KeepAliveMuxImpl(const KeepAliveMuxReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+ Buffer[0] = 17;
+ EncodeUInt16(Buffer+1, Settings.CommandId);
+ Buffer[3] = Settings.INReport;
+ EncodeUInt16(Buffer+4, Settings.Interval);
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer+1);
+ Settings.INReport = Buffer[3];
+ Settings.Interval = DecodeUInt16(Buffer+4);
+ }
+};
+
+// Temperature feature report.
+struct TemperatureImpl
+{
+ enum { PacketSize = 24 };
+ UByte Buffer[PacketSize];
+
+ TemperatureReport Settings;
+
+ TemperatureImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 20;
+ }
+
+ TemperatureImpl(const TemperatureReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 20;
+ EncodeUInt16(Buffer + 1, Settings.CommandId);
+ Buffer[3] = Settings.Version;
+
+ Buffer[4] = Settings.NumBins;
+ Buffer[5] = Settings.Bin;
+ Buffer[6] = Settings.NumSamples;
+ Buffer[7] = Settings.Sample;
+
+ EncodeSInt16(Buffer + 8 , SInt16(Settings.TargetTemperature * 1e2));
+ EncodeSInt16(Buffer + 10, SInt16(Settings.ActualTemperature * 1e2));
+
+ EncodeUInt32(Buffer + 12, Settings.Time);
+
+ Vector3d offset = Settings.Offset * 1e4;
+ PackSensor(Buffer + 16, (SInt16) offset.x, (SInt16) offset.y, (SInt16) offset.z);
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer + 1);
+ Settings.Version = Buffer[3];
+
+ Settings.NumBins = Buffer[4];
+ Settings.Bin = Buffer[5];
+ Settings.NumSamples = Buffer[6];
+ Settings.Sample = Buffer[7];
+
+ Settings.TargetTemperature = DecodeSInt16(Buffer + 8) * 1e-2;
+ Settings.ActualTemperature = DecodeSInt16(Buffer + 10) * 1e-2;
+
+ Settings.Time = DecodeUInt32(Buffer + 12);
+
+ SInt32 x, y, z;
+ UnpackSensor(Buffer + 16, &x, &y, &z);
+ Settings.Offset = Vector3d(x, y, z) * 1e-4;
+ }
+};
+
+// GyroOffset feature report.
+struct GyroOffsetImpl
+{
+ enum { PacketSize = 18 };
+ UByte Buffer[PacketSize];
+
+ GyroOffsetReport Settings;
+
+ GyroOffsetImpl()
+ {
+ memset(Buffer, 0, sizeof(Buffer));
+ Buffer[0] = 21;
+ }
+
+ GyroOffsetImpl(const GyroOffsetReport& settings)
+ : Settings(settings)
+ {
+ Pack();
+ }
+
+ void Pack()
+ {
+
+ Buffer[0] = 21;
+ Buffer[1] = UByte(Settings.CommandId & 0xFF);
+ Buffer[2] = UByte(Settings.CommandId >> 8);
+ Buffer[3] = UByte(Settings.Version);
+
+ Vector3d offset = Settings.Offset * 1e4;
+ PackSensor(Buffer + 4, (SInt32) offset.x, (SInt32) offset.y, (SInt32) offset.z);
+
+ EncodeSInt16(Buffer + 16, SInt16(Settings.Temperature * 1e2));
+ }
+
+ void Unpack()
+ {
+ Settings.CommandId = DecodeUInt16(Buffer + 1);
+ Settings.Version = GyroOffsetReport::VersionEnum(Buffer[3]);
+
+ SInt32 x, y, z;
+ UnpackSensor(Buffer + 4, &x, &y, &z);
+ Settings.Offset = Vector3d(x, y, z) * 1e-4f;
+
+ Settings.Temperature = DecodeSInt16(Buffer + 16) * 1e-2;
+ }
+};
+
+} // namespace OVR
+
+#endif // OVR_Sensor2ImplUtil_h
diff --git a/LibOVR/Src/OVR_SensorCalibration.cpp b/LibOVR/Src/OVR_SensorCalibration.cpp
new file mode 100644
index 0000000..94fbb27
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorCalibration.cpp
@@ -0,0 +1,354 @@
+/************************************************************************************
+
+Filename : OVR_SensorCalibration.cpp
+Content : Calibration data implementation for the IMU messages
+Created : January 28, 2014
+Authors : Max Katsev
+
+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_SensorCalibration.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_Threads.h"
+#include <time.h>
+
+namespace OVR {
+
+using namespace Alg;
+
+const UByte VERSION = 2;
+const UByte MAX_COMPAT_VERSION = 15;
+
+SensorCalibration::SensorCalibration(SensorDevice* pSensor)
+ : MagCalibrated(false), GyroFilter(6000), GyroAutoTemperature(0)
+{
+ this->pSensor = pSensor;
+};
+
+void SensorCalibration::Initialize()
+{
+ // read factory calibration
+ pSensor->GetFactoryCalibration(&AccelOffset, &GyroAutoOffset, &AccelMatrix, &GyroMatrix, &GyroAutoTemperature);
+
+ // if the headset has an autocalibrated offset, prefer it over the factory defaults
+ GyroOffsetReport gyroReport;
+ bool result = pSensor->GetGyroOffsetReport(&gyroReport);
+ if (result && gyroReport.Version != GyroOffsetReport::Version_NoOffset)
+ {
+ GyroAutoOffset = (Vector3f) gyroReport.Offset;
+ GyroAutoTemperature = (float) gyroReport.Temperature;
+ }
+
+ // read the temperature tables and prepare the interpolation structures
+ result = pSensor->GetAllTemperatureReports(&TemperatureReports);
+ OVR_ASSERT(result);
+ for (int i = 0; i < 3; i++)
+ Interpolators[i].Initialize(TemperatureReports, i);
+
+ // read the mag calibration
+ MagCalibrationReport report;
+ result = pSensor->GetMagCalibrationReport(&report);
+ MagCalibrated = result && report.Version > 0;
+ MagMatrix = report.Calibration;
+ if (!MagCalibrated)
+ {
+ // OVR_ASSERT(false);
+ LogError("Magnetometer calibration not found!\n");
+ }
+}
+
+void SensorCalibration::DebugPrintLocalTemperatureTable()
+{
+ LogText("TemperatureReports:\n");
+ for (int i = 0; i < (int)TemperatureReports.GetSize(); i++)
+ {
+ for (int j = 0; j < (int)TemperatureReports[i].GetSize(); j++)
+ {
+ TemperatureReport& tr = TemperatureReports[i][j];
+
+ LogText("[%d][%d]: Version=%3d, Bin=%d/%d, "
+ "Sample=%d/%d, TargetTemp=%3.1lf, "
+ "ActualTemp=%4.1lf, "
+ "Offset=(%7.2lf, %7.2lf, %7.2lf), "
+ "Time=%d\n", i, j, tr.Version,
+ tr.Bin, tr.NumBins,
+ tr.Sample, tr.NumSamples,
+ tr.TargetTemperature,
+ tr.ActualTemperature,
+ tr.Offset.x, tr.Offset.y, tr.Offset.z,
+ tr.Time);
+ }
+ }
+}
+
+void SensorCalibration::DebugClearHeadsetTemperatureReports()
+{
+ OVR_ASSERT(pSensor != NULL);
+
+ bool result;
+
+ Array<Array<TemperatureReport> > temperatureReports;
+ pSensor->GetAllTemperatureReports(&temperatureReports);
+
+ OVR_ASSERT(temperatureReports.GetSize() > 0);
+ OVR_ASSERT(temperatureReports[0].GetSize() > 0);
+
+ TemperatureReport& tr = TemperatureReports[0][0];
+
+ tr.ActualTemperature = 0.0;
+ tr.Time = 0;
+ tr.Version = 0;
+ tr.Offset.x = tr.Offset.y = tr.Offset.z = 0.0;
+
+ for (UByte i = 0; i < tr.NumBins; i++)
+ {
+ tr.Bin = i;
+
+ for (UByte j = 0; j < tr.NumSamples; j++)
+ {
+ tr.Sample = j;
+
+ result = pSensor->SetTemperatureReport(tr);
+ OVR_ASSERT(result);
+
+ // Need to wait for the tracker board to finish writing to eeprom.
+ Thread::MSleep(50);
+ }
+ }
+}
+
+void SensorCalibration::Apply(MessageBodyFrame& msg)
+{
+ AutocalibrateGyro(msg);
+
+ // compute the interpolated offset
+ Vector3f gyroOffset;
+ for (int i = 0; i < 3; i++)
+ gyroOffset[i] = (float) Interpolators[i].GetOffset(msg.Temperature, GyroAutoTemperature, GyroAutoOffset[i]);
+
+ // apply calibration
+ msg.RotationRate = GyroMatrix.Transform(msg.RotationRate - gyroOffset);
+ msg.Acceleration = AccelMatrix.Transform(msg.Acceleration - AccelOffset);
+ if (MagCalibrated)
+ msg.MagneticField = MagMatrix.Transform(msg.MagneticField);
+}
+
+void SensorCalibration::AutocalibrateGyro(MessageBodyFrame const& msg)
+{
+ const float alpha = 0.4f;
+ // 1.25f is a scaling factor related to conversion from per-axis comparison to length comparison
+ const float absLimit = 1.25f * 0.349066f;
+ const float noiseLimit = 1.25f * 0.03f;
+
+ Vector3f gyro = msg.RotationRate;
+ // do a moving average to reject short term noise
+ Vector3f avg = (GyroFilter.IsEmpty()) ? gyro : gyro * alpha + GyroFilter.PeekBack() * (1 - alpha);
+
+ // Make sure the absolute value is below what is likely motion
+ // Make sure it is close enough to the current average that it is probably noise and not motion
+ if (avg.Length() >= absLimit || (avg - GyroFilter.Mean()).Length() >= noiseLimit)
+ GyroFilter.Clear();
+ GyroFilter.PushBack(avg);
+
+ // if had a reasonable number of samples already use it for the current offset
+ if (GyroFilter.GetSize() > GyroFilter.GetCapacity() / 2)
+ {
+ GyroAutoOffset = GyroFilter.Mean();
+ GyroAutoTemperature = msg.Temperature;
+ // After ~6 seconds of no motion, use the average as the new zero rate offset
+ if (GyroFilter.IsFull())
+ StoreAutoOffset();
+ }
+}
+
+void SensorCalibration::StoreAutoOffset()
+{
+ const double maxDeltaT = 2.5;
+ const double minExtraDeltaT = 0.5;
+ const UInt32 minDelay = 24 * 3600; // 1 day in seconds
+
+ // find the best bin
+ UPInt binIdx = 0;
+ for (UPInt i = 1; i < TemperatureReports.GetSize(); i++)
+ if (Abs(GyroAutoTemperature - TemperatureReports[i][0].TargetTemperature) <
+ Abs(GyroAutoTemperature - TemperatureReports[binIdx][0].TargetTemperature))
+ binIdx = i;
+
+ // find the oldest and newest samples
+ // NB: uninitialized samples have Time == 0, so they will get picked as the oldest
+ UPInt newestIdx = 0, oldestIdx = 0;
+ for (UPInt i = 1; i < TemperatureReports[binIdx].GetSize(); i++)
+ {
+ // if the version is newer - do nothing
+ if (TemperatureReports[binIdx][i].Version > VERSION)
+ return;
+ if (TemperatureReports[binIdx][i].Time > TemperatureReports[binIdx][newestIdx].Time)
+ newestIdx = i;
+ if (TemperatureReports[binIdx][i].Time < TemperatureReports[binIdx][oldestIdx].Time)
+ oldestIdx = i;
+ }
+ TemperatureReport& oldestReport = TemperatureReports[binIdx][oldestIdx];
+ TemperatureReport& newestReport = TemperatureReports[binIdx][newestIdx];
+ OVR_ASSERT((oldestReport.Sample == 0 && newestReport.Sample == 0 && newestReport.Version == 0) ||
+ oldestReport.Sample == (newestReport.Sample + 1) % newestReport.NumSamples);
+
+ bool writeSuccess = false;
+ UInt32 now = (UInt32) time(0);
+ if (now - newestReport.Time > minDelay)
+ {
+ // only write a new sample if the temperature is close enough
+ if (Abs(GyroAutoTemperature - oldestReport.TargetTemperature) < maxDeltaT)
+ {
+ oldestReport.Time = now;
+ oldestReport.ActualTemperature = GyroAutoTemperature;
+ oldestReport.Offset = (Vector3d) GyroAutoOffset;
+ oldestReport.Version = VERSION;
+ writeSuccess = pSensor->SetTemperatureReport(oldestReport);
+ OVR_ASSERT(writeSuccess);
+ }
+ }
+ else
+ {
+ // if the newest sample is too recent - _update_ it if significantly closer to the target temp
+ if (Abs(GyroAutoTemperature - newestReport.TargetTemperature) + minExtraDeltaT
+ < Abs(newestReport.ActualTemperature - newestReport.TargetTemperature))
+ {
+ // (do not update the time!)
+ newestReport.ActualTemperature = GyroAutoTemperature;
+ newestReport.Offset = (Vector3d) GyroAutoOffset;
+ newestReport.Version = VERSION;
+ writeSuccess = pSensor->SetTemperatureReport(newestReport);
+ OVR_ASSERT(writeSuccess);
+ }
+ }
+
+ // update the interpolators with the new data
+ // this is not particularly expensive call and would only happen rarely
+ // but if performance is a problem, it's possible to only recompute the data that has changed
+ if (writeSuccess)
+ for (int i = 0; i < 3; i++)
+ Interpolators[i].Initialize(TemperatureReports, i);
+}
+
+const TemperatureReport& median(const Array<TemperatureReport>& temperatureReportsBin, int coord)
+{
+ Array<double> values;
+ values.Reserve(temperatureReportsBin.GetSize());
+ for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++)
+ if (temperatureReportsBin[i].ActualTemperature != 0)
+ values.PushBack(temperatureReportsBin[i].Offset[coord]);
+ if (values.GetSize() > 0)
+ {
+ double med = Median(values);
+ // this is kind of a hack
+ for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++)
+ if (temperatureReportsBin[i].Offset[coord] == med)
+ return temperatureReportsBin[i];
+ // if we haven't found the median in the original array, something is wrong
+ OVR_DEBUG_BREAK;
+ }
+ return temperatureReportsBin[0];
+}
+
+void OffsetInterpolator::Initialize(Array<Array<TemperatureReport> > const& temperatureReports, int coord)
+{
+ int bins = (int) temperatureReports.GetSize();
+ Temperatures.Clear();
+ Temperatures.Reserve(bins);
+ Values.Clear();
+ Values.Reserve(bins);
+
+ for (int bin = 0; bin < bins; bin++)
+ {
+ OVR_ASSERT(temperatureReports[bin].GetSize() == temperatureReports[0].GetSize());
+ const TemperatureReport& report = median(temperatureReports[bin], coord);
+ if (report.Version > 0 && report.Version <= MAX_COMPAT_VERSION)
+ {
+ Temperatures.PushBack(report.ActualTemperature);
+ Values.PushBack(report.Offset[coord]);
+ }
+ }
+}
+
+double OffsetInterpolator::GetOffset(double targetTemperature, double autoTemperature, double autoValue)
+{
+ const double autoRangeExtra = 1.0;
+ const double minInterpolationDist = 0.5;
+
+ // difference between current and autocalibrated temperature adjusted for preference over historical data
+ const double adjustedDeltaT = Abs(autoTemperature - targetTemperature) - autoRangeExtra;
+
+ int count = (int) Temperatures.GetSize();
+ // handle special cases when we don't have enough data for proper interpolation
+ if (count == 0)
+ return autoValue;
+ if (count == 1)
+ {
+ if (adjustedDeltaT < Abs(Temperatures[0] - targetTemperature))
+ return autoValue;
+ else
+ return Values[0];
+ }
+
+ // first, find the interval that contains targetTemperature
+ // if all points are on the same side of targetTemperature, find the adjacent interval
+ int l;
+ if (targetTemperature < Temperatures[1])
+ l = 0;
+ else if (targetTemperature >= Temperatures[count - 2])
+ l = count - 2;
+ else
+ for (l = 1; l < count - 2; l++)
+ if (Temperatures[l] <= targetTemperature && targetTemperature < Temperatures[l+1])
+ break;
+ int u = l + 1;
+
+ // extend the interval if it's too small and the interpolation is unreliable
+ if (Temperatures[u] - Temperatures[l] < minInterpolationDist)
+ {
+ if (l > 0
+ && (u == count - 1 || Temperatures[u] - Temperatures[l - 1] < Temperatures[u + 1] - Temperatures[l]))
+ l--;
+ else if (u < count - 1)
+ u++;
+ }
+
+ // verify correctness
+ OVR_ASSERT(l >= 0 && u < count);
+ OVR_ASSERT(l == 0 || Temperatures[l] <= targetTemperature);
+ OVR_ASSERT(u == count - 1 || targetTemperature < Temperatures[u]);
+ OVR_ASSERT((l == 0 && u == count - 1) || Temperatures[u] - Temperatures[l] > minInterpolationDist);
+ OVR_ASSERT(Temperatures[l] <= Temperatures[u]);
+
+ // perform the interpolation
+ double slope;
+ if (Temperatures[u] - Temperatures[l] >= minInterpolationDist)
+ slope = (Values[u] - Values[l]) / (Temperatures[u] - Temperatures[l]);
+ else
+ // avoid a badly conditioned problem
+ slope = 0;
+ if (adjustedDeltaT < Abs(Temperatures[u] - targetTemperature))
+ // use the autocalibrated value, if it's close
+ return autoValue + slope * (targetTemperature - autoTemperature);
+ else
+ return Values[u] + slope * (targetTemperature - Temperatures[u]);
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_SensorCalibration.h b/LibOVR/Src/OVR_SensorCalibration.h
new file mode 100644
index 0000000..62883d2
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorCalibration.h
@@ -0,0 +1,82 @@
+/************************************************************************************
+
+Filename : OVR_SensorCalibration.h
+Content : Calibration data implementation for the IMU messages
+Created : January 28, 2014
+Authors : Max Katsev
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorCalibration_h
+#define OVR_SensorCalibration_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorFilter.h"
+
+namespace OVR {
+
+class OffsetInterpolator
+{
+public:
+ void Initialize(Array<Array<TemperatureReport> > const& temperatureReports, int coord);
+ double GetOffset(double targetTemperature, double autoTemperature, double autoValue);
+
+ Array<double> Temperatures;
+ Array<double> Values;
+};
+
+class SensorCalibration : public NewOverrideBase
+{
+public:
+ SensorCalibration(SensorDevice* pSensor);
+
+ // Load data from the HW and perform the necessary preprocessing
+ void Initialize();
+ // Apply the calibration
+ void Apply(MessageBodyFrame& msg);
+ // Is mag calibration available?
+ bool IsMagCalibrated() { return MagCalibrated; }
+
+protected:
+ void StoreAutoOffset();
+ void AutocalibrateGyro(MessageBodyFrame const& msg);
+
+ void DebugPrintLocalTemperatureTable();
+ void DebugClearHeadsetTemperatureReports();
+
+ SensorDevice* pSensor;
+
+ // Factory calibration data
+ bool MagCalibrated;
+ Matrix4f AccelMatrix, GyroMatrix, MagMatrix;
+ Vector3f AccelOffset;
+
+ // Temperature based data
+ Array<Array<TemperatureReport> > TemperatureReports;
+ OffsetInterpolator Interpolators[3];
+
+ // Autocalibration data
+ SensorFilterf GyroFilter;
+ Vector3f GyroAutoOffset;
+ float GyroAutoTemperature;
+};
+
+} // namespace OVR
+#endif //OVR_SensorCalibration_h
diff --git a/LibOVR/Src/OVR_SensorFilter.cpp b/LibOVR/Src/OVR_SensorFilter.cpp
new file mode 100644
index 0000000..2c660ae
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFilter.cpp
@@ -0,0 +1,99 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.cpp
+Content : Basic filtering of sensor this->Data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova, Max Katsev
+
+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_SensorFilter.h"
+
+namespace OVR {
+
+template <typename T>
+Vector3<T> SensorFilter<T>::Median() const
+{
+ Vector3<T> result;
+ T* slice = (T*) OVR_ALLOC(this->ElemCount * sizeof(T));
+
+ for (int coord = 0; coord < 3; coord++)
+ {
+ for (int i = 0; i < this->ElemCount; i++)
+ slice[i] = this->Data[i][coord];
+ result[coord] = Alg::Median(ArrayAdaptor(slice, this->ElemCount));
+ }
+
+ OVR_FREE(slice);
+ return result;
+}
+
+// Only the diagonal of the covariance matrix.
+template <typename T>
+Vector3<T> SensorFilter<T>::Variance() const
+{
+ Vector3<T> mean = this->Mean();
+ Vector3<T> total;
+ for (int i = 0; i < this->ElemCount; i++)
+ {
+ total.x += (this->Data[i].x - mean.x) * (this->Data[i].x - mean.x);
+ total.y += (this->Data[i].y - mean.y) * (this->Data[i].y - mean.y);
+ total.z += (this->Data[i].z - mean.z) * (this->Data[i].z - mean.z);
+ }
+ return total / (float) this->ElemCount;
+}
+
+template <typename T>
+Matrix3<T> SensorFilter<T>::Covariance() const
+{
+ Vector3<T> mean = this->Mean();
+ Matrix3<T> total;
+ for (int i = 0; i < this->ElemCount; i++)
+ {
+ total.M[0][0] += (this->Data[i].x - mean.x) * (this->Data[i].x - mean.x);
+ total.M[1][0] += (this->Data[i].y - mean.y) * (this->Data[i].x - mean.x);
+ total.M[2][0] += (this->Data[i].z - mean.z) * (this->Data[i].x - mean.x);
+ total.M[1][1] += (this->Data[i].y - mean.y) * (this->Data[i].y - mean.y);
+ total.M[2][1] += (this->Data[i].z - mean.z) * (this->Data[i].y - mean.y);
+ total.M[2][2] += (this->Data[i].z - mean.z) * (this->Data[i].z - mean.z);
+ }
+ total.M[0][1] = total.M[1][0];
+ total.M[0][2] = total.M[2][0];
+ total.M[1][2] = total.M[2][1];
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ total.M[i][j] /= (float) this->ElemCount;
+ return total;
+}
+
+template <typename T>
+Vector3<T> SensorFilter<T>::PearsonCoefficient() const
+{
+ Matrix3<T> cov = this->Covariance();
+ Vector3<T> pearson;
+ pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1]));
+ pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2]));
+ pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0]));
+
+ return pearson;
+}
+
+} //namespace OVR
diff --git a/LibOVR/Src/OVR_SensorFilter.h b/LibOVR/Src/OVR_SensorFilter.h
new file mode 100644
index 0000000..0805d57
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFilter.h
@@ -0,0 +1,307 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFilter.h
+Content : Basic filtering of sensor data
+Created : March 7, 2013
+Authors : Steve LaValle, Anna Yershova, Max Katsev
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFilter_h
+#define OVR_SensorFilter_h
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Deque.h"
+#include "Kernel/OVR_Alg.h"
+
+namespace OVR {
+
+// A base class for filters that maintains a buffer of sensor data taken over time and implements
+// various simple filters, most of which are linear functions of the data history.
+// Maintains the running sum of its elements for better performance on large capacity values
+template <typename T>
+class SensorFilterBase : public CircularBuffer<T>
+{
+protected:
+ T RunningTotal; // Cached sum of the elements
+
+public:
+ SensorFilterBase(int capacity = CircularBuffer<T>::DefaultCapacity)
+ : CircularBuffer<T>(capacity), RunningTotal()
+ {
+ this->Clear();
+ };
+
+ // The following methods are augmented to update the cached running sum value
+ void PushBack(const T &e)
+ {
+ CircularBuffer<T>::PushBack(e);
+ RunningTotal += e;
+ if (this->End == 0)
+ {
+ // update the cached total to avoid error accumulation
+ RunningTotal = T();
+ for (int i = 0; i < this->ElemCount; i++)
+ RunningTotal += this->Data[i];
+ }
+ }
+
+ void PushFront(const T &e)
+ {
+ CircularBuffer<T>::PushFront(e);
+ RunningTotal += e;
+ if (this->Beginning == 0)
+ {
+ // update the cached total to avoid error accumulation
+ RunningTotal = T();
+ for (int i = 0; i < this->ElemCount; i++)
+ RunningTotal += this->Data[i];
+ }
+ }
+
+ T PopBack()
+ {
+ T e = CircularBuffer<T>::PopBack();
+ RunningTotal -= e;
+ return e;
+ }
+
+ T PopFront()
+ {
+ T e = CircularBuffer<T>::PopFront();
+ RunningTotal -= e;
+ return e;
+ }
+
+ void Clear()
+ {
+ CircularBuffer<T>::Clear();
+ RunningTotal = T();
+ }
+
+ // Simple statistics
+ T Total() const
+ {
+ return RunningTotal;
+ }
+
+ T Mean() const
+ {
+ return this->IsEmpty() ? T() : (Total() / (float) this->ElemCount);
+ }
+
+ T MeanN(int n) const
+ {
+ OVR_ASSERT(n > 0);
+ OVR_ASSERT(this->Capacity >= n);
+ T total = T();
+ for (int i = 0; i < n; i++)
+ {
+ total += this->PeekBack(i);
+ }
+ return total / n;
+ }
+
+ // A popular family of smoothing filters and smoothed derivatives
+
+ T SavitzkyGolaySmooth4()
+ {
+ OVR_ASSERT(this->Capacity >= 4);
+ return this->PeekBack(0)*0.7f +
+ this->PeekBack(1)*0.4f +
+ this->PeekBack(2)*0.1f -
+ this->PeekBack(3)*0.2f;
+ }
+
+ T SavitzkyGolaySmooth8() const
+ {
+ OVR_ASSERT(this->Capacity >= 8);
+ return this->PeekBack(0)*0.41667f +
+ this->PeekBack(1)*0.33333f +
+ this->PeekBack(2)*0.25f +
+ this->PeekBack(3)*0.16667f +
+ this->PeekBack(4)*0.08333f -
+ this->PeekBack(6)*0.08333f -
+ this->PeekBack(7)*0.16667f;
+ }
+
+ T SavitzkyGolayDerivative4() const
+ {
+ OVR_ASSERT(this->Capacity >= 4);
+ return this->PeekBack(0)*0.3f +
+ this->PeekBack(1)*0.1f -
+ this->PeekBack(2)*0.1f -
+ this->PeekBack(3)*0.3f;
+ }
+
+ T SavitzkyGolayDerivative5() const
+ {
+ OVR_ASSERT(this->Capacity >= 5);
+ return this->PeekBack(0)*0.2f +
+ this->PeekBack(1)*0.1f -
+ this->PeekBack(3)*0.1f -
+ this->PeekBack(4)*0.2f;
+ }
+
+ T SavitzkyGolayDerivative12() const
+ {
+ OVR_ASSERT(this->Capacity >= 12);
+ return this->PeekBack(0)*0.03846f +
+ this->PeekBack(1)*0.03147f +
+ this->PeekBack(2)*0.02448f +
+ this->PeekBack(3)*0.01748f +
+ this->PeekBack(4)*0.01049f +
+ this->PeekBack(5)*0.0035f -
+ this->PeekBack(6)*0.0035f -
+ this->PeekBack(7)*0.01049f -
+ this->PeekBack(8)*0.01748f -
+ this->PeekBack(9)*0.02448f -
+ this->PeekBack(10)*0.03147f -
+ this->PeekBack(11)*0.03846f;
+ }
+
+ T SavitzkyGolayDerivativeN(int n) const
+ {
+ OVR_ASSERT(this->capacity >= n);
+ int m = (n-1)/2;
+ T result = T();
+ for (int k = 1; k <= m; k++)
+ {
+ int ind1 = m - k;
+ int ind2 = n - m + k - 1;
+ result += (this->PeekBack(ind1) - this->PeekBack(ind2)) * (float) k;
+ }
+ float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f));
+ result = result*coef;
+ return result;
+ }
+
+ T Median() const
+ {
+ T* copy = (T*) OVR_ALLOC(this->ElemCount * sizeof(T));
+ T result = Alg::Median(ArrayAdaptor(copy));
+ OVR_FREE(copy);
+ return result;
+ }
+};
+
+// This class maintains a buffer of sensor data taken over time and implements
+// various simple filters, most of which are linear functions of the data history.
+template <typename T>
+class SensorFilter : public SensorFilterBase<Vector3<T> >
+{
+public:
+ SensorFilter(int capacity = SensorFilterBase<Vector3<T> >::DefaultCapacity) : SensorFilterBase<Vector3<T> >(capacity) { };
+
+ // Simple statistics
+ Vector3<T> Median() const;
+ Vector3<T> Variance() const; // The diagonal of covariance matrix
+ Matrix3<T> Covariance() const;
+ Vector3<T> PearsonCoefficient() const;
+};
+
+typedef SensorFilter<float> SensorFilterf;
+typedef SensorFilter<double> SensorFilterd;
+
+// This filter operates on the values that are measured in the body frame and rotate with the device
+class SensorFilterBodyFrame : public SensorFilterBase<Vector3d>
+{
+private:
+ // low pass filter gain
+ double gain;
+ // sum of squared norms of the values
+ double runningTotalLengthSq;
+ // cumulative rotation quaternion
+ Quatd Q;
+ // current low pass filter output
+ Vector3d output;
+
+ // make private so it isn't used by accident
+ // in addition to the normal SensorFilterBase::PushBack, keeps track of running sum of LengthSq
+ // for the purpose of variance computations
+ void PushBack(const Vector3d &e)
+ {
+ runningTotalLengthSq += this->IsFull() ? (e.LengthSq() - this->PeekFront().LengthSq()) : e.LengthSq();
+ SensorFilterBase<Vector3d>::PushBack(e);
+ if (this->End == 0)
+ {
+ // update the cached total to avoid error accumulation
+ runningTotalLengthSq = 0;
+ for (int i = 0; i < this->ElemCount; i++)
+ runningTotalLengthSq += this->Data[i].LengthSq();
+ }
+ }
+
+public:
+ SensorFilterBodyFrame(int capacity = SensorFilterBase<Vector3d>::DefaultCapacity)
+ : SensorFilterBase<Vector3d>(capacity), gain(2.5),
+ runningTotalLengthSq(0), Q(), output() { };
+
+ // return the scalar variance of the filter values (rotated to be in the same frame)
+ double Variance() const
+ {
+ return this->IsEmpty() ? 0 : (runningTotalLengthSq / this->ElemCount - this->Mean().LengthSq());
+ }
+
+ // return the scalar standard deviation of the filter values (rotated to be in the same frame)
+ double StdDev() const
+ {
+ return sqrt(Variance());
+ }
+
+ // confidence value based on the stddev of the data (between 0.0 and 1.0, more is better)
+ double Confidence() const
+ {
+ return Alg::Clamp(0.48 - 0.1 * log(StdDev()), 0.0, 1.0) * this->ElemCount / this->Capacity;
+ }
+
+ // add a new element to the filter
+ // takes rotation increment since the last update
+ // in order to rotate the previous value to the current body frame
+ void Update(Vector3d value, double deltaT, Quatd deltaQ = Quatd())
+ {
+ if (this->IsEmpty())
+ {
+ output = value;
+ }
+ else
+ {
+ // rotate by deltaQ
+ output = deltaQ.Inverted().Rotate(output);
+ // apply low-pass filter
+ output += (value - output) * gain * deltaT;
+ }
+
+ // put the value into the fixed frame for the stddev computation
+ Q = Q * deltaQ;
+ PushBack(Q.Rotate(output));
+ }
+
+ // returns the filter average in the current body frame
+ Vector3d GetFilteredValue() const
+ {
+ return Q.Inverted().Rotate(this->Mean());
+ }
+};
+
+} //namespace OVR
+
+#endif // OVR_SensorFilter_h
diff --git a/LibOVR/Src/OVR_SensorFusion.cpp b/LibOVR/Src/OVR_SensorFusion.cpp
new file mode 100644
index 0000000..5c21178
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusion.cpp
@@ -0,0 +1,904 @@
+/************************************************************************************
+
+Filename : OVR_SensorFusion.cpp
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle, Dov Katz, Max Katsev, Dan Gierl
+
+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_SensorFusion.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_System.h"
+#include "OVR_JSON.h"
+#include "OVR_Profile.h"
+#include "OVR_Stereo.h"
+#include "OVR_Recording.h"
+
+// Temporary for debugging
+bool Global_Flag_1 = true;
+
+//Convenient global variables to temporarily extract this data.
+float TPH_CameraPoseOrientationWxyz[4];
+double TPH_CameraPoseConfidence;
+double TPH_CameraPoseConfidenceThresholdOverrideIfNonZero = 0;
+bool TPH_IsPositionTracked = false;
+
+
+namespace OVR {
+
+const Transformd DefaultWorldFromCamera(Quatd(), Vector3d(0, 0, -1));
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor Fusion
+
+SensorFusion::SensorFusion(SensorDevice* sensor)
+ : ExposureRecordHistory(100), LastMessageExposureFrame(NULL),
+ FocusDirection(Vector3d(0, 0, 0)), FocusFOV(0.0),
+ FAccelInImuFrame(1000), FAccelInCameraFrame(1000), FAngV(20),
+ EnableGravity(true), EnableYawCorrection(true), MagCalibrated(false),
+ EnableCameraTiltCorrection(true),
+ MotionTrackingEnabled(true), VisionPositionEnabled(true),
+ CenterPupilDepth(0.0)
+{
+ pHandler = new BodyFrameHandler(this);
+
+ // And the clock is running...
+ LogText("*** SensorFusion Startup: TimeSeconds = %f\n", Timer::GetSeconds());
+
+ if (sensor)
+ AttachToSensor(sensor);
+
+ Reset();
+}
+
+SensorFusion::~SensorFusion()
+{
+ delete(pHandler);
+}
+
+bool SensorFusion::AttachToSensor(SensorDevice* sensor)
+{
+ pHandler->RemoveHandlerFromDevices();
+ Reset();
+
+ if (sensor != NULL)
+ {
+ // cache mag calibration state
+ MagCalibrated = sensor->IsMagCalibrated();
+
+ // Load IMU position
+ Array<PositionCalibrationReport> reports;
+ bool result = sensor->GetAllPositionCalibrationReports(&reports);
+ if (result)
+ {
+ PositionCalibrationReport imu = reports[reports.GetSize() - 1];
+ OVR_ASSERT(imu.PositionType == PositionCalibrationReport::PositionType_IMU);
+ // convert from vision to the world frame
+ // TBD convert rotation as necessary?
+ imu.Position.x *= -1.0;
+ imu.Position.z *= -1.0;
+
+ ImuFromScreen = Transformd(Quatd(imu.Normal, imu.Angle), imu.Position).Inverted();
+
+ Recording::GetRecorder().RecordLedPositions(reports);
+ Recording::GetRecorder().RecordDeviceIfcVersion(sensor->GetDeviceInterfaceVersion());
+ }
+
+ // Repopulate CPFOrigin
+ SetCenterPupilDepth(CenterPupilDepth);
+
+ // Subscribe to sensor updates
+ sensor->AddMessageHandler(pHandler);
+
+ // Initialize the sensor state
+ // TBD: This is a hack to avoid a race condition if sensor status is checked immediately
+ // after sensor creation but before any data has flowed through. We should probably
+ // not depend strictly on data flow to determine capabilities like orientation and position
+ // tracking, or else use some sort of synchronous method to wait for data
+ LocklessState init;
+ init.StatusFlags = Status_OrientationTracked;
+ UpdatedState.SetState(init);
+ }
+
+ return true;
+}
+
+// Resets the current orientation
+void SensorFusion::Reset()
+{
+ Lock::Locker lockScope(pHandler->GetHandlerLock());
+
+ UpdatedState.SetState(LocklessState());
+ WorldFromImu = PoseState<double>();
+ WorldFromImu.Pose = ImuFromCpf.Inverted(); // place CPF at the origin, not the IMU
+ CameraFromImu = PoseState<double>();
+ VisionError = PoseState<double>();
+ WorldFromCamera = DefaultWorldFromCamera;
+ WorldFromCameraConfidence = -1;
+
+ ExposureRecordHistory.Clear();
+ NextExposureRecord = ExposureRecord();
+ LastMessageExposureFrame = MessageExposureFrame(NULL);
+ LastVisionAbsoluteTime = 0;
+ Stage = 0;
+
+ MagRefs.Clear();
+ MagRefIdx = -1;
+ MagCorrectionIntegralTerm = Quatd();
+ AccelOffset = Vector3d();
+
+ FAccelInCameraFrame.Clear();
+ FAccelInImuFrame.Clear();
+ FAngV.Clear();
+
+ setNeckPivotFromPose ( WorldFromImu.Pose );
+}
+
+//-------------------------------------------------------------------------------------
+// Vision & message processing
+
+void SensorFusion::OnVisionFailure()
+{
+ // do nothing
+ Recording::GetRecorder().RecordVisionSuccess(false);
+}
+
+void SensorFusion::OnVisionPreviousFrame(const Transform<double>& cameraFromImu)
+{
+ // simply save the observation for use in the next OnVisionSuccess call;
+ // this should not have unintended side-effects for position filtering,
+ // since the vision time is not updated and the system keeps thinking we don't have vision yet
+ CameraFromImu.Pose = cameraFromImu;
+}
+
+void SensorFusion::OnVisionSuccess(const Transform<double>& cameraFromImu, UInt32 exposureCounter)
+{
+ Lock::Locker lockScope(pHandler->GetHandlerLock());
+
+ Recording::GetRecorder().RecordVisionSuccess(true);
+
+ LastVisionAbsoluteTime = GetTime();
+
+ // ********* LastVisionExposureRecord *********
+
+ // Skip old data and use the record that matches the exposure counter
+ while (!ExposureRecordHistory.IsEmpty() &&
+ (ExposureRecordHistory.PeekFront().ExposureCounter <= exposureCounter))
+ {
+ LastVisionExposureRecord = ExposureRecordHistory.PopFront();
+ }
+
+ // Use current values if we don't have historical data
+ // Right now, this will happen if we get first frame after prediction failure,
+ // and this exposure wasn't in the buffer. (TBD: Unlikely.. unless IMU message wasn't sent?)
+ if (LastVisionExposureRecord.ExposureCounter != exposureCounter)
+ LastVisionExposureRecord = ExposureRecord(exposureCounter, GetTime(), WorldFromImu, PoseState<double>());
+
+ // ********* CameraFromImu *********
+
+ // This is stored in the camera frame, so need to be careful when combining with the IMU data,
+ // which is in the world frame
+
+ Transformd cameraFromImuPrev = CameraFromImu.Pose;
+ CameraFromImu.Pose = cameraFromImu;
+ CameraFromImu.TimeInSeconds = LastVisionExposureRecord.ExposureTime;
+
+ // Check LastVisionExposureRecord.Delta.TimeInSeconds to avoid divide by zero, which we could (rarely)
+ // get if we didn't have exposures delta for history (skipped exposure counters
+ // due to video mode change that stalls USB, etc).
+ if (LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds > 0.001)
+ {
+ Vector3d visionVelocityInImuFrame = (cameraFromImu.Translation - cameraFromImuPrev.Translation) /
+ LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds;
+ // Use the accel data to estimate the velocity at the exposure time
+ // (as opposed to the average velocity between exposures)
+ Vector3d imuVelocityInWorldFrame = LastVisionExposureRecord.ImuOnlyDelta.LinearVelocity -
+ LastVisionExposureRecord.ImuOnlyDelta.Pose.Translation / LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds;
+ CameraFromImu.LinearVelocity = visionVelocityInImuFrame +
+ WorldFromCamera.Inverted().Rotate(imuVelocityInWorldFrame);
+ }
+ else
+ {
+ CameraFromImu.LinearVelocity = Vector3d(0,0,0);
+ }
+}
+
+PoseStated SensorFusion::computeVisionError()
+{
+ PoseStated worldFromImuVision = WorldFromCamera * CameraFromImu;
+ // Here we need to compute the difference between worldFromImuVision and WorldFromImu.
+ // However this difference needs to be represented in the World frame, not IMU frame.
+ // Therefore the computation is different from simply worldFromImuVision.Pose * WorldFromImu.Pose.Inverted().
+ PoseStated err;
+ err.Pose.Rotation = worldFromImuVision.Pose.Rotation *
+ LastVisionExposureRecord.WorldFromImu.Pose.Rotation.Inverted();
+ err.Pose.Translation = worldFromImuVision.Pose.Translation -
+ LastVisionExposureRecord.WorldFromImu.Pose.Translation;
+ err.LinearVelocity = worldFromImuVision.LinearVelocity -
+ LastVisionExposureRecord.WorldFromImu.LinearVelocity;
+ return err;
+}
+
+Transform<double> SensorFusion::GetVisionPrediction(UInt32 exposureCounter)
+{
+ Lock::Locker lockScope(pHandler->GetHandlerLock());
+
+ // Combine the small deltas together
+ // Should only be one iteration, unless we are skipping camera frames
+ ExposureRecord record;
+ PoseState<double> delta = PoseState<double>();
+
+ while (!ExposureRecordHistory.IsEmpty() &&
+ (ExposureRecordHistory.PeekFront().ExposureCounter <= exposureCounter))
+ {
+ record = ExposureRecordHistory.PopFront();
+ delta.AdvanceByDelta(record.ImuOnlyDelta);
+ }
+ // Put the combine exposure record back in the history, for use in HandleVisionSuccess(...)
+ record.ImuOnlyDelta = delta;
+ ExposureRecordHistory.PushFront(record);
+
+ Transformd result;
+ if (record.VisionTrackingAvailable)
+ {
+ // if the tracking is working normally, use the change in the main state (SFusion output)
+ // to compute the prediction
+ result = CameraFromImu.Pose *
+ LastVisionExposureRecord.WorldFromImu.Pose.Inverted() * record.WorldFromImu.Pose;
+ }
+ else
+ {
+ // if we just acquired vision, the main state probably doesn't have the correct position,
+ // so can't rely on it for prediction
+
+ // solution: use the accelerometer and vision velocity to propagate the previous sample forward
+ // (don't forget to transform IMU to the camera frame)
+ result = Transform<double>
+ (
+ CameraFromImu.Pose.Rotation * delta.Pose.Rotation,
+ CameraFromImu.Pose.Translation + CameraFromImu.LinearVelocity * delta.TimeInSeconds +
+ WorldFromCamera.Inverted().Rotate(delta.Pose.Translation)
+ );
+ }
+
+ return result;
+}
+
+void SensorFusion::handleMessage(const MessageBodyFrame& msg)
+{
+ if (msg.Type != Message_BodyFrame || !IsMotionTrackingEnabled())
+ return;
+
+ // Put the sensor readings into convenient local variables
+ Vector3d gyro(msg.RotationRate);
+ Vector3d accel(msg.Acceleration);
+ Vector3d mag(msg.MagneticField);
+ double DeltaT = msg.TimeDelta;
+
+ // Keep track of time
+ WorldFromImu.TimeInSeconds = msg.AbsoluteTimeSeconds;
+ // We got an update in the last 60ms and the data is not very old
+ bool visionIsRecent = (GetTime() - LastVisionAbsoluteTime < 0.07) && (GetVisionLatency() < 0.25);
+ Stage++;
+
+ // Insert current sensor data into filter history
+ FAngV.PushBack(gyro);
+ FAccelInImuFrame.Update(accel, DeltaT, Quatd(gyro, gyro.Length() * DeltaT));
+
+ // Process raw inputs
+ // in the future the gravity offset can be calibrated using vision feedback
+ Vector3d accelInWorldFrame = WorldFromImu.Pose.Rotate(accel) - Vector3d(0, 9.8, 0);
+
+ // Recompute the vision error to account for all the corrections and the new data
+ VisionError = computeVisionError();
+
+ // Update headset orientation
+ WorldFromImu.StoreAndIntegrateGyro(gyro, DeltaT);
+ // Tilt correction based on accelerometer
+ if (EnableGravity)
+ applyTiltCorrection(DeltaT);
+ // Yaw correction based on camera
+ if (EnableYawCorrection && visionIsRecent)
+ applyVisionYawCorrection(DeltaT);
+ // Yaw correction based on magnetometer
+ if (EnableYawCorrection && MagCalibrated) // MagCalibrated is always false for DK2 for now
+ applyMagYawCorrection(mag, DeltaT);
+ // Focus Correction
+ if ((FocusDirection.x != 0.0f || FocusDirection.z != 0.0f) && FocusFOV < Mathf::Pi)
+ applyFocusCorrection(DeltaT);
+
+ // Update camera orientation
+ if (EnableCameraTiltCorrection && visionIsRecent)
+ applyCameraTiltCorrection(accel, DeltaT);
+
+ // The quaternion magnitude may slowly drift due to numerical error,
+ // so it is periodically normalized.
+ if ((Stage & 0xFF) == 0)
+ {
+ WorldFromImu.Pose.Rotation.Normalize();
+ WorldFromCamera.Rotation.Normalize();
+ }
+
+ // Update headset position
+ if (VisionPositionEnabled && visionIsRecent)
+ {
+ // Integrate UMI and velocity here up to a fixed amount of time after vision.
+ WorldFromImu.StoreAndIntegrateAccelerometer(accelInWorldFrame + AccelOffset, DeltaT);
+ // Position correction based on camera
+ applyPositionCorrection(DeltaT);
+ // Compute where the neck pivot would be.
+ setNeckPivotFromPose(WorldFromImu.Pose);
+ }
+ else
+ {
+ // Fall back onto internal head model
+ // Use the last-known neck pivot position to figure out the expected IMU position.
+ // (should be the opposite of SensorFusion::setNeckPivotFromPose)
+ WorldFromNeck.Rotation = WorldFromImu.Pose.Rotation;
+ WorldFromImu.Pose = WorldFromNeck * (ImuFromCpf * CpfFromNeck).Inverted();
+
+ // We can't trust velocity past this point.
+ WorldFromImu.LinearVelocity = Vector3d(0,0,0);
+ WorldFromImu.LinearAcceleration = accelInWorldFrame;
+ }
+
+ // Compute the angular acceleration
+ WorldFromImu.AngularAcceleration = (FAngV.GetSize() >= 12 && DeltaT > 0) ?
+ (FAngV.SavitzkyGolayDerivative12() / DeltaT) : Vector3d();
+
+ // Update the dead reckoning state used for incremental vision tracking
+ NextExposureRecord.ImuOnlyDelta.StoreAndIntegrateGyro(gyro, DeltaT);
+ NextExposureRecord.ImuOnlyDelta.StoreAndIntegrateAccelerometer(accelInWorldFrame, DeltaT);
+ NextExposureRecord.ImuOnlyDelta.TimeInSeconds = WorldFromImu.TimeInSeconds - LastMessageExposureFrame.CameraTimeSeconds;
+ NextExposureRecord.VisionTrackingAvailable &= (VisionPositionEnabled && visionIsRecent);
+
+ Recording::GetRecorder().LogData("sfTimeSeconds", WorldFromImu.TimeInSeconds);
+ Recording::GetRecorder().LogData("sfStage", (double)Stage);
+ Recording::GetRecorder().LogData("sfPose", WorldFromImu.Pose);
+ //Recorder::LogData("sfAngAcc", State.AngularAcceleration);
+ //Recorder::LogData("sfAngVel", State.AngularVelocity);
+ //Recorder::LogData("sfLinAcc", State.LinearAcceleration);
+ //Recorder::LogData("sfLinVel", State.LinearVelocity);
+
+ // Store the lockless state.
+ LocklessState lstate;
+ lstate.StatusFlags = Status_OrientationTracked;
+ if (VisionPositionEnabled)
+ lstate.StatusFlags |= Status_PositionConnected;
+ if (VisionPositionEnabled && visionIsRecent)
+ lstate.StatusFlags |= Status_PositionTracked;
+
+ //A convenient means to temporarily extract this flag
+ TPH_IsPositionTracked = visionIsRecent;
+
+ lstate.State = WorldFromImu;
+ lstate.Temperature = msg.Temperature;
+ lstate.Magnetometer = mag;
+ UpdatedState.SetState(lstate);
+}
+
+void SensorFusion::handleExposure(const MessageExposureFrame& msg)
+{
+ NextExposureRecord.ExposureCounter = msg.CameraFrameCount;
+ NextExposureRecord.ExposureTime = msg.CameraTimeSeconds;
+ NextExposureRecord.WorldFromImu = WorldFromImu;
+ NextExposureRecord.ImuOnlyDelta.TimeInSeconds = msg.CameraTimeSeconds - LastMessageExposureFrame.CameraTimeSeconds;
+ ExposureRecordHistory.PushBack(NextExposureRecord);
+
+ // Every new exposure starts from zero
+ NextExposureRecord = ExposureRecord();
+ LastMessageExposureFrame = msg;
+}
+
+// If you have a known-good pose, this sets the neck pivot position.
+void SensorFusion::setNeckPivotFromPose(Transformd const &worldFromImu)
+{
+ WorldFromNeck = worldFromImu * ImuFromCpf * CpfFromNeck;
+}
+
+// These two functions need to be moved into Quat class
+// Compute a rotation required to transform "from" into "to".
+Quatd vectorAlignmentRotation(const Vector3d &from, const Vector3d &to)
+{
+ Vector3d axis = from.Cross(to);
+ if (axis.LengthSq() == 0)
+ // this handles both collinear and zero-length input cases
+ return Quatd();
+ double angle = from.Angle(to);
+ return Quatd(axis, angle);
+}
+
+// Compute the part of the quaternion that rotates around Y axis
+Quatd extractYawRotation(const Quatd &error)
+{
+ if (error.y == 0)
+ return Quatd();
+ double phi = atan2(error.w, error.y);
+ double alpha = Mathd::Pi - 2 * phi;
+ return Quatd(Axis_Y, alpha);
+}
+
+void SensorFusion::applyPositionCorrection(double deltaT)
+{
+ // Each component of gainPos is equivalent to a Kalman gain of (sigma_process / sigma_observation)
+ const Vector3d gainPos = Vector3d(10, 10, 8);
+ const Vector3d gainVel = gainPos.EntrywiseMultiply(gainPos) * 0.5;
+ const Vector3d gainAccel = gainVel * 0.5;
+ const double snapThreshold = 0.1; // Large value (previously 0.01, which caused frequent jumping)
+
+ Vector3d correctionPos, correctionVel;
+ if (VisionError.Pose.Translation.LengthSq() > (snapThreshold * snapThreshold) ||
+ !(UpdatedState.GetState().StatusFlags & Status_PositionTracked))
+ {
+ // high error or just reacquired position from vision - apply full correction
+
+ // to know where we are right now, take the vision pose (which is slightly old)
+ // and update it using the imu data since then
+ PoseStated worldFromImuVision = WorldFromCamera * CameraFromImu;
+ for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++)
+ worldFromImuVision.AdvanceByDelta(ExposureRecordHistory.PeekFront(i).ImuOnlyDelta);
+ worldFromImuVision.AdvanceByDelta(NextExposureRecord.ImuOnlyDelta);
+
+ correctionPos = worldFromImuVision.Pose.Translation - WorldFromImu.Pose.Translation;
+ correctionVel = worldFromImuVision.LinearVelocity - WorldFromImu.LinearVelocity;
+ AccelOffset = Vector3d();
+ }
+ else
+ {
+ correctionPos = VisionError.Pose.Translation.EntrywiseMultiply(gainPos) * deltaT;
+ correctionVel = VisionError.Pose.Translation.EntrywiseMultiply(gainVel) * deltaT;
+ AccelOffset += VisionError.Pose.Translation.EntrywiseMultiply(gainAccel) * deltaT;
+ }
+
+ WorldFromImu.Pose.Translation += correctionPos;
+ WorldFromImu.LinearVelocity += correctionVel;
+
+ // Update the exposure records so that we don't apply the same correction twice
+ LastVisionExposureRecord.WorldFromImu.Pose.Translation += correctionPos;
+ LastVisionExposureRecord.WorldFromImu.LinearVelocity += correctionVel;
+ for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++)
+ {
+ PoseStated& state = ExposureRecordHistory.PeekBack(i).WorldFromImu;
+ state.Pose.Translation += correctionPos;
+ state.LinearVelocity += correctionVel;
+ }
+}
+
+void SensorFusion::applyVisionYawCorrection(double deltaT)
+{
+ const double gain = 0.25;
+ const double snapThreshold = 0.1;
+
+ Quatd yawError = extractYawRotation(VisionError.Pose.Rotation);
+
+ Quatd correction;
+ if (Alg::Abs(yawError.w) < cos(snapThreshold / 2)) // angle(yawError) > snapThreshold
+ // high error, jump to the vision position
+ correction = yawError;
+ else
+ correction = yawError.Nlerp(Quatd(), gain * deltaT);
+
+ WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation;
+
+ // Update the exposure records so that we don't apply the same correction twice
+ LastVisionExposureRecord.WorldFromImu.Pose.Rotation = correction * LastVisionExposureRecord.WorldFromImu.Pose.Rotation;
+ for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++)
+ {
+ PoseStated& state = ExposureRecordHistory.PeekBack(i).WorldFromImu;
+ state.Pose.Rotation = correction * state.Pose.Rotation;
+ }
+}
+
+void SensorFusion::applyMagYawCorrection(Vector3d mag, double deltaT)
+{
+ const double minMagLengthSq = Mathd::Tolerance; // need to use a real value to discard very weak fields
+ const double maxMagRefDist = 0.1;
+ const double maxTiltError = 0.05;
+ const double proportionalGain = 0.01;
+ const double integralGain = 0.0005;
+
+ Vector3d magInWorldFrame = WorldFromImu.Pose.Rotate(mag);
+ // verify that the horizontal component is sufficient
+ if (magInWorldFrame.x * magInWorldFrame.x + magInWorldFrame.z * magInWorldFrame.z < minMagLengthSq)
+ return;
+ magInWorldFrame.Normalize();
+
+ // Delete a bad point
+ if (MagRefIdx >= 0 && MagRefs[MagRefIdx].Score < 0)
+ {
+ MagRefs.RemoveAtUnordered(MagRefIdx);
+ MagRefIdx = -1;
+ }
+
+ // Update the reference point if needed
+ if (MagRefIdx < 0 || mag.Distance(MagRefs[MagRefIdx].InImuFrame) > maxMagRefDist)
+ {
+ // Find a new one
+ MagRefIdx = -1;
+ double bestDist = maxMagRefDist;
+ for (unsigned int i = 0; i < MagRefs.GetSize(); i++)
+ {
+ double dist = mag.Distance(MagRefs[i].InImuFrame);
+ if (bestDist > dist)
+ {
+ bestDist = dist;
+ MagRefIdx = i;
+ }
+ }
+
+ // Create one if needed
+ if (MagRefIdx < 0 && MagRefs.GetSize() < MagMaxReferences)
+ {
+ MagRefs.PushBack(MagReferencePoint(mag, WorldFromImu.Pose, 1000));
+ }
+ }
+
+ if (MagRefIdx >= 0)
+ {
+ Vector3d magRefInWorldFrame = MagRefs[MagRefIdx].WorldFromImu.Rotate(MagRefs[MagRefIdx].InImuFrame);
+ magRefInWorldFrame.Normalize();
+
+ // If the vertical angle is wrong, decrease the score and do nothing
+ if (Alg::Abs(magRefInWorldFrame.y - magInWorldFrame.y) > maxTiltError)
+ {
+ MagRefs[MagRefIdx].Score -= 1;
+ return;
+ }
+
+ MagRefs[MagRefIdx].Score += 2;
+#if 0
+ // this doesn't seem to work properly, need to investigate
+ Quatd error = vectorAlignmentRotation(magW, magRefW);
+ Quatd yawError = extractYawRotation(error);
+#else
+ // Correction is computed in the horizontal plane
+ magInWorldFrame.y = magRefInWorldFrame.y = 0;
+ Quatd yawError = vectorAlignmentRotation(magInWorldFrame, magRefInWorldFrame);
+#endif
+ Quatd correction = yawError.Nlerp(Quatd(), proportionalGain * deltaT) *
+ MagCorrectionIntegralTerm.Nlerp(Quatd(), deltaT);
+ MagCorrectionIntegralTerm = MagCorrectionIntegralTerm * yawError.Nlerp(Quatd(), integralGain * deltaT);
+
+ WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation;
+ }
+}
+
+void SensorFusion::applyTiltCorrection(double deltaT)
+{
+ const double gain = 0.25;
+ const double snapThreshold = 0.1;
+ const Vector3d up(0, 1, 0);
+
+ Vector3d accelInWorldFrame = WorldFromImu.Pose.Rotate(FAccelInImuFrame.GetFilteredValue());
+ Quatd error = vectorAlignmentRotation(accelInWorldFrame, up);
+
+ Quatd correction;
+ if (FAccelInImuFrame.GetSize() == 1 ||
+ ((Alg::Abs(error.w) < cos(snapThreshold / 2) && FAccelInImuFrame.Confidence() > 0.75)))
+ // full correction for start-up
+ // or large error with high confidence
+ correction = error;
+ else if (FAccelInImuFrame.Confidence() > 0.5)
+ correction = error.Nlerp(Quatd(), gain * deltaT);
+ else
+ // accelerometer is unreliable due to movement
+ return;
+
+ WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation;
+}
+
+void SensorFusion::applyCameraTiltCorrection(Vector3d accel, double deltaT)
+{
+ const double snapThreshold = 0.02; // in radians
+ const double maxCameraPositionOffset = 0.2;
+ const Vector3d up(0, 1, 0), forward(0, 0, -1);
+
+ // for startup use filtered value instead of instantaneous for stability
+ if (FAccelInCameraFrame.IsEmpty())
+ accel = FAccelInImuFrame.GetFilteredValue();
+
+ Transformd cameraFromImu = WorldFromCamera.Inverted() * VisionError.Pose * WorldFromImu.Pose;
+ // this is what the hypothetical camera-mounted accelerometer would show
+ Vector3d accelInCameraFrame = cameraFromImu.Rotate(accel);
+ FAccelInCameraFrame.Update(accelInCameraFrame, deltaT);
+ Vector3d cameraAccelInWorldFrame = WorldFromCamera.Rotate(FAccelInCameraFrame.GetFilteredValue());
+
+ Quatd error1 = vectorAlignmentRotation(cameraAccelInWorldFrame, up);
+ // cancel out yaw rotation
+ Vector3d forwardCamera = (error1 * WorldFromCamera.Rotation).Rotate(forward);
+ forwardCamera.y = 0;
+ Quatd error2 = vectorAlignmentRotation(forwardCamera, forward);
+ // combined error
+ Quatd error = error2 * error1;
+
+ double confidence = FAccelInCameraFrame.Confidence();
+ // penalize the confidence if looking away from the camera
+ // TODO: smooth fall-off
+ if (CameraFromImu.Pose.Rotate(forward).Angle(forward) > 1)
+ confidence *= 0.5;
+
+ //Convenient global variable to temporarily extract this data.
+ TPH_CameraPoseConfidence = confidence;
+ //Allow override of confidence threshold
+ double confidenceThreshold = 0.75f;
+ if (TPH_CameraPoseConfidenceThresholdOverrideIfNonZero)
+ {
+ confidenceThreshold = TPH_CameraPoseConfidenceThresholdOverrideIfNonZero;
+ }
+
+ Quatd correction;
+ if (FAccelInCameraFrame.GetSize() == 1 ||
+ confidence > WorldFromCameraConfidence + 0.2 ||
+ // disabled due to false positives when moving side to side
+// (Alg::Abs(error.w) < cos(5 * snapThreshold / 2) && confidence > 0.55) ||
+ (Alg::Abs(error.w) < cos(snapThreshold / 2) && confidence > confidenceThreshold))
+ {
+ // large error with high confidence
+ correction = error;
+ // update the confidence level
+ WorldFromCameraConfidence = confidence;
+ }
+ else
+ {
+ // accelerometer is unreliable due to movement
+ return;
+ }
+
+ Transformd newWorldFromCamera(correction * WorldFromCamera.Rotation, Vector3d());
+
+ // compute a camera position change that together with the camera rotation would result in zero player movement
+ newWorldFromCamera.Translation += (WorldFromCamera * CameraFromImu.Pose).Translation -
+ (newWorldFromCamera * CameraFromImu.Pose).Translation;
+ // if the new position is too far, reset to default
+ // (can't hide the rotation, might as well use it to reset the position)
+ if (newWorldFromCamera.Translation.DistanceSq(DefaultWorldFromCamera.Translation) > maxCameraPositionOffset * maxCameraPositionOffset)
+ newWorldFromCamera.Translation = DefaultWorldFromCamera.Translation;
+
+ WorldFromCamera = newWorldFromCamera;
+
+ //Convenient global variable to temporarily extract this data.
+ TPH_CameraPoseOrientationWxyz[0] = (float) WorldFromCamera.Rotation.w;
+ TPH_CameraPoseOrientationWxyz[1] = (float) WorldFromCamera.Rotation.x;
+ TPH_CameraPoseOrientationWxyz[2] = (float) WorldFromCamera.Rotation.y;
+ TPH_CameraPoseOrientationWxyz[3] = (float) WorldFromCamera.Rotation.z;
+}
+
+void SensorFusion::applyFocusCorrection(double deltaT)
+{
+ Vector3d up = Vector3d(0, 1, 0);
+ double gain = 0.01;
+ Vector3d currentDir = WorldFromImu.Pose.Rotate(Vector3d(0, 0, 1));
+
+ Vector3d focusYawComponent = FocusDirection.ProjectToPlane(up);
+ Vector3d currentYawComponent = currentDir.ProjectToPlane(up);
+
+ double angle = focusYawComponent.Angle(currentYawComponent);
+
+ if( angle > FocusFOV )
+ {
+ Quatd yawError;
+ if ( FocusFOV != 0.0f)
+ {
+ Vector3d lFocus = Quatd(up, -FocusFOV).Rotate(focusYawComponent);
+ Vector3d rFocus = Quatd(up, FocusFOV).Rotate(focusYawComponent);
+ double lAngle = lFocus.Angle(currentYawComponent);
+ double rAngle = rFocus.Angle(currentYawComponent);
+ if(lAngle < rAngle)
+ {
+ yawError = vectorAlignmentRotation(currentDir, lFocus);
+ }
+ else
+ {
+ yawError = vectorAlignmentRotation(currentDir, rFocus);
+ }
+ }
+ else
+ {
+ yawError = vectorAlignmentRotation(currentYawComponent, focusYawComponent);
+ }
+
+ Quatd correction = yawError.Nlerp(Quatd(), gain * deltaT);
+ WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation;
+ }
+}
+
+//------------------------------------------------------------------------------------
+// Focus filter setting functions
+
+void SensorFusion::SetFocusDirection()
+{
+ SetFocusDirection(WorldFromImu.Pose.Rotate(Vector3d(0.0, 0.0, 1.0)));
+}
+
+void SensorFusion::SetFocusDirection(Vector3d direction)
+{
+ FocusDirection = direction;
+}
+
+void SensorFusion::SetFocusFOV(double fov)
+{
+ OVR_ASSERT(fov >= 0.0);
+ FocusFOV = fov;
+}
+
+void SensorFusion::ClearFocus()
+{
+ FocusDirection = Vector3d(0.0, 0.0, 0.0);
+ FocusFOV = 0.0f;
+}
+
+//-------------------------------------------------------------------------------------
+// Head model functions.
+
+// Sets up head-and-neck model and device-to-pupil dimensions from the user's profile.
+void SensorFusion::SetUserHeadDimensions(Profile const &profile, HmdRenderInfo const &hmdRenderInfo)
+{
+ float neckeye[2];
+ int count = profile.GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);
+ // Make sure these are vaguely sensible values.
+ if (count == 2)
+ {
+ OVR_ASSERT ( ( neckeye[0] > 0.05f ) && ( neckeye[0] < 0.5f ) );
+ OVR_ASSERT ( ( neckeye[1] > 0.05f ) && ( neckeye[1] < 0.5f ) );
+ SetHeadModel ( Vector3f ( 0.0, neckeye[1], -neckeye[0] ) );
+ }
+
+ // Find the distance from the center of the screen to the "center eye"
+ // This center eye is used by systems like rendering & audio to represent the player,
+ // and they will handle the offsets needed from there to each actual eye.
+
+ // HACK HACK HACK
+ // We know for DK1 the screen->lens surface distance is roughly 0.049f, and that the faceplate->lens is 0.02357f.
+ // We're going to assume(!!!!) that all HMDs have the same screen->faceplate distance.
+ // Crystal Cove was measured to be roughly 0.025 screen->faceplate which agrees with this assumption.
+ // TODO: do this properly! Update: Measured this at 0.02733 with a CC prototype, CES era (PT7), on 2/19/14 -Steve
+ float screenCenterToMidplate = 0.02733f;
+ float centerEyeRelief = hmdRenderInfo.GetEyeCenter().ReliefInMeters;
+ float centerPupilDepth = screenCenterToMidplate + hmdRenderInfo.LensSurfaceToMidplateInMeters + centerEyeRelief;
+ SetCenterPupilDepth ( centerPupilDepth );
+
+ Recording::GetRecorder().RecordUserParams(GetHeadModel(), GetCenterPupilDepth());
+}
+
+Vector3f SensorFusion::GetHeadModel() const
+{
+ return (Vector3f)CpfFromNeck.Inverted().Translation;
+}
+
+void SensorFusion::SetHeadModel(const Vector3f &headModel, bool resetNeckPivot /*= true*/ )
+{
+ Lock::Locker lockScope(pHandler->GetHandlerLock());
+ // The head model should look something like (0, 0.12, -0.12), so
+ // these asserts are to try to prevent sign problems, as
+ // they can be subtle but nauseating!
+ OVR_ASSERT ( headModel.y > 0.0f );
+ OVR_ASSERT ( headModel.z < 0.0f );
+ CpfFromNeck = Transformd(Quatd(), (Vector3d)headModel).Inverted();
+ if ( resetNeckPivot )
+ {
+ setNeckPivotFromPose ( WorldFromImu.Pose );
+ }
+}
+
+float SensorFusion::GetCenterPupilDepth() const
+{
+ return CenterPupilDepth;
+}
+
+void SensorFusion::SetCenterPupilDepth(float centerPupilDepth)
+{
+ CenterPupilDepth = centerPupilDepth;
+
+ Transformd screenFromCpf(Quatd(), Vector3d(0, 0, centerPupilDepth));
+ ImuFromCpf = ImuFromScreen * screenFromCpf;
+
+ setNeckPivotFromPose ( WorldFromImu.Pose );
+}
+
+//-------------------------------------------------------------------------------------
+
+// This is a "perceptually tuned predictive filter", which means that it is optimized
+// for improvements in the VR experience, rather than pure error. In particular,
+// jitter is more perceptible at lower speeds whereas latency is more perceptible
+// after a high-speed motion. Therefore, the prediction interval is dynamically
+// adjusted based on speed. Significant more research is needed to further improve
+// this family of filters.
+static Transform<double> calcPredictedPose(const PoseState<double>& poseState, double predictionDt)
+{
+ Transform<double> pose = poseState.Pose;
+ const double linearCoef = 1.0;
+ Vector3d angularVelocity = poseState.AngularVelocity;
+ double angularSpeed = angularVelocity.Length();
+
+ // This could be tuned so that linear and angular are combined with different coefficients
+ double speed = angularSpeed + linearCoef * poseState.LinearVelocity.Length();
+
+ const double slope = 0.2; // The rate at which the dynamic prediction interval varies
+ double candidateDt = slope * speed; // TODO: Replace with smoothstep function
+
+ double dynamicDt = predictionDt;
+
+ // Choose the candidate if it is shorter, to improve stability
+ if (candidateDt < predictionDt)
+ dynamicDt = candidateDt;
+
+ if (angularSpeed > 0.001)
+ pose.Rotation = pose.Rotation * Quatd(angularVelocity, angularSpeed * dynamicDt);
+
+ pose.Translation += poseState.LinearVelocity * dynamicDt;
+
+ return pose;
+}
+
+
+Transformf SensorFusion::GetPoseAtTime(double absoluteTime) const
+{
+ SensorState ss = GetSensorStateAtTime ( absoluteTime );
+ return ss.Predicted.Pose;
+}
+
+
+SensorState SensorFusion::GetSensorStateAtTime(double absoluteTime) const
+{
+ const LocklessState lstate = UpdatedState.GetState();
+ // Delta time from the last available data
+ const double pdt = absoluteTime - lstate.State.TimeInSeconds;
+
+ SensorState ss;
+ ss.Recorded = PoseStatef(lstate.State);
+ ss.Temperature = lstate.Temperature;
+ ss.Magnetometer = Vector3f(lstate.Magnetometer);
+ ss.StatusFlags = lstate.StatusFlags;
+
+ ss.Predicted = ss.Recorded;
+ ss.Predicted.TimeInSeconds = absoluteTime;
+
+ // Do prediction logic and ImuFromCpf transformation
+ ss.Recorded.Pose = Transformf(lstate.State.Pose * ImuFromCpf);
+ ss.Predicted.Pose = Transformf(calcPredictedPose(lstate.State, pdt) * ImuFromCpf);
+ return ss;
+}
+
+unsigned SensorFusion::GetStatus() const
+{
+ return UpdatedState.GetState().StatusFlags;
+}
+
+//-------------------------------------------------------------------------------------
+
+void SensorFusion::OnMessage(const MessageBodyFrame& msg)
+{
+ OVR_ASSERT(!IsAttachedToSensor());
+ handleMessage(msg);
+}
+
+//-------------------------------------------------------------------------------------
+
+void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg)
+{
+ Recording::GetRecorder().RecordMessage(msg);
+ if (msg.Type == Message_BodyFrame)
+ pFusion->handleMessage(static_cast<const MessageBodyFrame&>(msg));
+ if (msg.Type == Message_ExposureFrame)
+ pFusion->handleExposure(static_cast<const MessageExposureFrame&>(msg));
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_SensorFusion.h b/LibOVR/Src/OVR_SensorFusion.h
new file mode 100644
index 0000000..2a17920
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusion.h
@@ -0,0 +1,582 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFusion.h
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle, Dov Katz, Max Katsev, Dan Gierl
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFusion_h
+#define OVR_SensorFusion_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorFilter.h"
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Threads.h"
+#include "Kernel/OVR_Lockless.h"
+
+// CAPI forward declarations.
+typedef struct ovrPoseStatef_ ovrPoseStatef;
+typedef struct ovrSensorState_ ovrSensorState;
+
+namespace OVR {
+
+struct HmdRenderInfo;
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor State
+
+// These values are reported as compatible with C API.
+
+
+// PoseState describes the complete pose, or a rigid body configuration, at a
+// point in time, including first and second derivatives. It is used to specify
+// instantaneous location and movement of the headset.
+// SensorState is returned as a part of the sensor state.
+
+template<class T>
+class PoseState
+{
+public:
+ typedef typename CompatibleTypes<Transform<T> >::Type CompatibleType;
+
+ PoseState() : TimeInSeconds(0.0) { }
+ // float <-> double conversion constructor.
+ explicit PoseState(const PoseState<typename Math<T>::OtherFloatType> &src)
+ : Pose(src.Pose),
+ AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity),
+ AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration),
+ TimeInSeconds(src.TimeInSeconds)
+ { }
+
+ // C-interop support: PoseStatef <-> ovrPoseStatef
+ PoseState(const typename CompatibleTypes<PoseState<T> >::Type& src)
+ : Pose(src.Pose),
+ AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity),
+ AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration),
+ TimeInSeconds(src.TimeInSeconds)
+ { }
+
+ operator typename CompatibleTypes<PoseState<T> >::Type () const
+ {
+ typename CompatibleTypes<PoseState<T> >::Type result;
+ result.Pose = Pose;
+ result.AngularVelocity = AngularVelocity;
+ result.LinearVelocity = LinearVelocity;
+ result.AngularAcceleration = AngularAcceleration;
+ result.LinearAcceleration = LinearAcceleration;
+ result.TimeInSeconds = TimeInSeconds;
+ return result;
+ }
+
+
+ Transform<T> Pose;
+ Vector3<T> AngularVelocity;
+ Vector3<T> LinearVelocity;
+ Vector3<T> AngularAcceleration;
+ Vector3<T> LinearAcceleration;
+ // Absolute time of this state sample; always a double measured in seconds.
+ double TimeInSeconds;
+
+
+ // ***** Helpers for Pose integration
+
+ // Stores and integrates gyro angular velocity reading for a given time step.
+ void StoreAndIntegrateGyro(Vector3d angVel, double dt);
+ // Stores and integrates position/velocity from accelerometer reading for a given time step.
+ void StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt);
+
+ // Performs integration of state by adding next state delta to it
+ // to produce a combined state change
+ void AdvanceByDelta(const PoseState<T>& delta);
+};
+
+
+
+// External API returns pose as float, but uses doubles internally for quaternion precision.
+typedef PoseState<float> PoseStatef;
+typedef PoseState<double> PoseStated;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor State
+
+
+// Bit flags describing the current status of sensor tracking.
+enum StatusBits
+{
+ Status_OrientationTracked = 0x0001, // Orientation is currently tracked (connected and in use).
+ Status_PositionTracked = 0x0002, // Position is currently tracked (false if out of range).
+ Status_PositionConnected = 0x0020, // Position tracking HW is conceded.
+ // Status_HMDConnected = 0x0080 // HMD Display is available & connected.
+};
+
+
+// Full state of of the sensor reported by GetSensorState() at a given absolute time.
+class SensorState
+{
+public:
+ SensorState() : Temperature(0), StatusFlags(0) { }
+
+ // C-interop support
+ SensorState(const ovrSensorState& s);
+ operator ovrSensorState () const;
+
+ // Pose state at the time that SensorState was requested.
+ PoseStatef Predicted;
+ // Actual recorded pose configuration based on sensor sample at a
+ // moment closest to the requested time.
+ PoseStatef Recorded;
+
+ // Calibrated magnetometer reading, in Gauss, at sample time.
+ Vector3f Magnetometer;
+ // Sensor temperature reading, in degrees Celsius, at sample time.
+ float Temperature;
+ // Sensor status described by ovrStatusBits.
+ unsigned int StatusFlags;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+
+class VisionHandler
+{
+public:
+ virtual void OnVisionSuccess(const Transform<double>& cameraFromImu, UInt32 exposureCounter) = 0;
+ virtual void OnVisionPreviousFrame(const Transform<double>& cameraFromImu) = 0;
+ virtual void OnVisionFailure() = 0;
+
+ // Get a configuration that represents the change over a short time interval
+ virtual Transform<double> GetVisionPrediction(UInt32 exposureCounter) = 0;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion
+
+// SensorFusion class accumulates Sensor notification messages to keep track of
+// orientation, which involves integrating the gyro and doing correction with gravity.
+// Magnetometer based yaw drift correction is also supported; it is usually enabled
+// automatically based on loaded magnetometer configuration.
+// Orientation is reported as a quaternion, from which users can obtain either the
+// rotation matrix or Euler angles.
+//
+// The class can operate in two ways:
+// - By user manually passing MessageBodyFrame messages to the OnMessage() function.
+// - By attaching SensorFusion to a SensorDevice, in which case it will
+// automatically handle notifications from that device.
+
+
+class SensorFusion : public NewOverrideBase, public VisionHandler
+{
+ friend class SensorFusionDebug;
+
+ enum
+ {
+ MagMaxReferences = 1000
+ };
+
+public:
+
+ // -------------------------------------------------------------------------------
+ // Critical components for tiny API
+
+ SensorFusion(SensorDevice* sensor = 0);
+ ~SensorFusion();
+
+ // Attaches this SensorFusion to the IMU sensor device, from which it will receive
+ // notification messages. If a sensor is attached, manual message notification
+ // is not necessary. Calling this function also resets SensorFusion state.
+ bool AttachToSensor(SensorDevice* sensor);
+
+ // Returns true if this Sensor fusion object is attached to the IMU.
+ bool IsAttachedToSensor() const;
+
+ // Sets up head-and-neck model and device-to-pupil dimensions from the user's profile and the HMD stats.
+ // This copes elegantly if profile is NULL.
+ void SetUserHeadDimensions(Profile const &profile, HmdRenderInfo const &hmdRenderInfo);
+
+ // Get the predicted pose (orientation, position) of the center pupil frame (CPF) at a specific point in time.
+ Transformf GetPoseAtTime(double absoluteTime) const;
+
+ // Get the full dynamical system state of the CPF, which includes velocities and accelerations,
+ // predicted at a specified absolute point in time.
+ SensorState GetSensorStateAtTime(double absoluteTime) const;
+
+ // Get the sensor status (same as GetSensorStateAtTime(...).Status)
+ unsigned int GetStatus() const;
+
+ // End tiny API components
+ // -------------------------------------------------------------------------------
+
+ // Resets the current orientation.
+ void Reset ();
+
+ // Configuration
+ void EnableMotionTracking(bool enable = true) { MotionTrackingEnabled = enable; }
+ bool IsMotionTrackingEnabled() const { return MotionTrackingEnabled; }
+
+ // Accelerometer/Gravity Correction Control
+ // Enables/disables gravity correction (on by default).
+ void SetGravityEnabled (bool enableGravity);
+ bool IsGravityEnabled () const;
+
+ // Vision Position and Orientation Configuration
+ // -----------------------------------------------
+ bool IsVisionPositionEnabled () const;
+ void SetVisionPositionEnabled (bool enableVisionPosition);
+
+ // compensates for a tilted camera
+ void SetCameraTiltCorrectionEnabled(bool enable);
+ bool IsCameraTiltCorrectionEnabled () const;
+
+ // Message Handling Logic
+ // -----------------------------------------------
+ // Notifies SensorFusion object about a new BodyFrame
+ // message from a sensor.
+ // Should be called by user if not attached to sensor.
+ void OnMessage (const MessageBodyFrame& msg);
+
+
+ // Interaction with vision
+ // -----------------------------------------------
+ // Handle observation from vision system (orientation, position, time)
+ virtual void OnVisionSuccess(const Transform<double>& cameraFromImu, UInt32 exposureCounter);
+
+ virtual void OnVisionPreviousFrame(const Transform<double>& cameraFromImu);
+ virtual void OnVisionFailure();
+ // Get a configuration that represents the change over a short time interval
+ virtual Transform<double> GetVisionPrediction(UInt32 exposureCounter);
+
+ double GetTime () const;
+ double GetVisionLatency () const;
+
+
+ // Detailed head dimension control
+ // -----------------------------------------------
+ // These are now deprecated in favour of SetUserHeadDimensions()
+ Vector3f GetHeadModel() const;
+ void SetHeadModel(const Vector3f &headModel, bool resetNeckPivot = true );
+ float GetCenterPupilDepth() const;
+ void SetCenterPupilDepth(float centerPupilDepth);
+
+
+ // Magnetometer and Yaw Drift Section:
+ // ---------------------------------------
+
+ // Enables/disables magnetometer based yaw drift correction.
+ // Must also have mag calibration data for this correction to work.
+ void SetYawCorrectionEnabled(bool enable);
+ // Determines if yaw correction is enabled.
+ bool IsYawCorrectionEnabled () const;
+
+ // Clear the reference points associating
+ // mag readings with orientations
+ void ClearMagReferences ();
+
+ // Sets the focus filter direction to the current HMD direction
+ void SetFocusDirection();
+ // Sets the focus filter to a direction in the body frame. Once set, a complementary filter
+ // will very slowly drag the world to keep the direction of the HMD within the FOV of the focus
+ void SetFocusDirection(Vector3d direction);
+ // Sets the FOV (in radians) of the focus. When the yaw difference between the HMD's current pose
+ // and the focus is smaller than the FOV, the complementary filter does not act.
+ void SetFocusFOV(double rads);
+ // Turns off the focus filter (equivalent to setting the focus to 0
+ void ClearFocus();
+
+private:
+
+ // -----------------------------------------------
+
+ class BodyFrameHandler : public NewOverrideBase, public MessageHandler
+ {
+ SensorFusion* pFusion;
+ public:
+ BodyFrameHandler(SensorFusion* fusion)
+ : pFusion(fusion) {}
+
+ ~BodyFrameHandler();
+
+ virtual void OnMessage(const Message& msg);
+ virtual bool SupportsMessageType(MessageType type) const;
+ };
+
+
+ // -----------------------------------------------
+
+ // State version stored in lockless updater "queue" and used for
+ // prediction by GetPoseAtTime/GetSensorStateAtTime
+ struct LocklessState
+ {
+ PoseState<double> State;
+ float Temperature;
+ Vector3d Magnetometer;
+ unsigned int StatusFlags;
+
+ LocklessState() : Temperature(0.0), StatusFlags(0) { };
+ };
+
+
+ // -----------------------------------------------
+
+ // Entry describing the state of the headset at the time of an exposure as reported by the DK2 board.
+ // This is used in combination with the vision data for
+ // incremental tracking based on IMU change and for drift correction
+ struct ExposureRecord
+ {
+ UInt32 ExposureCounter;
+ double ExposureTime;
+ // State of the headset at the time of exposure
+ PoseState<double> WorldFromImu;
+ // Change in state since the last exposure based on IMU data only
+ PoseState<double> ImuOnlyDelta;
+ // Did we have tracking for the entire interval between exposures
+ bool VisionTrackingAvailable;
+
+ ExposureRecord() : ExposureCounter(0), ExposureTime(0.0), VisionTrackingAvailable(true) { }
+ ExposureRecord(UInt32 exposureCounter, double exposureTime,
+ const PoseState<double>& worldFromImu, const PoseState<double>& imuOnlyDelta)
+ : ExposureCounter(exposureCounter), ExposureTime(exposureTime),
+ WorldFromImu(worldFromImu), ImuOnlyDelta(imuOnlyDelta), VisionTrackingAvailable(true) { }
+ };
+
+ // -----------------------------------------------
+
+ // Entry describing the magnetometer reference point
+ // Used for mag yaw correction
+ struct MagReferencePoint
+ {
+ Vector3d InImuFrame;
+ Transformd WorldFromImu;
+ int Score;
+
+ MagReferencePoint() { }
+ MagReferencePoint(const Vector3d& inImuFrame, const Transformd& worldFromImu, int score)
+ : InImuFrame(inImuFrame), WorldFromImu(worldFromImu), Score(score) { }
+ };
+
+ // -----------------------------------------------
+
+ // The phase of the head as estimated by sensor fusion
+ PoseState<double> WorldFromImu;
+
+ // State that can be read without any locks, so that high priority rendering thread
+ // doesn't have to worry about being blocked by a sensor/vision threads that got preempted.
+ LocklessUpdater<LocklessState> UpdatedState;
+
+ // The pose we got from Vision, augmented with velocity information from numerical derivatives
+ PoseState<double> CameraFromImu;
+ // Difference between the vision and sensor fusion poses at the time of last exposure adjusted
+ // by all the corrections applied since then
+ // NB: this one is unlike all the other poses/transforms we use, since it's a difference
+ // between 2 WorldFromImu transforms, but is stored in the world frame, not the IMU frame
+ // (see computeVisionError() for details)
+ // For composition purposes it should be considered a WorldFromWorld transform, where the left
+ // side comes from vision and the right - from sensor fusion
+ PoseState<double> VisionError;
+ // Past exposure records between the last update from vision and now
+ // (should only be one record unless vision latency is high)
+ CircularBuffer<ExposureRecord> ExposureRecordHistory;
+ // ExposureRecord that corresponds to the last pose we got from vision
+ ExposureRecord LastVisionExposureRecord;
+ // Incomplete ExposureRecord that will go into the history buffer when
+ // the new MessageExposureFrame is received
+ ExposureRecord NextExposureRecord;
+ // Timings of the previous exposure, used to populate ExposureRecordHistory
+ MessageExposureFrame LastMessageExposureFrame;
+ // Time of the last vision update
+ double LastVisionAbsoluteTime;
+
+ unsigned int Stage;
+ BodyFrameHandler *pHandler;
+
+ Vector3d FocusDirection;
+ double FocusFOV;
+
+ SensorFilterBodyFrame FAccelInImuFrame, FAccelInCameraFrame;
+ SensorFilterd FAngV;
+
+ Vector3d AccelOffset;
+
+ bool EnableGravity;
+
+ bool EnableYawCorrection;
+ bool MagCalibrated;
+ Array<MagReferencePoint> MagRefs;
+ int MagRefIdx;
+ Quatd MagCorrectionIntegralTerm;
+
+ bool EnableCameraTiltCorrection;
+ // Describes the pose of the camera in the world coordinate system
+ Transformd WorldFromCamera;
+ double WorldFromCameraConfidence;
+
+ bool MotionTrackingEnabled;
+ bool VisionPositionEnabled;
+
+ // This is a signed distance, but positive because Z increases looking inward.
+ // This is expressed relative to the IMU in the HMD and corresponds to the location
+ // of the cyclopean virtual camera focal point if both the physical and virtual
+ // worlds are isometrically mapped onto each other. -Steve
+ float CenterPupilDepth;
+ // Describes the position of the user eyes relative to the IMU
+ Transformd ImuFromCpf;
+ // Position of the center of the screen relative to the IMU (loaded from the headset firmware)
+ Transformd ImuFromScreen;
+ // Built-in head model for faking position using orientation only
+ Transformd CpfFromNeck;
+ // Last known base of the neck pose used for head model computations
+ Transformd WorldFromNeck;
+
+ //---------------------------------------------
+
+ // Internal handler for messages
+ // bypasses error checking.
+ void handleMessage(const MessageBodyFrame& msg);
+ void handleExposure(const MessageExposureFrame& msg);
+
+ // Compute the difference between vision and sensor fusion data
+ PoseStated computeVisionError();
+ // Apply headset yaw correction from magnetometer
+ // for models without camera or when camera isn't available
+ void applyMagYawCorrection(Vector3d mag, double deltaT);
+ // Apply headset tilt correction from the accelerometer
+ void applyTiltCorrection(double deltaT);
+ // Apply headset yaw correction from the camera
+ void applyVisionYawCorrection(double deltaT);
+ // Apply headset position correction from the camera
+ void applyPositionCorrection(double deltaT);
+ // Apply camera tilt correction from the accelerometer
+ void applyCameraTiltCorrection(Vector3d accel, double deltaT);
+ // Apply camera focus correction
+ void applyFocusCorrection(double deltaT);
+
+ // If you have a known-good pose, this sets the neck pivot position.
+ void setNeckPivotFromPose ( Transformd const &pose );
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion - Inlines
+
+inline bool SensorFusion::IsAttachedToSensor() const
+{
+ return pHandler->IsHandlerInstalled();
+}
+
+inline void SensorFusion::SetGravityEnabled(bool enableGravity)
+{
+ EnableGravity = enableGravity;
+}
+
+inline bool SensorFusion::IsGravityEnabled() const
+{
+ return EnableGravity;
+}
+
+inline void SensorFusion::SetYawCorrectionEnabled(bool enable)
+{
+ EnableYawCorrection = enable;
+}
+
+inline bool SensorFusion::IsYawCorrectionEnabled() const
+{
+ return EnableYawCorrection;
+}
+
+inline bool SensorFusion::IsVisionPositionEnabled() const
+{
+ return VisionPositionEnabled;
+}
+
+inline void SensorFusion::SetVisionPositionEnabled(bool enableVisionPosition)
+{
+ VisionPositionEnabled = enableVisionPosition;
+}
+
+inline void SensorFusion::SetCameraTiltCorrectionEnabled(bool enable)
+{
+ EnableCameraTiltCorrection = enable;
+}
+
+inline bool SensorFusion::IsCameraTiltCorrectionEnabled() const
+{
+ return EnableCameraTiltCorrection;
+}
+
+inline double SensorFusion::GetVisionLatency() const
+{
+ return LastVisionAbsoluteTime - CameraFromImu.TimeInSeconds;
+}
+
+inline double SensorFusion::GetTime() const
+{
+ return Timer::GetSeconds();
+}
+
+inline SensorFusion::BodyFrameHandler::~BodyFrameHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+inline bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const
+{
+ return (type == Message_BodyFrame || type == Message_ExposureFrame);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** PoseState - Inlines
+
+// Stores and integrates gyro angular velocity reading for a given time step.
+template<class T>
+void PoseState<T>::StoreAndIntegrateGyro(Vector3d angVel, double dt)
+{
+ AngularVelocity = angVel;
+ double angle = angVel.Length() * dt;
+ if (angle > 0)
+ Pose.Rotation = Pose.Rotation * Quatd(angVel, angle);
+}
+
+template<class T>
+void PoseState<T>::StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt)
+{
+ LinearAcceleration = linearAccel;
+ Pose.Translation += LinearVelocity * dt + LinearAcceleration * (dt * dt * 0.5);
+ LinearVelocity += LinearAcceleration * dt;
+}
+
+// Performs integration of state by adding next state delta to it
+// to produce a combined state change
+template<class T>
+void PoseState<T>::AdvanceByDelta(const PoseState<T>& delta)
+{
+ Pose.Rotation = Pose.Rotation * delta.Pose.Rotation;
+ Pose.Translation += delta.Pose.Translation + LinearVelocity * delta.TimeInSeconds;
+ LinearVelocity += delta.LinearVelocity;
+ TimeInSeconds += delta.TimeInSeconds;
+}
+
+} // namespace OVR
+#endif
diff --git a/LibOVR/Src/OVR_SensorFusionDebug.h b/LibOVR/Src/OVR_SensorFusionDebug.h
new file mode 100644
index 0000000..0fc7eb5
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusionDebug.h
@@ -0,0 +1,82 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFusionDebug.h
+Content : Friend proxy to allow debugging access to SensorFusion
+Created : April 16, 2014
+Authors : Dan Gierl
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFusionDebug_h
+#define OVR_SensorFusionDebug_h
+
+#include "OVR_SensorFusion.h"
+
+namespace OVR {
+
+class SensorFusionDebug
+{
+private:
+
+ SensorFusion * sf;
+
+public:
+
+ SensorFusionDebug (SensorFusion * const sf) :
+ sf(sf)
+ {
+ }
+
+ // Returns the number of magnetometer reference points currently gathered
+ int GetNumMagRefPoints () const;
+ // Returns the index of the magnetometer reference point being currently used
+ int GetCurMagRefPointIdx () const;
+ // Returns a copy of all the data associated with a magnetometer reference point
+ // This includes it's score, the magnetometer reading as a vector,
+ // and the HMD's pose at the time it was gathered
+ void GetMagRefData (int idx, int * score, Vector3d * magBF, Quatd * magPose) const;
+
+};
+
+//------------------------------------------------------------------------------------
+// Magnetometer reference point access functions
+
+int SensorFusionDebug::GetNumMagRefPoints() const
+{
+ return (int)sf->MagRefs.GetSize();
+}
+
+int SensorFusionDebug::GetCurMagRefPointIdx() const
+{
+ return sf->MagRefIdx;
+}
+
+void SensorFusionDebug::GetMagRefData(int idx, int * score, Vector3d * magBF, Quatd * magPose) const
+{
+ OVR_ASSERT(idx >= 0 && idx < GetNumMagRefPoints());
+ *score = sf->MagRefs[idx].Score;
+ *magBF = sf->MagRefs[idx].InImuFrame;
+ *magPose = sf->MagRefs[idx].WorldFromImu.Rotation;
+}
+
+} // OVR
+
+#endif \ No newline at end of file
diff --git a/LibOVR/Src/OVR_SensorImpl.cpp b/LibOVR/Src/OVR_SensorImpl.cpp
new file mode 100644
index 0000000..91ae7e0
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl.cpp
@@ -0,0 +1,1165 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl.cpp
+Content : Oculus Sensor device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper, Dov Katz
+
+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_SensorImpl.h"
+#include "OVR_Sensor2Impl.h"
+#include "OVR_SensorImpl_Common.h"
+#include "OVR_JSON.h"
+#include "OVR_Profile.h"
+#include "Kernel/OVR_Alg.h"
+#include <time.h>
+
+// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo.
+
+#include "Kernel/OVR_Timer.h"
+
+//extern FILE *SF_LOG_fp;
+
+namespace OVR {
+
+using namespace Alg;
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor-specific packet data structures
+
+enum {
+ Sensor_VendorId = Oculus_VendorId,
+ Sensor_Tracker_ProductId = Device_Tracker_ProductId,
+ Sensor_Tracker2_ProductId = Device_Tracker2_ProductId,
+ Sensor_KTracker_ProductId = Device_KTracker_ProductId,
+
+ Sensor_BootLoader = 0x1001,
+
+ Sensor_DefaultReportRate = 500, // Hz
+ Sensor_MaxReportRate = 1000 // Hz
+};
+
+
+// Messages we care for
+enum TrackerMessageType
+{
+ TrackerMessage_None = 0,
+ TrackerMessage_Sensors = 1,
+ TrackerMessage_Unknown = 0x100,
+ TrackerMessage_SizeError = 0x101,
+};
+
+
+struct TrackerSensors
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+ UInt16 LastCommandID;
+ SInt16 Temperature;
+
+ TrackerSample Samples[3];
+
+ SInt16 MagX, MagY, MagZ;
+
+ TrackerMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 62)
+ return TrackerMessage_SizeError;
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+ LastCommandID = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+
+ //if (SampleCount > 2)
+ // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount));
+
+ // Only unpack as many samples as there actually are
+ int iterationCount = (SampleCount > 2) ? 3 : SampleCount;
+
+ for (int i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 56);
+ MagY = DecodeSInt16(buffer + 58);
+ MagZ = DecodeSInt16(buffer + 60);
+
+ return TrackerMessage_Sensors;
+ }
+};
+
+struct TrackerMessage
+{
+ TrackerMessageType Type;
+ TrackerSensors Sensors;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDisplayInfoImpl
+SensorDisplayInfoImpl::SensorDisplayInfoImpl()
+ : CommandId(0), DistortionType(Base_None)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 9;
+}
+
+void SensorDisplayInfoImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ DistortionType = Buffer[3];
+ HResolution = DecodeUInt16(Buffer+4);
+ VResolution = DecodeUInt16(Buffer+6);
+ HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f);
+ VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f);
+ VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f);
+ LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f);
+
+#if 0
+ // These are not well-measured on most devices - probably best to ignore them.
+ OutsideLensSurfaceToScreen[0] = DecodeUInt32(Buffer+24) * (1/1000000.f);
+ OutsideLensSurfaceToScreen[1] = DecodeUInt32(Buffer+28) * (1/1000000.f);
+ // TODO: add spline-based distortion.
+ // TODO: currently these values are all zeros in the HMD itself.
+ DistortionK[0] = DecodeFloat(Buffer+32);
+ DistortionK[1] = DecodeFloat(Buffer+36);
+ DistortionK[2] = DecodeFloat(Buffer+40);
+ DistortionK[3] = DecodeFloat(Buffer+44);
+ DistortionK[4] = DecodeFloat(Buffer+48);
+ DistortionK[5] = DecodeFloat(Buffer+52);
+#else
+ // The above are either measured poorly, or don't have values at all.
+ // To remove the temptation to use them, set them to junk.
+ OutsideLensSurfaceToScreen[0] = -1.0f;
+ OutsideLensSurfaceToScreen[1] = -1.0f;
+ DistortionK[0] = -1.0f;
+ DistortionK[1] = -1.0f;
+ DistortionK[2] = -1.0f;
+ DistortionK[3] = -1.0f;
+ DistortionK[4] = -1.0f;
+ DistortionK[5] = -1.0f;
+#endif
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceFactory
+
+SensorDeviceFactory &SensorDeviceFactory::GetInstance()
+{
+ static SensorDeviceFactory instance;
+ return instance;
+}
+
+void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class SensorEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const SensorEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+
+ if (desc.ProductId == Sensor_BootLoader)
+ { // If we find a sensor in boot loader mode then notify the app
+ // about the existence of the device, but don't allow the app
+ // to create or access the device
+ BootLoaderDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+ return;
+ }
+
+ SensorDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+
+ // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially
+ // mismatching monitor information (in case wrong EDID is reported by splitter),
+ // or to create a new "virtualized" HMD Device.
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor);
+ }
+ }
+ }
+ };
+
+ //double start = Timer::GetProfileSeconds();
+
+ SensorEnumerator sensorEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator);
+
+ //double totalSeconds = Timer::GetProfileSeconds() - start;
+}
+
+bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker_ProductId)) ||
+ ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker2_ProductId)) ||
+ ((vendorId == Sensor_VendorId) && (productId == Sensor_KTracker_ProductId));
+}
+
+bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ if (desc.ProductId == Sensor_BootLoader)
+ { // If we find a sensor in boot loader mode then notify the app
+ // about the existence of the device, but don't allow them
+ // to create or access the device
+ BootLoaderDeviceCreateDesc createDesc(this, desc);
+ pdevMgr->AddDevice_NeedsLock(createDesc);
+ return false; // return false to allow upstream boot loader factories to catch the device
+ }
+ else
+ {
+ SensorDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceCreateDesc
+
+DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance()
+{
+ if (HIDDesc.ProductId == Sensor_Tracker2_ProductId)
+ {
+ return new Sensor2DeviceImpl(this);
+ }
+
+ return new SensorDeviceImpl(this);
+}
+
+bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Sensor) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Sensor;
+ info->ProductName = HIDDesc.Product;
+ info->Manufacturer = HIDDesc.Manufacturer;
+ info->Version = HIDDesc.VersionNumber;
+
+ if (info->InfoClassType == Device_Sensor)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange();
+ sinfo->SerialNumber = HIDDesc.SerialNumber;
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::SensorDevice>(createDesc, 0),
+ Coordinates(SensorDevice::Coord_Sensor),
+ HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default.
+ NextKeepAliveTickSeconds(0),
+ FullTimestamp(0),
+ MaxValidRange(SensorRangeImpl::GetMaxSensorRange()),
+ magCalibrated(false)
+{
+ SequenceValid = false;
+ LastSampleCount = 0;
+ LastTimestamp = 0;
+
+ OldCommandId = 0;
+
+ PrevAbsoluteTime = 0.0;
+
+#ifdef OVR_OS_ANDROID
+ pPhoneSensors = PhoneSensors::Create();
+#endif
+}
+
+SensorDeviceImpl::~SensorDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+
+// Internal creation APIs.
+bool SensorDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::SensorDevice>::Initialize(parent))
+ {
+ openDevice();
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&CurrentRange);
+ // Increase the magnetometer range, since the default value is not enough in practice
+ CurrentRange.MaxMagneticField = 2.5f;
+ setRange(CurrentRange);
+ }
+
+ // Read the currently configured calibration from sensor.
+ SensorFactoryCalibrationImpl sc;
+ if (GetInternalDevice()->GetFeatureReport(sc.Buffer, SensorFactoryCalibrationImpl::PacketSize))
+ {
+ sc.Unpack();
+ AccelCalibrationOffset = sc.AccelOffset;
+ GyroCalibrationOffset = sc.GyroOffset;
+ AccelCalibrationMatrix = sc.AccelMatrix;
+ GyroCalibrationMatrix = sc.GyroMatrix;
+ CalibrationTemperature = sc.Temperature;
+ }
+
+ // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default.
+ SensorDisplayInfoImpl displayInfo;
+ if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+ Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ?
+ Coord_HMD : Coord_Sensor;
+ }
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor_DefaultReportRate);
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Load mag calibration
+ MagCalibrationReport report;
+ bool res = GetMagCalibrationReport(&report);
+ if (res && report.Version > 0)
+ {
+ magCalibration = report.Calibration;
+ magCalibrated = true;
+ }
+}
+
+void SensorDeviceImpl::closeDeviceOnError()
+{
+ LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr());
+ NextKeepAliveTickSeconds = 0;
+}
+
+void SensorDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::SensorDevice>::Shutdown();
+
+ LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+ TrackerMessage message;
+ if (decodeTrackerMessage(&message, pData, length))
+ {
+ processed = true;
+ onTrackerMessage(&message);
+ }
+ }
+}
+
+double SensorDeviceImpl::OnTicks(double tickSeconds)
+{
+ if (tickSeconds >= NextKeepAliveTickSeconds)
+ {
+ // Use 3-seconds keep alive by default.
+ double keepAliveDelta = 3.0;
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ // OnTicks is called from background thread so we don't need to add this to the command queue.
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta;
+ }
+ return NextKeepAliveTickSeconds - tickSeconds;
+}
+
+bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag)
+{
+ bool result = 0;
+ ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range);
+ }
+
+ if (!threadQueue->PushCallAndWaitResult(this,
+ &SensorDeviceImpl::setRange,
+ &result,
+ range))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+void SensorDeviceImpl::GetRange(SensorRange* range) const
+{
+ Lock::Locker lockScope(GetLock());
+ *range = CurrentRange;
+}
+
+bool SensorDeviceImpl::setRange(const SensorRange& range)
+{
+ SensorRangeImpl sr(range);
+
+ if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ Lock::Locker lockScope(GetLock());
+ sr.GetSensorRange(&CurrentRange);
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true);
+}
+
+SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const
+{
+ return Coordinates;
+}
+
+Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe)
+{
+
+ Coordinates = coordframe;
+
+ // Read the original coordinate frame, then try to change it.
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ scfg.SetSensorCoordinates(coordframe == Coord_Sensor);
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+
+ // Re-read the state, in case of older firmware that doesn't support Sensor coordinates.
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD;
+ }
+ else
+ {
+ HWCoordinates = Coord_HMD;
+ }
+ return 0;
+}
+
+void SensorDeviceImpl::SetReportRate(unsigned rateHz)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true);
+}
+
+unsigned SensorDeviceImpl::GetReportRate() const
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ return Sensor_MaxReportRate / (scfg.PacketInterval + 1);
+ }
+ return 0; // error
+}
+
+Void SensorDeviceImpl::setReportRate(unsigned rateHz)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (rateHz > Sensor_MaxReportRate)
+ rateHz = Sensor_MaxReportRate;
+ else if (rateHz == 0)
+ rateHz = Sensor_DefaultReportRate;
+
+ scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset,
+ Matrix4f* AccelMatrix, Matrix4f* GyroMatrix,
+ float* Temperature)
+{
+ *AccelOffset = AccelCalibrationOffset;
+ *GyroOffset = GyroCalibrationOffset;
+ *AccelMatrix = AccelCalibrationMatrix;
+ *GyroMatrix = GyroCalibrationMatrix;
+ *Temperature = CalibrationTemperature;
+}
+
+bool SensorDeviceImpl::IsMagCalibrated()
+{
+ return magCalibrated;
+}
+
+void SensorDeviceImpl::SetOnboardCalibrationEnabled(bool enabled)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setOnboardCalibrationEnabled, enabled, true);
+}
+
+Void SensorDeviceImpl::setOnboardCalibrationEnabled(bool enabled)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (enabled)
+ scfg.Flags |= (SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration);
+ else
+ scfg.Flags &= ~(SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::AddMessageHandler(MessageHandler* handler)
+{
+ if (handler)
+ SequenceValid = false;
+ DeviceBase::AddMessageHandler(handler);
+}
+
+// Sensor reports data in the following coordinate system:
+// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down.
+// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right.
+
+
+// We need to convert it to the following RHS coordinate system:
+// X right, Y Up, Z Back (out of screen)
+//
+Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az);
+ return val * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update,
+ Matrix4f magCalibration,
+ bool convertHMDToSensor = false)
+{
+ float mx = (float)update.MagX;
+ float my = (float)update.MagY;
+ float mz = (float)update.MagZ;
+ // Note: Y and Z are swapped in comparison to the Accel.
+ // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases.
+ Vector3f mag = convertHMDToSensor ? Vector3f(mx, my, -mz) : Vector3f(mx, mz, my);
+ mag *= 0.0001f;
+ // Apply calibration
+ return magCalibration.Transform(mag);
+}
+
+Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz);
+ return val * 0.0001f;
+}
+
+bool SensorDeviceImpl::decodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(TrackerMessage));
+
+ if (size < 4)
+ {
+ message->Type = TrackerMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case TrackerMessage_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = TrackerMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None);
+}
+
+void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message)
+{
+ if (message->Type != TrackerMessage_Sensors)
+ return;
+
+ const double timeUnit = (1.0 / 1000.0);
+ double scaledTimeUnit = timeUnit;
+ TrackerSensors& s = message->Sensors;
+ // DK1 timestamps the first sample, so the actual device time will be later
+ // by the time we get the message if there are multiple samples.
+ int timestampAdjust = (s.SampleCount > 0) ? s.SampleCount-1 : 0;
+
+ const double now = Timer::GetSeconds();
+ double absoluteTimeSeconds = 0.0;
+
+
+ if (SequenceValid)
+ {
+ unsigned timestampDelta;
+
+ if (s.Timestamp < LastTimestamp)
+ {
+ // The timestamp rolled around the 16 bit counter, so FullTimeStamp
+ // needs a high word increment.
+ FullTimestamp += 0x10000;
+ timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp);
+ }
+ else
+ {
+ timestampDelta = (s.Timestamp - LastTimestamp);
+ }
+ // Update the low word of FullTimeStamp
+ FullTimestamp = ( FullTimestamp & ~0xffff ) | s.Timestamp;
+
+ double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit;
+ absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime);
+ scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit);
+ PrevAbsoluteTime = absoluteTimeSeconds;
+
+ // If we missed a small number of samples, generate the sample that would have immediately
+ // proceeded the current one. Re-use the IMU values from the last processed sample.
+ if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254))
+ {
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.SampleCount * scaledTimeUnit;
+ sensors.TimeDelta = (float)((timestampDelta - LastSampleCount) * scaledTimeUnit);
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ HandlerRef.Call(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+
+ // This is our baseline sensor to host time delta,
+ // it will be adjusted with each new message.
+ FullTimestamp = s.Timestamp;
+
+ double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit;
+ absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime);
+ scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit);
+ PrevAbsoluteTime = absoluteTimeSeconds;
+ }
+
+ LastSampleCount = s.SampleCount;
+ LastTimestamp = s.Timestamp;
+
+ bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD);
+
+#ifdef OVR_OS_ANDROID
+ // LDC - Normally we get the coordinate system from the tracker.
+ // Since KTracker doesn't store it we'll always assume HMD coordinate system.
+ convertHMDToSensor = false;
+#endif
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.SampleCount;
+
+ if (s.SampleCount > 3)
+ {
+ iterations = 3;
+ sensors.TimeDelta = (float)((s.SampleCount - 2) * scaledTimeUnit);
+ }
+ else
+ {
+ sensors.TimeDelta = (float)scaledTimeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledTimeUnit;
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.MagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor);
+
+#ifdef OVR_OS_ANDROID
+ replaceWithPhoneMag(&(sensors.MagneticField));
+#endif
+ sensors.Temperature = s.Temperature * 0.01f;
+ HandlerRef.Call(sensors);
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = (float)scaledTimeUnit;
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+ }
+ else
+ {
+ UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1);
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastMagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor);
+
+#ifdef OVR_OS_ANDROID
+ replaceWithPhoneMag(&LastMagneticField);
+#endif
+ LastTemperature = s.Temperature * 0.01f;
+ }
+}
+
+
+#ifdef OVR_OS_ANDROID
+
+void SensorDeviceImpl::replaceWithPhoneMag(Vector3f* val)
+{
+
+ // Native calibrated.
+ pPhoneSensors->SetMagSource(PhoneSensors::MagnetometerSource_Native);
+
+ Vector3f magPhone;
+ pPhoneSensors->GetLatestMagValue(&magPhone);
+
+ // Phone value is in micro-Tesla. Convert it to Gauss and flip axes.
+ magPhone *= 10000.0f/1000000.0f;
+
+ Vector3f res;
+ res.x = -magPhone.y;
+ res.y = magPhone.x;
+ res.z = magPhone.z;
+
+ *val = res;
+}
+#endif
+
+const int MAX_DEVICE_PROFILE_MAJOR_VERSION = 1;
+
+// Writes the current calibration for a particular device to a device profile file
+bool SensorDeviceImpl::SetMagCalibrationReport(const MagCalibrationReport &data)
+{
+ // Get device info
+ SensorInfo sinfo;
+ GetDeviceInfo(&sinfo);
+
+ // A named calibration may be specified for calibration in different
+ // environments, otherwise the default calibration is used
+ const char* calibrationName = "default";
+
+ // Generate a mag calibration event
+ JSON* calibration = JSON::CreateObject();
+ // (hardcoded for now) the measurement and representation method
+ calibration->AddStringItem("Version", "2.0");
+ calibration->AddStringItem("Name", "default");
+
+ // time stamp the calibration
+ char time_str[64];
+
+#ifdef OVR_OS_WIN32
+ struct tm caltime;
+ time_t now = time(0);
+ localtime_s(&caltime, &now);
+ strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", &caltime);
+#else
+ struct tm* caltime;
+ time_t now = time(0);
+ caltime = localtime(&now);
+ strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", caltime);
+#endif
+
+ calibration->AddStringItem("Time", time_str);
+
+ // write the full calibration matrix
+ char matrix[256];
+ data.Calibration.ToString(matrix, 256);
+ calibration->AddStringItem("CalibrationMatrix", matrix);
+ // save just the offset, for backwards compatibility
+ // this can be removed when we don't want to support 0.2.4 anymore
+ Vector3f center(data.Calibration.M[0][3], data.Calibration.M[1][3], data.Calibration.M[2][3]);
+ Matrix4f tmp = data.Calibration; tmp.M[0][3] = tmp.M[1][3] = tmp.M[2][3] = 0; tmp.M[3][3] = 1;
+ center = tmp.Inverted().Transform(center);
+ Matrix4f oldcalmat; oldcalmat.M[0][3] = center.x; oldcalmat.M[1][3] = center.y; oldcalmat.M[2][3] = center.z;
+ oldcalmat.ToString(matrix, 256);
+ calibration->AddStringItem("Calibration", matrix);
+
+ String path = GetBaseOVRPath(true);
+ path += "/Devices.json";
+
+ // Look for a preexisting device file to edit
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root)
+ { // Quick sanity check of the file type and format before we parse it
+ JSON* version = root->GetFirstItem();
+ if (version && version->Name == "Oculus Device Profile Version")
+ {
+ int major = atoi(version->Value.ToCStr());
+ if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
+ {
+ // don't use the file on unsupported major version number
+ root->Release();
+ root = NULL;
+ }
+ }
+ else
+ {
+ root->Release();
+ root = NULL;
+ }
+ }
+
+ JSON* device = NULL;
+ if (root)
+ {
+ device = root->GetFirstItem(); // skip the header
+ device = root->GetNextItem(device);
+ while (device)
+ { // Search for a previous calibration with the same name for this device
+ // and remove it before adding the new one
+ if (device->Name == "Device")
+ {
+ JSON* item = device->GetItemByName("Serial");
+ if (item && item->Value == sinfo.SerialNumber)
+ { // found an entry for this device
+ item = device->GetNextItem(item);
+ while (item)
+ {
+ if (item->Name == "MagCalibration")
+ {
+ JSON* name = item->GetItemByName("Name");
+ if (name && name->Value == calibrationName)
+ { // found a calibration of the same name
+ item->RemoveNode();
+ item->Release();
+ break;
+ }
+ }
+ item = device->GetNextItem(item);
+ }
+
+
+ /*
+ this is removed temporarily, since this is a sensor fusion setting, not sensor itself
+ should be moved to the correct place when Brant has finished the user profile implementation
+ // update the auto-mag flag
+ item = device->GetItemByName("EnableYawCorrection");
+ if (item)
+ item->dValue = (double)EnableYawCorrection;
+ else
+ device->AddBoolItem("EnableYawCorrection", EnableYawCorrection);*/
+
+ break;
+ }
+ }
+
+ device = root->GetNextItem(device);
+ }
+ }
+ else
+ { // Create a new device root
+ root = *JSON::CreateObject();
+ root->AddStringItem("Oculus Device Profile Version", "1.0");
+ }
+
+ if (device == NULL)
+ {
+ device = JSON::CreateObject();
+ device->AddStringItem("Product", sinfo.ProductName);
+ device->AddNumberItem("ProductID", sinfo.ProductId);
+ device->AddStringItem("Serial", sinfo.SerialNumber);
+ // removed temporarily, see above
+ //device->AddBoolItem("EnableYawCorrection", EnableYawCorrection);
+
+ root->AddItem("Device", device);
+ }
+
+ // Create and the add the new calibration event to the device
+ device->AddItem("MagCalibration", calibration);
+ return root->Save(path);
+}
+
+// Loads a saved calibration for the specified device from the device profile file
+bool SensorDeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data)
+{
+ data->Version = 0;
+ data->Calibration.SetIdentity();
+
+ // Get device info
+ SensorInfo sinfo;
+ GetDeviceInfo(&sinfo);
+
+ // A named calibration may be specified for calibration in different
+ // environments, otherwise the default calibration is used
+ const char* calibrationName = "default";
+
+ String path = GetBaseOVRPath(true);
+ path += "/Devices.json";
+
+ // Load the device profiles
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root == NULL)
+ return false;
+
+ // Quick sanity check of the file type and format before we parse it
+ JSON* version = root->GetFirstItem();
+ if (version && version->Name == "Oculus Device Profile Version")
+ {
+ int major = atoi(version->Value.ToCStr());
+ if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
+ return false; // don't parse the file on unsupported major version number
+ }
+ else
+ {
+ return false;
+ }
+
+ JSON* device = root->GetNextItem(version);
+ while (device)
+ { // Search for a previous calibration with the same name for this device
+ // and remove it before adding the new one
+ if (device->Name == "Device")
+ {
+ JSON* item = device->GetItemByName("Serial");
+ if (item && item->Value == sinfo.SerialNumber)
+ { // found an entry for this device
+
+ JSON* autoyaw = device->GetItemByName("EnableYawCorrection");
+ // as a temporary HACK, return no calibration if EnableYawCorrection is off
+ // this will force disable yaw correction in SensorFusion
+ // proper solution would load the value in the Profile, which SensorFusion can access
+ if (autoyaw && autoyaw->dValue == 0)
+ return true;
+
+ item = device->GetNextItem(item);
+ while (item)
+ {
+ if (item->Name == "MagCalibration")
+ {
+ JSON* calibration = item;
+ JSON* name = calibration->GetItemByName("Name");
+ if (name && name->Value == calibrationName)
+ { // found a calibration with this name
+
+ int major = 0;
+ JSON* version = calibration->GetItemByName("Version");
+ if (version)
+ major = atoi(version->Value.ToCStr());
+
+ if (major > data->Version && major <= 2)
+ {
+ time_t now;
+ time(&now);
+
+ // parse the calibration time
+ //time_t calibration_time = now;
+ JSON* caltime = calibration->GetItemByName("Time");
+ if (caltime)
+ {
+ const char* caltime_str = caltime->Value.ToCStr();
+
+ tm ct;
+ memset(&ct, 0, sizeof(tm));
+
+#ifdef OVR_OS_WIN32
+ struct tm nowtime;
+ localtime_s(&nowtime, &now);
+ ct.tm_isdst = nowtime.tm_isdst;
+ sscanf_s(caltime_str, "%d-%d-%d %d:%d:%d",
+ &ct.tm_year, &ct.tm_mon, &ct.tm_mday,
+ &ct.tm_hour, &ct.tm_min, &ct.tm_sec);
+#else
+ struct tm* nowtime = localtime(&now);
+ ct.tm_isdst = nowtime->tm_isdst;
+ sscanf(caltime_str, "%d-%d-%d %d:%d:%d",
+ &ct.tm_year, &ct.tm_mon, &ct.tm_mday,
+ &ct.tm_hour, &ct.tm_min, &ct.tm_sec);
+#endif
+ ct.tm_year -= 1900;
+ ct.tm_mon--;
+ //calibration_time = mktime(&ct);
+ }
+
+ // parse the calibration matrix
+ JSON* cal = calibration->GetItemByName("CalibrationMatrix");
+ if (!cal)
+ cal = calibration->GetItemByName("Calibration");
+ if (cal)
+ {
+ data->Calibration = Matrix4f::FromString(cal->Value.ToCStr());
+ data->Version = (UByte)major;
+ }
+ }
+ }
+ }
+ item = device->GetNextItem(item);
+ }
+
+ return true;
+ }
+ }
+
+ device = root->GetNextItem(device);
+ }
+
+ return true;
+}
+
+
+bool SensorDeviceImpl::SetSerialReport(const SerialReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setSerialReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool SensorDeviceImpl::setSerialReport(const SerialReport& data)
+{
+ SerialImpl di(data);
+ return GetInternalDevice()->SetFeatureReport(di.Buffer, SerialImpl::PacketSize);
+}
+
+bool SensorDeviceImpl::GetSerialReport(SerialReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getSerialReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool SensorDeviceImpl::getSerialReport(SerialReport* data)
+{
+ SerialImpl di;
+ if (GetInternalDevice()->GetFeatureReport(di.Buffer, SerialImpl::PacketSize))
+ {
+ di.Unpack();
+ *data = di.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_SensorImpl.h b/LibOVR/Src/OVR_SensorImpl.h
new file mode 100644
index 0000000..f53b831
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl.h
@@ -0,0 +1,316 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl.h
+Content : Sensor device specific implementation.
+Created : March 7, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorImpl_h
+#define OVR_SensorImpl_h
+
+#include "OVR_HIDDeviceImpl.h"
+#include "OVR_SensorTimeFilter.h"
+#include "OVR_Device.h"
+
+#ifdef OVR_OS_ANDROID
+#include "OVR_PhoneSensors.h"
+#endif
+
+namespace OVR {
+
+struct TrackerMessage;
+class ExternalVisitor;
+
+//-------------------------------------------------------------------------------------
+// SensorDeviceFactory enumerates Oculus Sensor devices.
+class SensorDeviceFactory : public DeviceFactory
+{
+public:
+ static SensorDeviceFactory &GetInstance();
+
+ // Enumerates devices, creating and destroying relevant objects in manager.
+ virtual void EnumerateDevices(EnumerateVisitor& visitor);
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
+ virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
+protected:
+ DeviceManager* getManager() const { return (DeviceManager*) pManager; }
+};
+
+
+// Describes a single a Oculus Sensor device and supports creating its instance.
+class SensorDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new SensorDeviceCreateDesc(*this);
+ }
+
+ virtual DeviceBase* NewDeviceInstance();
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_Sensor) && (pFactory == other.pFactory))
+ {
+ const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber) &&
+ (HIDDesc.VersionNumber == hidDesc.VersionNumber));
+ }
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const;
+};
+
+// A simple stub for notification of a sensor in Boot Loader mode
+// This descriptor does not support the creation of a device, only the detection
+// of its existence to warn apps that the sensor device needs firmware.
+// The Boot Loader descriptor reuses and is created by the Sensor device factory
+// but in the future may use a dedicated factory
+class BootLoaderDeviceCreateDesc : public HIDDeviceCreateDesc
+{
+public:
+ BootLoaderDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
+ : HIDDeviceCreateDesc(factory, Device_BootLoader, hidDesc) { }
+
+ virtual DeviceCreateDesc* Clone() const
+ {
+ return new BootLoaderDeviceCreateDesc(*this);
+ }
+
+ // Boot Loader device creation is not allowed
+ virtual DeviceBase* NewDeviceInstance() { return NULL; };
+
+ virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
+ DeviceCreateDesc**) const
+ {
+ if ((other.Type == Device_BootLoader) && (pFactory == other.pFactory))
+ {
+ const BootLoaderDeviceCreateDesc& s2 = (const BootLoaderDeviceCreateDesc&) other;
+ if (MatchHIDDevice(s2.HIDDesc))
+ return Match_Found;
+ }
+ return Match_None;
+ }
+
+ virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
+ {
+ // should paths comparison be case insensitive?
+ return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
+ (HIDDesc.SerialNumber == hidDesc.SerialNumber));
+ }
+
+ virtual bool GetDeviceInfo(DeviceInfo* info) const
+ {
+ OVR_UNUSED(info);
+ return false;
+ }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDisplayInfoImpl
+
+// DisplayInfo obtained from sensor; these values are used to report distortion
+// settings and other coefficients.
+// Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults.
+// Currently, only resolutions and sizes are used.
+struct SensorDisplayInfoImpl
+{
+ enum { PacketSize = 56 };
+ UByte Buffer[PacketSize];
+
+ enum
+ {
+ Mask_BaseFmt = 0x0f,
+ Mask_OptionFmts = 0xf0,
+ Base_None = 0,
+ Base_ScreenOnly = 1,
+ Base_Distortion = 2,
+ };
+
+ UInt16 CommandId;
+
+ UByte DistortionType;
+ UInt16 HResolution, VResolution;
+ float HScreenSize, VScreenSize;
+ float VCenter;
+ float LensSeparation;
+ // Currently these values are not well-measured.
+ float OutsideLensSurfaceToScreen[2];
+ // TODO: add DistortionEqn
+ // TODO: currently these values are all zeros and the
+ // distortion is hard-coded in HMDDeviceCreateDesc::GetDeviceInfo()
+ float DistortionK[6];
+
+ SensorDisplayInfoImpl();
+
+ void Unpack();
+};
+
+//-------------------------------------------------------------------------------------
+// ***** OVR::SensorDeviceImpl
+
+// Oculus Sensor interface.
+
+class SensorDeviceImpl : public HIDDeviceImpl<OVR::SensorDevice>
+{
+public:
+ SensorDeviceImpl(SensorDeviceCreateDesc* createDesc);
+ ~SensorDeviceImpl();
+
+
+ // DeviceCommaon interface
+ virtual bool Initialize(DeviceBase* parent);
+ virtual void Shutdown();
+
+ virtual void AddMessageHandler(MessageHandler* handler);
+
+ // HIDDevice::Notifier interface.
+ virtual void OnInputReport(UByte* pData, UInt32 length);
+ virtual double OnTicks(double tickSeconds);
+
+ // HMD-Mounted sensor has a different coordinate frame.
+ virtual void SetCoordinateFrame(CoordinateFrame coordframe);
+ virtual CoordinateFrame GetCoordinateFrame() const;
+
+ // SensorDevice interface
+ virtual bool SetRange(const SensorRange& range, bool waitFlag);
+ virtual void GetRange(SensorRange* range) const;
+
+ virtual void GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset,
+ Matrix4f* AccelMatrix, Matrix4f* GyroMatrix,
+ float* Temperature);
+ virtual void SetOnboardCalibrationEnabled(bool enabled);
+ virtual bool IsMagCalibrated();
+
+ // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
+ // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
+ // called twice or thrice at the same 'tick'.
+ // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
+ // times for each 'tick': the first call will contain averaged values, the second
+ // and third calls will provide with most recent two recorded samples.
+ virtual void SetReportRate(unsigned rateHz);
+ // Returns currently set report rate, in Hz. If 0 - error occurred.
+ // Note, this value may be different from the one provided for SetReportRate. The return
+ // value will contain the actual rate.
+ virtual unsigned GetReportRate() const;
+
+ bool SetSerialReport(const SerialReport& data);
+ bool GetSerialReport(SerialReport* data);
+
+ // Hack to create HMD device from sensor display info.
+ static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo,
+ DeviceFactory::EnumerateVisitor& visitor);
+
+ // These methods actually store data in a JSON file
+ virtual bool SetMagCalibrationReport(const MagCalibrationReport& data);
+ virtual bool GetMagCalibrationReport(MagCalibrationReport* data);
+
+protected:
+
+ virtual void openDevice();
+ void closeDeviceOnError();
+
+ Void setCoordinateFrame(CoordinateFrame coordframe);
+ bool setRange(const SensorRange& range);
+
+ Void setReportRate(unsigned rateHz);
+
+ Void setOnboardCalibrationEnabled(bool enabled);
+
+ bool setSerialReport(const SerialReport& data);
+ bool getSerialReport(SerialReport* data);
+
+ // Called for decoded messages
+ void onTrackerMessage(TrackerMessage* message);
+ bool decodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size);
+
+ // Helpers to reduce casting.
+/*
+ SensorDeviceCreateDesc* getCreateDesc() const
+ { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); }
+
+ HIDDeviceDesc* getHIDDesc() const
+ { return &getCreateDesc()->HIDDesc; }
+*/
+
+ // Set if the sensor is located on the HMD.
+ // Older prototype firmware doesn't support changing HW coordinates,
+ // so we track its state.
+ CoordinateFrame Coordinates;
+ CoordinateFrame HWCoordinates;
+ double NextKeepAliveTickSeconds;
+
+ bool SequenceValid;
+ UInt16 LastTimestamp;
+ UByte LastSampleCount;
+ float LastTemperature;
+ Vector3f LastAcceleration;
+ Vector3f LastRotationRate;
+ Vector3f LastMagneticField;
+
+ // This tracks wrap around, and should be monotonically increasing.
+ UInt32 FullTimestamp;
+
+ // Current sensor range obtained from device.
+ SensorRange MaxValidRange;
+ SensorRange CurrentRange;
+
+ // IMU calibration obtained from device.
+ Vector3f AccelCalibrationOffset;
+ Vector3f GyroCalibrationOffset;
+ Matrix4f AccelCalibrationMatrix;
+ Matrix4f GyroCalibrationMatrix;
+ float CalibrationTemperature;
+
+ UInt16 OldCommandId;
+
+ SensorTimeFilter TimeFilter;
+ double PrevAbsoluteTime;
+
+#ifdef OVR_OS_ANDROID
+ void replaceWithPhoneMag(Vector3f* val);
+ PhoneSensors* pPhoneSensors;
+#endif
+
+private:
+ Matrix4f magCalibration;
+ bool magCalibrated;
+};
+
+} // namespace OVR
+
+#endif // OVR_SensorImpl_h
diff --git a/LibOVR/Src/OVR_SensorImpl_Common.cpp b/LibOVR/Src/OVR_SensorImpl_Common.cpp
new file mode 100644
index 0000000..99febe8
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl_Common.cpp
@@ -0,0 +1,245 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl_Common.cpp
+Content : Source common to SensorImpl and Sensor2Impl.
+Created : January 21, 2014
+Authors : Lee Cooper
+
+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_SensorImpl_Common.h"
+#include "Kernel/OVR_Alg.h"
+
+namespace OVR
+{
+
+void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z)
+{
+ // Sign extending trick
+ // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend
+ struct {SInt32 x:21;} s;
+
+ *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3);
+ *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) |
+ ((buffer[5] & 0xC0) >> 6);
+ *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1);
+}
+
+void PackSensor(UByte* buffer, SInt32 x, SInt32 y, SInt32 z)
+{
+ // Pack 3 32 bit integers into 8 bytes
+ buffer[0] = UByte(x >> 13);
+ buffer[1] = UByte(x >> 5);
+ buffer[2] = UByte((x << 3) | ((y >> 18) & 0x07));
+ buffer[3] = UByte(y >> 10);
+ buffer[4] = UByte(y >> 2);
+ buffer[5] = UByte((y << 6) | ((z >> 15) & 0x3F));
+ buffer[6] = UByte(z >> 7);
+ buffer[7] = UByte(z << 1);
+}
+
+UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count,
+ float val, float factor, const char* label)
+{
+ UInt16 threshold = (UInt16)(val * factor);
+
+ for (unsigned i = 0; i<count; i++)
+ {
+ if (ramp[i] >= threshold)
+ return ramp[i];
+ }
+ OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f",
+ label, float(ramp[count-1]) / factor));
+ OVR_UNUSED2(factor, label);
+ return ramp[count-1];
+}
+
+SensorRangeImpl::SensorRangeImpl(const SensorRange& r, UInt16 commandId)
+{
+ SetSensorRange(r, commandId);
+}
+
+void SensorRangeImpl::SetSensorRange(const SensorRange& r, UInt16 commandId)
+{
+ CommandId = commandId;
+ AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]),
+ r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration");
+ GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]),
+ r.MaxRotationRate, Math<float>::RadToDegreeFactor, "MaxRotationRate");
+ MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]),
+ r.MaxMagneticField, 1000.0f, "MaxMagneticField");
+ Pack();
+}
+
+void SensorRangeImpl::GetSensorRange(SensorRange* r)
+{
+ r->MaxAcceleration = AccelScale * 9.81f;
+ r->MaxRotationRate = DegreeToRad((float)GyroScale);
+ r->MaxMagneticField= MagScale * 0.001f;
+}
+
+SensorRange SensorRangeImpl::GetMaxSensorRange()
+{
+ return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f,
+ GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] *
+ Math<float>::DegreeToRadFactor,
+ MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f);
+}
+
+void SensorRangeImpl::Pack()
+{
+ Buffer[0] = 4;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(AccelScale);
+ Buffer[4] = UByte(GyroScale & 0xFF);
+ Buffer[5] = UByte(GyroScale >> 8);
+ Buffer[6] = UByte(MagScale & 0xFF);
+ Buffer[7] = UByte(MagScale >> 8);
+}
+
+void SensorRangeImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ AccelScale= Buffer[3];
+ GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8);
+ MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8);
+}
+
+SensorConfigImpl::SensorConfigImpl()
+ : CommandId(0), Flags(0), PacketInterval(0), SampleRate(0)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 2;
+}
+
+void SensorConfigImpl::SetSensorCoordinates(bool sensorCoordinates)
+{
+ Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0);
+}
+
+bool SensorConfigImpl::IsUsingSensorCoordinates() const
+{
+ return (Flags & Flag_SensorCoordinates) != 0;
+}
+
+void SensorConfigImpl::Pack()
+{
+ Buffer[0] = 2;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = Flags;
+ Buffer[4] = UByte(PacketInterval);
+ Buffer[5] = UByte(SampleRate & 0xFF);
+ Buffer[6] = UByte(SampleRate >> 8);
+}
+
+void SensorConfigImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ Flags = Buffer[3];
+ PacketInterval = Buffer[4];
+ SampleRate = Buffer[5] | (UInt16(Buffer[6]) << 8);
+}
+
+SensorFactoryCalibrationImpl::SensorFactoryCalibrationImpl()
+ : AccelOffset(), GyroOffset(), AccelMatrix(), GyroMatrix(), Temperature(0)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 3;
+}
+
+void SensorFactoryCalibrationImpl::Pack()
+{
+ SInt32 x, y, z;
+
+ Buffer[0] = 3;
+
+ x = SInt32(AccelOffset.x * 1e4f);
+ y = SInt32(AccelOffset.y * 1e4f);
+ z = SInt32(AccelOffset.z * 1e4f);
+ PackSensor(Buffer + 3, x, y, z);
+
+ x = SInt32(GyroOffset.x * 1e4f);
+ y = SInt32(GyroOffset.y * 1e4f);
+ z = SInt32(GyroOffset.z * 1e4f);
+ PackSensor(Buffer + 11, x, y, z);
+
+ // ignore the scale matrices for now
+}
+
+void SensorFactoryCalibrationImpl::Unpack()
+{
+ static const float sensorMax = (1 << 20) - 1;
+ SInt32 x, y, z;
+
+ UnpackSensor(Buffer + 3, &x, &y, &z);
+ AccelOffset.y = (float) y * 1e-4f;
+ AccelOffset.z = (float) z * 1e-4f;
+ AccelOffset.x = (float) x * 1e-4f;
+
+ UnpackSensor(Buffer + 11, &x, &y, &z);
+ GyroOffset.x = (float) x * 1e-4f;
+ GyroOffset.y = (float) y * 1e-4f;
+ GyroOffset.z = (float) z * 1e-4f;
+
+ for (int i = 0; i < 3; i++)
+ {
+ UnpackSensor(Buffer + 19 + 8 * i, &x, &y, &z);
+ AccelMatrix.M[i][0] = (float) x / sensorMax;
+ AccelMatrix.M[i][1] = (float) y / sensorMax;
+ AccelMatrix.M[i][2] = (float) z / sensorMax;
+ AccelMatrix.M[i][i] += 1.0f;
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ UnpackSensor(Buffer + 43 + 8 * i, &x, &y, &z);
+ GyroMatrix.M[i][0] = (float) x / sensorMax;
+ GyroMatrix.M[i][1] = (float) y / sensorMax;
+ GyroMatrix.M[i][2] = (float) z / sensorMax;
+ GyroMatrix.M[i][i] += 1.0f;
+ }
+
+ Temperature = (float) Alg::DecodeSInt16(Buffer + 67) / 100.0f;
+}
+
+SensorKeepAliveImpl::SensorKeepAliveImpl(UInt16 interval, UInt16 commandId)
+ : CommandId(commandId), KeepAliveIntervalMs(interval)
+{
+ Pack();
+}
+
+void SensorKeepAliveImpl::Pack()
+{
+ Buffer[0] = 8;
+ Buffer[1] = UByte(CommandId & 0xFF);
+ Buffer[2] = UByte(CommandId >> 8);
+ Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF);
+ Buffer[4] = UByte(KeepAliveIntervalMs >> 8);
+}
+
+void SensorKeepAliveImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8);
+}
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_SensorImpl_Common.h b/LibOVR/Src/OVR_SensorImpl_Common.h
new file mode 100644
index 0000000..a7091ba
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl_Common.h
@@ -0,0 +1,150 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl_Common.h
+Content : Source common to SensorImpl and Sensor2Impl.
+Created : January 21, 2014
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorImpl_Common_h
+#define OVR_SensorImpl_Common_h
+
+#include "Kernel/OVR_System.h"
+#include "OVR_Device.h"
+
+namespace OVR
+{
+
+void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z);
+void PackSensor(UByte* buffer, SInt32 x, SInt32 y, SInt32 z);
+
+// Sensor HW only accepts specific maximum range values, used to maximize
+// the 16-bit sensor outputs. Use these ramps to specify and report appropriate values.
+const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 };
+const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 };
+const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 };
+
+UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count,
+ float val, float factor, const char* label);
+
+// SensorScaleImpl provides buffer packing logic for the Sensor Range
+// record that can be applied to DK1 sensor through Get/SetFeature. We expose this
+// through SensorRange class, which has different units.
+struct SensorRangeImpl
+{
+ enum { PacketSize = 8 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 AccelScale;
+ UInt16 GyroScale;
+ UInt16 MagScale;
+
+ SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0);
+
+ void SetSensorRange(const SensorRange& r, UInt16 commandId = 0);
+ void GetSensorRange(SensorRange* r);
+
+ static SensorRange GetMaxSensorRange();
+
+ void Pack();
+ void Unpack();
+};
+
+struct SensorConfigImpl
+{
+ enum { PacketSize = 7 };
+ UByte Buffer[PacketSize];
+
+ // Flag values for Flags.
+ enum {
+ Flag_RawMode = 0x01,
+ Flag_CalibrationTest = 0x02, // Internal test mode
+ Flag_UseCalibration = 0x04,
+ Flag_AutoCalibration = 0x08,
+ Flag_MotionKeepAlive = 0x10,
+ Flag_CommandKeepAlive = 0x20,
+ Flag_SensorCoordinates = 0x40
+ };
+
+ UInt16 CommandId;
+ UByte Flags;
+ UInt16 PacketInterval; // LDC - This should be a UByte. Fix when you have time to test it.
+ UInt16 SampleRate;
+
+ SensorConfigImpl();
+
+ void SetSensorCoordinates(bool sensorCoordinates);
+ bool IsUsingSensorCoordinates() const;
+
+ void Pack();
+ void Unpack();
+};
+
+struct SensorFactoryCalibrationImpl
+{
+ enum { PacketSize = 69 };
+ UByte Buffer[PacketSize];
+
+ Vector3f AccelOffset;
+ Vector3f GyroOffset;
+ Matrix4f AccelMatrix;
+ Matrix4f GyroMatrix;
+ float Temperature;
+
+ SensorFactoryCalibrationImpl();
+
+ void Pack(); // Not yet implemented.
+ void Unpack();
+};
+
+
+// SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor
+// to receive commands.
+struct SensorKeepAliveImpl
+{
+ enum { PacketSize = 5 };
+ UByte Buffer[PacketSize];
+
+ UInt16 CommandId;
+ UInt16 KeepAliveIntervalMs;
+
+ SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0);
+
+ void Pack();
+ void Unpack();
+};
+
+struct TrackerSample
+{
+ SInt32 AccelX, AccelY, AccelZ;
+ SInt32 GyroX, GyroY, GyroZ;
+};
+
+enum LastCommandIdFlags
+{
+ LastCommandId_Shutter = 1,
+ LastCommandId_LEDs = 2
+};
+
+} // namespace OVR
+
+#endif // OVR_SensorImpl_Common_h
diff --git a/LibOVR/Src/OVR_SensorTimeFilter.cpp b/LibOVR/Src/OVR_SensorTimeFilter.cpp
new file mode 100644
index 0000000..ee0c385
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorTimeFilter.cpp
@@ -0,0 +1,385 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_SensorTimeFilter.cpp
+Content : Class to filter HMD time and convert it to system time
+Created : December 20, 2013
+Author : Michael Antonov
+Notes :
+
+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_SensorTimeFilter.h"
+#include "Kernel/OVR_Log.h"
+
+
+#include <stdio.h>
+#include <math.h>
+
+namespace OVR {
+
+// Comment out for debug logging to file
+//#define OVR_TIMEFILTER_LOG_CODE( code ) code
+#define OVR_TIMEFILTER_LOG_CODE( code )
+
+#if defined(OVR_OS_ANDROID)
+ #define OVR_TIMEFILTER_LOG_FILENAME "/sdcard/TimeFilterLog.txt"
+#elif defined(OVR_OS_WIN32)
+ #define OVR_TIMEFILTER_LOG_FILENAME "C:\\TimeFilterLog.txt"
+#else
+ #define OVR_TIMEFILTER_LOG_FILENAME "TimeFilterLog.txt"
+#endif
+
+OVR_TIMEFILTER_LOG_CODE( FILE* pTFLogFile = 0; )
+
+
+// Ideally, the following would always be true:
+// - NewSampleTime > PrevSample
+// - NewSampleTime < now systemTime
+// - (NewSampleTime - PrevSampleTime) == integration delta, matching
+// HW sample time difference + drift
+//
+// In practice, these issues affect us:
+// - System thread can be suspended for a while
+// - System de-buffering of recorded samples cause deviceTime to advance up
+// much faster then system time for ~100+ samples
+// - Device (DK1) and system clock granularities are high; this can
+// lead to potentially having estimations in the future
+//
+
+
+// ***** TimerFilter
+
+SensorTimeFilter::SensorTimeFilter(const Settings& settings)
+{
+ FilterSettings = settings;
+
+ ClockInitialized = false;
+ ClockDelta = 0;
+ ClockDeltaDriftPerSecond = 0;
+ ClockDeltaCorrectPerSecond = 0;
+ ClockDeltaCorrectSecondsLeft = 0;
+ OldClockDeltaDriftExpire = 0;
+
+ LastLargestDeviceTime = 0;
+ PrevSystemTime = 0;
+ PastSampleResetTime = 0;
+
+ MinWindowsCollected = 0;
+ MinWindowDuration = 0; // assigned later
+ MinWindowLastTime = 0;
+ MinWindowSamples = settings.MinSamples; // Force initialization
+
+ OVR_TIMEFILTER_LOG_CODE( pTFLogFile = fopen(OVR_TIMEFILTER_LOG_FILENAME, "w+"); )
+}
+
+
+double SensorTimeFilter::SampleToSystemTime(double sampleDeviceTime, double systemTime,
+ double prevResult, const char* debugTag)
+{
+ double clockDelta = systemTime - sampleDeviceTime + FilterSettings.ClockDeltaAdjust;
+ double deviceTimeDelta = sampleDeviceTime - LastLargestDeviceTime;
+ double result;
+
+ // Collect a sample ClockDelta for a "MinimumWindow" or process
+ // the window by adjusting drift rates if it's full of samples.
+ // - (deviceTimeDelta < 1.0f) is a corner cases, as it would imply timestamp skip/wrap.
+
+ if (ClockInitialized)
+ {
+ // Samples in the past commonly occur if they come from separately incrementing
+ // data channels. Just adjust them with ClockDelta.
+
+ if (deviceTimeDelta < 0.0)
+ {
+ result = sampleDeviceTime + ClockDelta;
+
+ if (result > (prevResult - 0.00001))
+ goto clamp_and_log_result;
+
+ // Consistent samples less then prevResult for indicate a back-jump or bad input.
+ // In this case we return prevResult for a while, then reset filter if it keeps going.
+ if (PastSampleResetTime < 0.0001)
+ {
+ PastSampleResetTime = systemTime + FilterSettings.PastSampleResetSeconds;
+ goto clamp_and_log_result;
+ }
+ else if (systemTime > PastSampleResetTime)
+ {
+ OVR_DEBUG_LOG(("SensorTimeFilter - Filtering reset due to samples in the past!\n"));
+ initClockSampling(sampleDeviceTime, clockDelta);
+ // Fall through to below, to ' PastSampleResetTime = 0.0; '
+ }
+ else
+ {
+ goto clamp_and_log_result;
+ }
+ }
+
+ // Most common case: Record window sample.
+ else if ( (deviceTimeDelta < 1.0f) &&
+ ( (sampleDeviceTime < MinWindowLastTime) ||
+ (MinWindowSamples < FilterSettings.MinSamples) ) )
+ {
+ // Pick minimum ClockDelta sample.
+ if (clockDelta < MinWindowClockDelta)
+ MinWindowClockDelta = clockDelta;
+ MinWindowSamples++;
+ }
+ else
+ {
+ processFinishedMinWindow(sampleDeviceTime, clockDelta);
+ }
+
+ PastSampleResetTime = 0.0;
+ }
+ else
+ {
+ initClockSampling(sampleDeviceTime, clockDelta);
+ }
+
+
+ // Clock adjustment for drift.
+ ClockDelta += ClockDeltaDriftPerSecond * deviceTimeDelta;
+
+ // ClockDelta "nudging" towards last known MinWindowClockDelta.
+ if (ClockDeltaCorrectSecondsLeft > 0.000001)
+ {
+ double correctTimeDelta = deviceTimeDelta;
+ if (deviceTimeDelta > ClockDeltaCorrectSecondsLeft)
+ correctTimeDelta = ClockDeltaCorrectSecondsLeft;
+ ClockDeltaCorrectSecondsLeft -= correctTimeDelta;
+
+ ClockDelta += ClockDeltaCorrectPerSecond * correctTimeDelta;
+ }
+
+ // Record largest device time, so we know what samples to use in accumulation
+ // of min-window in the future.
+ LastLargestDeviceTime = sampleDeviceTime;
+
+ // Compute our resulting sample time after ClockDelta adjustment.
+ result = sampleDeviceTime + ClockDelta;
+
+
+clamp_and_log_result:
+
+ OVR_TIMEFILTER_LOG_CODE( double savedResult = result; )
+
+ // Clamp to ensure that result >= PrevResult, or not to far in the future.
+ // Future clamp primarily happens in the very beginning if we are de-queuing
+ // system buffer full of samples.
+ if (result < prevResult)
+ {
+ result = prevResult;
+ }
+ if (result > (systemTime + FilterSettings.FutureClamp))
+ {
+ result = (systemTime + FilterSettings.FutureClamp);
+ }
+
+ OVR_TIMEFILTER_LOG_CODE(
+
+ // Tag lines that were outside desired range, with '<' or '>'.
+ char rangeClamp = ' ';
+ char resultDeltaFar = ' ';
+
+ if (savedResult > (systemTime + 0.0000001))
+ rangeClamp = '>';
+ if (savedResult < prevResult)
+ rangeClamp = '<';
+
+ // Tag any result delta outside desired threshold with a '*'.
+ if (fabs(deviceTimeDelta - (result - prevResult)) >= 0.00002)
+ resultDeltaFar = '*';
+
+ fprintf(pTFLogFile, "Res%s = %13.7f, dt = % 8.7f, ClkD = %13.6f "
+ "sysT = %13.6f, sysDt = %f, "
+ "sysDiff = % f, devT = %11.6f, ddevT = %9.6f %c%c\n",
+ debugTag, result, result - prevResult, ClockDelta,
+ systemTime, systemTime - PrevSystemTime,
+ -(systemTime - result), // Negatives in the past, positive > now.
+ sampleDeviceTime, deviceTimeDelta, rangeClamp, resultDeltaFar);
+
+ ) // OVR_TIMEFILTER_LOG_CODE()
+ OVR_UNUSED(debugTag);
+
+ // Record prior values. Useful or logging and clamping.
+ PrevSystemTime = systemTime;
+
+ return result;
+}
+
+
+void SensorTimeFilter::initClockSampling(double sampleDeviceTime, double clockDelta)
+{
+ ClockInitialized = true;
+ ClockDelta = clockDelta;
+ ClockDeltaDriftPerSecond = 0;
+ OldClockDeltaDriftExpire = 0;
+ ClockDeltaCorrectSecondsLeft = 0;
+ ClockDeltaCorrectPerSecond = 0;
+
+ MinWindowsCollected = 0;
+ MinWindowDuration = 0.25;
+ MinWindowClockDelta = clockDelta;
+ MinWindowLastTime = sampleDeviceTime + MinWindowDuration;
+ MinWindowSamples = 0;
+}
+
+
+void SensorTimeFilter::processFinishedMinWindow(double sampleDeviceTime, double clockDelta)
+{
+ MinRecord newRec = { MinWindowClockDelta, sampleDeviceTime };
+
+ double clockDeltaDiff = MinWindowClockDelta - ClockDelta;
+ double absClockDeltaDiff = fabs(clockDeltaDiff);
+
+
+ // Abrupt change causes Reset of minClockDelta collection.
+ // > 8 ms would a Large jump in a minimum sample, as those are usually stable.
+ // > 1 second intantaneous jump would land us here as well, as that would imply
+ // device being suspended, clock wrap or some other unexpected issue.
+ if ((absClockDeltaDiff > 0.008) ||
+ ((sampleDeviceTime - LastLargestDeviceTime) >= 1.0))
+ {
+ OVR_TIMEFILTER_LOG_CODE(
+ fprintf(pTFLogFile,
+ "\nMinWindow Finished: %d Samples, MinWindowClockDelta=%f, MW-CD=%f,"
+ " ** ClockDelta Reset **\n\n",
+ MinWindowSamples, MinWindowClockDelta, MinWindowClockDelta-ClockDelta);
+ )
+
+ // Use old collected ClockDeltaDriftPerSecond drift value
+ // up to 1 minute until we collect better samples.
+ if (!MinRecords.IsEmpty())
+ {
+ OldClockDeltaDriftExpire = MinRecords.GetNewest().LastSampleDeviceTime -
+ MinRecords.GetOldest().LastSampleDeviceTime;
+ if (OldClockDeltaDriftExpire > 60.0)
+ OldClockDeltaDriftExpire = 60.0;
+ OldClockDeltaDriftExpire += sampleDeviceTime;
+ }
+
+ // Jump to new ClockDelta value.
+ if ((sampleDeviceTime - LastLargestDeviceTime) > 1.0)
+ ClockDelta = clockDelta;
+ else
+ ClockDelta = MinWindowClockDelta;
+
+ ClockDeltaCorrectSecondsLeft = 0;
+ ClockDeltaCorrectPerSecond = 0;
+
+ // Reset buffers, we'll be collecting a new MinWindow.
+ MinRecords.Reset();
+ MinWindowsCollected = 0;
+ MinWindowDuration = 0.25;
+ MinWindowSamples = 0;
+ }
+ else
+ {
+ OVR_ASSERT(MinWindowSamples >= FilterSettings.MinSamples);
+
+ double timeElapsed = 0;
+
+ // If we have older values, use them to update clock drift in
+ // ClockDeltaDriftPerSecond
+ if (!MinRecords.IsEmpty() && (sampleDeviceTime > OldClockDeltaDriftExpire))
+ {
+ MinRecord rec = MinRecords.GetOldest();
+
+ // Compute clock rate of drift.
+ timeElapsed = sampleDeviceTime - rec.LastSampleDeviceTime;
+
+ // Check for divide by zero shouldn't be necessary here, but just be be safe...
+ if (timeElapsed > 0.000001)
+ {
+ ClockDeltaDriftPerSecond = (MinWindowClockDelta - rec.MinClockDelta) / timeElapsed;
+ ClockDeltaDriftPerSecond = clampRate(ClockDeltaDriftPerSecond,
+ FilterSettings.MaxChangeRate);
+ }
+ else
+ {
+ ClockDeltaDriftPerSecond = 0.0;
+ }
+ }
+
+ MinRecords.AddRecord(newRec);
+
+
+ // Catchup correction nudges ClockDelta towards MinWindowClockDelta.
+ // These are needed because clock drift correction alone is not enough
+ // for past accumulated error/high-granularity clock delta changes.
+ // The further away we are, the stronger correction we apply.
+ // Correction has timeout, as we don't want it to overshoot in case
+ // of a large delay between samples.
+
+ if (absClockDeltaDiff >= 0.00125)
+ {
+ // Correct large discrepancy immediately.
+ if (absClockDeltaDiff > 0.00175)
+ {
+ if (clockDeltaDiff > 0)
+ ClockDelta += (clockDeltaDiff - 0.00175);
+ else
+ ClockDelta += (clockDeltaDiff + 0.00175);
+
+ clockDeltaDiff = MinWindowClockDelta - ClockDelta;
+ }
+
+ ClockDeltaCorrectPerSecond = clockDeltaDiff;
+ ClockDeltaCorrectSecondsLeft = 1.0;
+ }
+ else if (absClockDeltaDiff > 0.0005)
+ {
+ ClockDeltaCorrectPerSecond = clockDeltaDiff / 8.0;
+ ClockDeltaCorrectSecondsLeft = 8.0;
+ }
+ else
+ {
+ ClockDeltaCorrectPerSecond = clockDeltaDiff / 15.0;
+ ClockDeltaCorrectSecondsLeft = 15.0;
+ }
+
+ ClockDeltaCorrectPerSecond = clampRate(ClockDeltaCorrectPerSecond,
+ FilterSettings.MaxCorrectRate);
+
+ OVR_TIMEFILTER_LOG_CODE(
+ fprintf(pTFLogFile,
+ "\nMinWindow Finished: %d Samples, MinWindowClockDelta=%f, MW-CD=%f,"
+ " tileElapsed=%f, ClockChange=%f, ClockCorrect=%f\n\n",
+ MinWindowSamples, MinWindowClockDelta, MinWindowClockDelta-ClockDelta,
+ timeElapsed, ClockDeltaDriftPerSecond, ClockDeltaCorrectPerSecond);
+ )
+ }
+
+ // New MinClockDelta collection window.
+ // Switch to longer duration after first few windows.
+ MinWindowsCollected ++;
+ if (MinWindowsCollected > 5)
+ MinWindowDuration = 0.5;
+
+ MinWindowClockDelta = clockDelta;
+ MinWindowLastTime = sampleDeviceTime + MinWindowDuration;
+ MinWindowSamples = 0;
+}
+
+
+} // namespace OVR
+
diff --git a/LibOVR/Src/OVR_SensorTimeFilter.h b/LibOVR/Src/OVR_SensorTimeFilter.h
new file mode 100644
index 0000000..409fe66
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorTimeFilter.h
@@ -0,0 +1,226 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_SensorTimeFilter.h
+Content : Class to filter HMD time and convert it to system time
+Created : December 20, 2013
+Author : Michael Antonov
+Notes :
+
+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.
+
+************************************************************************************/
+
+#ifndef OVR_SensorTimeFilter_h
+#define OVR_SensorTimeFilter_h
+
+#include "Kernel/OVR_Types.h"
+
+namespace OVR {
+
+
+//-----------------------------------------------------------------------------------
+// ***** SensorTimeFilter
+
+// SensorTimeFilter converts sample device time, in seconds, to absolute system
+// time. It filter maintains internal state to estimate the following:
+//
+// - Difference between system and device time values (ClockDelta).
+// ~= (systemTime - deviceTime)
+// - Drift rate between system and device clocks (ClockDeltaDriftPerSecond).
+//
+// Additionally, the following criteria are enforced:
+// - Resulting samples must be increasing, compared to prevSample.
+// - Returned sample time should not exceed 'now' system time by more then a fixed
+// value.
+// * Ideally this should be 0, however, enforcing this is hard when clocks
+// have high discrete values.
+// - Returned sample AbsoluteTime values deltas are very close to HW samples,
+// adjusted by drift rate. Note that this is not always possible due to clamping,
+// in which case it is better to use ScaleTimeUnit(deviceTimeDelta)
+// for integration.
+//
+// Algorithm: We collect minimum ClockDelta on windows of
+// consecutive samples (500 ms each set). Long term difference between sample
+// set minimums is drift. ClockDelta is also continually nudged towards most recent
+// minimum.
+
+class SensorTimeFilter
+{
+public:
+
+ // It may be desirable to configure these per device/platform.
+ // For example, rates can be tighter for DK2 because of microsecond clock.
+ struct Settings
+ {
+ Settings(int minSamples = 50,
+ double clockDeltaAdjust = -0.0002, // 200 mks in the past.
+ double futureClamp = 0.0008)
+ : MinSamples(minSamples),
+ ClockDeltaAdjust(clockDeltaAdjust),
+ // PastClamp(-0.032),
+ FutureClamp(futureClamp),
+ PastSampleResetSeconds(0.2),
+ MaxChangeRate(0.004),
+ MaxCorrectRate(0.004)
+ { }
+
+ // Minimum number of samples in a window. Different number may be desirable
+ // based on how often samples come in.
+ int MinSamples;
+
+ // Factor always added to ClockDelta, used to skew all values into the past by fixed
+ // value and reduce the chances we report a sample "in the future".
+ double ClockDeltaAdjust;
+ // How much away in a past can a sample be before being shifted closer to system time.
+ //double PastClamp;
+ // How much larger then systemTime can a value be? Set to 0 to clamp to null,
+ // put small positive value is better.
+ double FutureClamp;
+
+ // How long (in system time) do we take to reset the system if a device sample.
+ // comes in the past. Generally, this should never happened, but exists as a way to
+ // address bad timing coming form firmware (temp CCove issue, presumably fixed)
+ // or buggy input.
+ double PastSampleResetSeconds;
+
+ // Maximum drift change and near-term correction rates, in seconds.
+ double MaxChangeRate;
+ double MaxCorrectRate;
+ };
+
+
+ SensorTimeFilter(const Settings& settings = Settings());
+
+
+ // Convert device sample time to system time, driving clock drift estimation.
+ // Input: SampleTime, System Time
+ // Return: Absolute system time for sample
+ double SampleToSystemTime(double sampleDeviceTime, double systemTime,
+ double prevResult, const char* debugTag = "");
+
+
+ // Scales device time to account for drift.
+ double ScaleTimeUnit(double deviceClockDelta)
+ {
+ return deviceClockDelta * (1.0 + ClockDeltaDriftPerSecond);
+ }
+
+ // Return currently estimated difference between the clocks.
+ double GetClockDelta() const { return ClockDelta; }
+
+
+private:
+
+ void initClockSampling(double sampleDeviceTime, double clockDelta);
+ void processFinishedMinWindow(double sampleDeviceTime, double systemTime);
+
+ static double clampRate(double rate, double limit)
+ {
+ if (rate > limit)
+ rate = limit;
+ else if (rate < -limit)
+ rate = -limit;
+ return rate;
+ }
+
+
+ // Describes minimum observed ClockDelta for sample set seen in the past.
+ struct MinRecord
+ {
+ double MinClockDelta;
+ double LastSampleDeviceTime;
+ };
+
+ // Circular buffer storing MinRecord(s) several minutes into the past.
+ // Oldest value here is used to help estimate drift.
+ class MinRecordBuffer
+ {
+ enum { BufferSize = 60*6 }; // 3 min
+ public:
+
+ MinRecordBuffer() : Head(0), Tail(0) { }
+
+ void Reset() { Head = Tail = 0; }
+ bool IsEmpty() const { return Head == Tail; }
+
+ const MinRecord& GetOldest() const
+ {
+ OVR_ASSERT(!IsEmpty());
+ return Records[Tail];
+ }
+ const MinRecord& GetNewest() const
+ {
+ OVR_ASSERT(!IsEmpty());
+ return Records[(BufferSize + Head - 1) % BufferSize];
+ }
+
+ void AddRecord(const MinRecord& rec)
+ {
+ Records[Head] = rec;
+ Head = advanceIndex(Head);
+ if (Head == Tail)
+ Tail = advanceIndex(Tail);
+ }
+
+ private:
+
+ static int advanceIndex(int index)
+ {
+ index++;
+ if (index >= BufferSize)
+ index = 0;
+ return index;
+ }
+
+ MinRecord Records[BufferSize];
+ int Head; // Location we will most recent entry, unused.
+ int Tail; // Oldest entry.
+ };
+
+
+ Settings FilterSettings;
+
+ // Clock correction state.
+ bool ClockInitialized;
+ double ClockDelta;
+ double ClockDeltaDriftPerSecond;
+ double ClockDeltaCorrectPerSecond;
+ double ClockDeltaCorrectSecondsLeft;
+ double OldClockDeltaDriftExpire;
+
+ double LastLargestDeviceTime;
+ double PrevSystemTime;
+ // Used to reset timing if we get multiple "samples in the past"
+ double PastSampleResetTime;
+
+ // "MinWindow" is a block of time during which minimum ClockDelta values
+ // are collected into MinWindowClockDelta.
+ int MinWindowsCollected;
+ double MinWindowDuration; // Device sample seconds
+ double MinWindowLastTime;
+ double MinWindowClockDelta;
+ int MinWindowSamples;
+
+ // Historic buffer used to determine rate of clock change over time.
+ MinRecordBuffer MinRecords;
+};
+
+} // namespace OVR
+
+#endif // OVR_SensorTimeFilter_h
diff --git a/LibOVR/Src/OVR_Stereo.cpp b/LibOVR/Src/OVR_Stereo.cpp
new file mode 100644
index 0000000..936a02a
--- /dev/null
+++ b/LibOVR/Src/OVR_Stereo.cpp
@@ -0,0 +1,1805 @@
+/************************************************************************************
+
+Filename : OVR_Stereo.cpp
+Content : Stereo rendering functions
+Created : November 30, 2013
+Authors : Tom Fosyth
+
+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_Stereo.h"
+#include "OVR_Profile.h"
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_Alg.h"
+
+//To allow custom distortion to be introduced to CatMulSpline.
+float (*CustomDistortion)(float) = NULL;
+float (*CustomDistortionInv)(float) = NULL;
+
+
+namespace OVR {
+
+
+using namespace Alg;
+
+//-----------------------------------------------------------------------------------
+
+// Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3])
+// Result is four coefficients in pResults[0] through pResults[3] such that
+// y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) );
+// passes through all four input points.
+// Return is true if it succeeded, false if it failed (because two control points
+// have the same pFitX value).
+bool FitCubicPolynomial ( float *pResult, const float *pFitX, const float *pFitY )
+{
+ float d0 = ( ( pFitX[0]-pFitX[1] ) * ( pFitX[0]-pFitX[2] ) * ( pFitX[0]-pFitX[3] ) );
+ float d1 = ( ( pFitX[1]-pFitX[2] ) * ( pFitX[1]-pFitX[3] ) * ( pFitX[1]-pFitX[0] ) );
+ float d2 = ( ( pFitX[2]-pFitX[3] ) * ( pFitX[2]-pFitX[0] ) * ( pFitX[2]-pFitX[1] ) );
+ float d3 = ( ( pFitX[3]-pFitX[0] ) * ( pFitX[3]-pFitX[1] ) * ( pFitX[3]-pFitX[2] ) );
+
+ if ( ( d0 == 0.0f ) || ( d1 == 0.0f ) || ( d2 == 0.0f ) || ( d3 == 0.0f ) )
+ {
+ return false;
+ }
+
+ float f0 = pFitY[0] / d0;
+ float f1 = pFitY[1] / d1;
+ float f2 = pFitY[2] / d2;
+ float f3 = pFitY[3] / d3;
+
+ pResult[0] = -( f0*pFitX[1]*pFitX[2]*pFitX[3]
+ + f1*pFitX[0]*pFitX[2]*pFitX[3]
+ + f2*pFitX[0]*pFitX[1]*pFitX[3]
+ + f3*pFitX[0]*pFitX[1]*pFitX[2] );
+ pResult[1] = f0*(pFitX[1]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[1])
+ + f1*(pFitX[0]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[0])
+ + f2*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[3] + pFitX[3]*pFitX[0])
+ + f3*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[2] + pFitX[2]*pFitX[0]);
+ pResult[2] = -( f0*(pFitX[1]+pFitX[2]+pFitX[3])
+ + f1*(pFitX[0]+pFitX[2]+pFitX[3])
+ + f2*(pFitX[0]+pFitX[1]+pFitX[3])
+ + f3*(pFitX[0]+pFitX[1]+pFitX[2]) );
+ pResult[3] = f0 + f1 + f2 + f3;
+
+ return true;
+}
+
+
+
+float EvalCatmullRom10Spline ( float const *K, float scaledVal )
+{
+ int const NumSegments = LensConfig::NumCoefficients;
+
+ float scaledValFloor = floorf ( scaledVal );
+ scaledValFloor = Alg::Max ( 0.0f, Alg::Min ( (float)(NumSegments-1), scaledValFloor ) );
+ float t = scaledVal - scaledValFloor;
+ int k = (int)scaledValFloor;
+
+ float p0, p1;
+ float m0, m1;
+ switch ( k )
+ {
+ case 0:
+ // Curve starts at 1.0 with gradient K[1]-K[0]
+ p0 = 1.0f;
+ m0 = ( K[1] - K[0] ); // general case would have been (K[1]-K[-1])/2
+ p1 = K[1];
+ m1 = 0.5f * ( K[2] - K[0] );
+ break;
+ default:
+ // General case
+ p0 = K[k ];
+ m0 = 0.5f * ( K[k+1] - K[k-1] );
+ p1 = K[k+1];
+ m1 = 0.5f * ( K[k+2] - K[k ] );
+ break;
+ case NumSegments-2:
+ // Last tangent is just the slope of the last two points.
+ p0 = K[NumSegments-2];
+ m0 = 0.5f * ( K[NumSegments-1] - K[NumSegments-2] );
+ p1 = K[NumSegments-1];
+ m1 = K[NumSegments-1] - K[NumSegments-2];
+ break;
+ case NumSegments-1:
+ // Beyond the last segment it's just a straight line
+ p0 = K[NumSegments-1];
+ m0 = K[NumSegments-1] - K[NumSegments-2];
+ p1 = p0 + m0;
+ m1 = m0;
+ break;
+ }
+
+ float omt = 1.0f - t;
+ float res = ( p0 * ( 1.0f + 2.0f * t ) + m0 * t ) * omt * omt
+ + ( p1 * ( 1.0f + 2.0f * omt ) - m1 * omt ) * t * t;
+
+ return res;
+}
+
+
+
+
+// Converts a Profile eyecup string into an eyecup enumeration
+void SetEyeCup(HmdRenderInfo* renderInfo, const char* cup)
+{
+ if (OVR_strcmp(cup, "A") == 0)
+ renderInfo->EyeCups = EyeCup_DK1A;
+ else if (OVR_strcmp(cup, "B") == 0)
+ renderInfo->EyeCups = EyeCup_DK1B;
+ else if (OVR_strcmp(cup, "C") == 0)
+ renderInfo->EyeCups = EyeCup_DK1C;
+ else if (OVR_strcmp(cup, "Orange A") == 0)
+ renderInfo->EyeCups = EyeCup_OrangeA;
+ else if (OVR_strcmp(cup, "Red A") == 0)
+ renderInfo->EyeCups = EyeCup_RedA;
+ else if (OVR_strcmp(cup, "Pink A") == 0)
+ renderInfo->EyeCups = EyeCup_PinkA;
+ else if (OVR_strcmp(cup, "Blue A") == 0)
+ renderInfo->EyeCups = EyeCup_BlueA;
+ else
+ renderInfo->EyeCups = EyeCup_DK1A;
+}
+
+
+
+//-----------------------------------------------------------------------------------
+
+
+// The result is a scaling applied to the distance.
+float LensConfig::DistortionFnScaleRadiusSquared (float rsq) const
+{
+ float scale = 1.0f;
+ switch ( Eqn )
+ {
+ case Distortion_Poly4:
+ // This version is deprecated! Prefer one of the other two.
+ scale = ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) );
+ break;
+ case Distortion_RecipPoly4:
+ scale = 1.0f / ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) );
+ break;
+ case Distortion_CatmullRom10:{
+ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[10]
+ // evenly spaced in R^2 from 0.0 to MaxR^2
+ // K[0] controls the slope at radius=0.0, rather than the actual value.
+ const int NumSegments = LensConfig::NumCoefficients;
+ OVR_ASSERT ( NumSegments <= NumCoefficients );
+ float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxR * MaxR );
+ scale = EvalCatmullRom10Spline ( K, scaledRsq );
+
+
+ //Intercept, and overrule if needed
+ if (CustomDistortion)
+ {
+ scale = CustomDistortion(rsq);
+ }
+
+ }break;
+ default:
+ OVR_ASSERT ( false );
+ break;
+ }
+ return scale;
+}
+
+// x,y,z components map to r,g,b
+Vector3f LensConfig::DistortionFnScaleRadiusSquaredChroma (float rsq) const
+{
+ float scale = DistortionFnScaleRadiusSquared ( rsq );
+ Vector3f scaleRGB;
+ scaleRGB.x = scale * ( 1.0f + ChromaticAberration[0] + rsq * ChromaticAberration[1] ); // Red
+ scaleRGB.y = scale; // Green
+ scaleRGB.z = scale * ( 1.0f + ChromaticAberration[2] + rsq * ChromaticAberration[3] ); // Blue
+ return scaleRGB;
+}
+
+// DistortionFnInverse computes the inverse of the distortion function on an argument.
+float LensConfig::DistortionFnInverse(float r) const
+{
+ OVR_ASSERT((r <= 20.0f));
+
+ float s, d;
+ float delta = r * 0.25f;
+
+ // Better to start guessing too low & take longer to converge than too high
+ // and hit singularities. Empirically, r * 0.5f is too high in some cases.
+ s = r * 0.25f;
+ d = fabs(r - DistortionFn(s));
+
+ for (int i = 0; i < 20; i++)
+ {
+ float sUp = s + delta;
+ float sDown = s - delta;
+ float dUp = fabs(r - DistortionFn(sUp));
+ float dDown = fabs(r - DistortionFn(sDown));
+
+ if (dUp < d)
+ {
+ s = sUp;
+ d = dUp;
+ }
+ else if (dDown < d)
+ {
+ s = sDown;
+ d = dDown;
+ }
+ else
+ {
+ delta *= 0.5f;
+ }
+ }
+
+ return s;
+}
+
+
+
+float LensConfig::DistortionFnInverseApprox(float r) const
+{
+ float rsq = r * r;
+ float scale = 1.0f;
+ switch ( Eqn )
+ {
+ case Distortion_Poly4:
+ // Deprecated
+ OVR_ASSERT ( false );
+ break;
+ case Distortion_RecipPoly4:
+ scale = 1.0f / ( InvK[0] + rsq * ( InvK[1] + rsq * ( InvK[2] + rsq * InvK[3] ) ) );
+ break;
+ case Distortion_CatmullRom10:{
+ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[9]
+ // evenly spaced in R^2 from 0.0 to MaxR^2
+ // K[0] controls the slope at radius=0.0, rather than the actual value.
+ const int NumSegments = LensConfig::NumCoefficients;
+ OVR_ASSERT ( NumSegments <= NumCoefficients );
+ float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxInvR * MaxInvR );
+ scale = EvalCatmullRom10Spline ( InvK, scaledRsq );
+
+ //Intercept, and overrule if needed
+ if (CustomDistortionInv)
+ {
+ scale = CustomDistortionInv(rsq);
+ }
+
+ }break;
+ default:
+ OVR_ASSERT ( false );
+ break;
+ }
+ return r * scale;
+}
+
+void LensConfig::SetUpInverseApprox()
+{
+ float maxR = MaxInvR;
+
+ switch ( Eqn )
+ {
+ case Distortion_Poly4:
+ // Deprecated
+ OVR_ASSERT ( false );
+ break;
+ case Distortion_RecipPoly4:{
+
+ float sampleR[4];
+ float sampleRSq[4];
+ float sampleInv[4];
+ float sampleFit[4];
+
+ // Found heuristically...
+ sampleR[0] = 0.0f;
+ sampleR[1] = maxR * 0.4f;
+ sampleR[2] = maxR * 0.8f;
+ sampleR[3] = maxR * 1.5f;
+ for ( int i = 0; i < 4; i++ )
+ {
+ sampleRSq[i] = sampleR[i] * sampleR[i];
+ sampleInv[i] = DistortionFnInverse ( sampleR[i] );
+ sampleFit[i] = sampleR[i] / sampleInv[i];
+ }
+ sampleFit[0] = 1.0f;
+ FitCubicPolynomial ( InvK, sampleRSq, sampleFit );
+
+ #if 0
+ // Should be a nearly exact match on the chosen points.
+ OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[0] ) - DistortionFnInverseApprox ( sampleR[0] ) ) / maxR < 0.0001f );
+ OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[1] ) - DistortionFnInverseApprox ( sampleR[1] ) ) / maxR < 0.0001f );
+ OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[2] ) - DistortionFnInverseApprox ( sampleR[2] ) ) / maxR < 0.0001f );
+ OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[3] ) - DistortionFnInverseApprox ( sampleR[3] ) ) / maxR < 0.0001f );
+ // Should be a decent match on the rest of the range.
+ const int maxCheck = 20;
+ for ( int i = 0; i < maxCheck; i++ )
+ {
+ float checkR = (float)i * maxR / (float)maxCheck;
+ float realInv = DistortionFnInverse ( checkR );
+ float testInv = DistortionFnInverseApprox ( checkR );
+ float error = fabsf ( realInv - testInv ) / maxR;
+ OVR_ASSERT ( error < 0.1f );
+ }
+ #endif
+
+ }break;
+ case Distortion_CatmullRom10:{
+
+ const int NumSegments = LensConfig::NumCoefficients;
+ OVR_ASSERT ( NumSegments <= NumCoefficients );
+ for ( int i = 1; i < NumSegments; i++ )
+ {
+ float scaledRsq = (float)i;
+ float rsq = scaledRsq * MaxInvR * MaxInvR / (float)( NumSegments - 1);
+ float r = sqrtf ( rsq );
+ float inv = DistortionFnInverse ( r );
+ InvK[i] = inv / r;
+ InvK[0] = 1.0f; // TODO: fix this.
+ }
+
+#if 0
+ const int maxCheck = 20;
+ for ( int i = 0; i <= maxCheck; i++ )
+ {
+ float checkR = (float)i * MaxInvR / (float)maxCheck;
+ float realInv = DistortionFnInverse ( checkR );
+ float testInv = DistortionFnInverseApprox ( checkR );
+ float error = fabsf ( realInv - testInv ) / MaxR;
+ OVR_ASSERT ( error < 0.01f );
+ }
+#endif
+
+ }break;
+
+ default:
+ break;
+ }
+}
+
+
+void LensConfig::SetToIdentity()
+{
+ for ( int i = 0; i < NumCoefficients; i++ )
+ {
+ K[i] = 0.0f;
+ InvK[i] = 0.0f;
+ }
+ Eqn = Distortion_RecipPoly4;
+ K[0] = 1.0f;
+ InvK[0] = 1.0f;
+ MaxR = 1.0f;
+ MaxInvR = 1.0f;
+ ChromaticAberration[0] = 0.0f;
+ ChromaticAberration[1] = 0.0f;
+ ChromaticAberration[2] = 0.0f;
+ ChromaticAberration[3] = 0.0f;
+ MetersPerTanAngleAtCenter = 0.05f;
+}
+
+
+enum LensConfigStoredVersion
+{
+ LCSV_CatmullRom10Version1 = 1
+};
+
+// DO NOT CHANGE THESE ONCE THEY HAVE BEEN BAKED INTO FIRMWARE.
+// If something needs to change, add a new one!
+struct LensConfigStored_CatmullRom10Version1
+{
+ // All these items must be fixed-length integers - no "float", no "int", etc.
+ UInt16 VersionNumber; // Must be LCSV_CatmullRom10Version1
+
+ UInt16 K[11];
+ UInt16 MaxR;
+ UInt16 MetersPerTanAngleAtCenter;
+ UInt16 ChromaticAberration[4];
+ // InvK and MaxInvR are calculated on load.
+};
+
+UInt16 EncodeFixedPointUInt16 ( float val, UInt16 zeroVal, int fractionalBits )
+{
+ OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) );
+ float valWhole = val * (float)( 1 << fractionalBits );
+ valWhole += (float)zeroVal + 0.5f;
+ valWhole = floorf ( valWhole );
+ OVR_ASSERT ( ( valWhole >= 0.0f ) && ( valWhole < (float)( 1 << 16 ) ) );
+ return (UInt16)valWhole;
+}
+
+float DecodeFixedPointUInt16 ( UInt16 val, UInt16 zeroVal, int fractionalBits )
+{
+ OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) );
+ float valFloat = (float)val;
+ valFloat -= (float)zeroVal;
+ valFloat *= 1.0f / (float)( 1 << fractionalBits );
+ return valFloat;
+}
+
+
+// Returns true on success.
+bool LoadLensConfig ( LensConfig *presult, UByte const *pbuffer, int bufferSizeInBytes )
+{
+ if ( bufferSizeInBytes < 2 )
+ {
+ // Can't even tell the version number!
+ return false;
+ }
+ UInt16 version = DecodeUInt16 ( pbuffer + 0 );
+ switch ( version )
+ {
+ case LCSV_CatmullRom10Version1:
+ {
+ if ( bufferSizeInBytes < (int)sizeof(LensConfigStored_CatmullRom10Version1) )
+ {
+ return false;
+ }
+ LensConfigStored_CatmullRom10Version1 lcs;
+ lcs.VersionNumber = DecodeUInt16 ( pbuffer + 0 );
+ for ( int i = 0; i < 11; i++ )
+ {
+ lcs.K[i] = DecodeUInt16 ( pbuffer + 2 + 2*i );
+ }
+ lcs.MaxR = DecodeUInt16 ( pbuffer + 24 );
+ lcs.MetersPerTanAngleAtCenter = DecodeUInt16 ( pbuffer + 26 );
+ for ( int i = 0; i < 4; i++ )
+ {
+ lcs.ChromaticAberration[i] = DecodeUInt16 ( pbuffer + 28 + 2*i );
+ }
+ OVR_COMPILER_ASSERT ( sizeof(lcs) == 36 );
+
+ // Convert to the real thing.
+ LensConfig result;
+ result.Eqn = Distortion_CatmullRom10;
+ for ( int i = 0; i < 11; i++ )
+ {
+ // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0.
+ result.K[i] = DecodeFixedPointUInt16 ( lcs.K[i], 0, 14 );
+ }
+ // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov),
+ // but may get arbitrarily high. tan(76)=4 is a very reasonable limit!
+ result.MaxR = DecodeFixedPointUInt16 ( lcs.MaxR, 0, 14 );
+ // MetersPerTanAngleAtCenter is also known as focal length!
+ // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction)
+ result.MetersPerTanAngleAtCenter = DecodeFixedPointUInt16 ( lcs.MetersPerTanAngleAtCenter, 0, 16+3 );
+ for ( int i = 0; i < 4; i++ )
+ {
+ // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction)
+ result.ChromaticAberration[i] = DecodeFixedPointUInt16 ( lcs.ChromaticAberration[i], 0x8000, 16+3 );
+ }
+ result.MaxInvR = result.DistortionFn ( result.MaxR );
+ result.SetUpInverseApprox();
+
+ OVR_ASSERT ( version == lcs.VersionNumber );
+
+ *presult = result;
+ }
+ break;
+ default:
+ // Unknown format.
+ return false;
+ break;
+ }
+ return true;
+}
+
+// Returns number of bytes needed.
+int SaveLensConfigSizeInBytes ( LensConfig const &config )
+{
+ OVR_UNUSED ( config );
+ return sizeof ( LensConfigStored_CatmullRom10Version1 );
+}
+
+// Returns true on success.
+bool SaveLensConfig ( UByte *pbuffer, int bufferSizeInBytes, LensConfig const &config )
+{
+ if ( bufferSizeInBytes < (int)sizeof ( LensConfigStored_CatmullRom10Version1 ) )
+ {
+ return false;
+ }
+
+ // Construct the values.
+ LensConfigStored_CatmullRom10Version1 lcs;
+ lcs.VersionNumber = LCSV_CatmullRom10Version1;
+ for ( int i = 0; i < 11; i++ )
+ {
+ // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0.
+ lcs.K[i] = EncodeFixedPointUInt16 ( config.K[i], 0, 14 );
+ }
+ // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov),
+ // but may get arbitrarily high. tan(76)=4 is a very reasonable limit!
+ lcs.MaxR = EncodeFixedPointUInt16 ( config.MaxR, 0, 14 );
+ // MetersPerTanAngleAtCenter is also known as focal length!
+ // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction)
+ lcs.MetersPerTanAngleAtCenter = EncodeFixedPointUInt16 ( config.MetersPerTanAngleAtCenter, 0, 16+3 );
+ for ( int i = 0; i < 4; i++ )
+ {
+ // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction)
+ lcs.ChromaticAberration[i] = EncodeFixedPointUInt16 ( config.ChromaticAberration[i], 0x8000, 16+3 );
+ }
+
+
+ // Now store them out, sensitive to endinness.
+ EncodeUInt16 ( pbuffer + 0, lcs.VersionNumber );
+ for ( int i = 0; i < 11; i++ )
+ {
+ EncodeUInt16 ( pbuffer + 2 + 2*i, lcs.K[i] );
+ }
+ EncodeUInt16 ( pbuffer + 24, lcs.MaxR );
+ EncodeUInt16 ( pbuffer + 26, lcs.MetersPerTanAngleAtCenter );
+ for ( int i = 0; i < 4; i++ )
+ {
+ EncodeUInt16 ( pbuffer + 28 + 2*i, lcs.ChromaticAberration[i] );
+ }
+ OVR_COMPILER_ASSERT ( 36 == sizeof(lcs) );
+
+ return true;
+}
+
+#ifdef OVR_BUILD_DEBUG
+void TestSaveLoadLensConfig ( LensConfig const &config )
+{
+ OVR_ASSERT ( config.Eqn == Distortion_CatmullRom10 );
+ // As a test, make sure this can be encoded and decoded correctly.
+ const int bufferSize = 256;
+ UByte buffer[bufferSize];
+ OVR_ASSERT ( SaveLensConfigSizeInBytes ( config ) < bufferSize );
+ bool success;
+ success = SaveLensConfig ( buffer, bufferSize, config );
+ OVR_ASSERT ( success );
+ LensConfig testConfig;
+ success = LoadLensConfig ( &testConfig, buffer, bufferSize );
+ OVR_ASSERT ( success );
+ OVR_ASSERT ( testConfig.Eqn == config.Eqn );
+ for ( int i = 0; i < 11; i++ )
+ {
+ OVR_ASSERT ( fabs ( testConfig.K[i] - config.K[i] ) < 0.0001f );
+ }
+ OVR_ASSERT ( fabsf ( testConfig.MaxR - config.MaxR ) < 0.0001f );
+ OVR_ASSERT ( fabsf ( testConfig.MetersPerTanAngleAtCenter - config.MetersPerTanAngleAtCenter ) < 0.00001f );
+ for ( int i = 0; i < 4; i++ )
+ {
+ OVR_ASSERT ( fabsf ( testConfig.ChromaticAberration[i] - config.ChromaticAberration[i] ) < 0.00001f );
+ }
+}
+#endif
+
+
+
+//-----------------------------------------------------------------------------------
+
+// TBD: There is a question of whether this is the best file for CreateDebugHMDInfo. As long as there are many
+// constants for HmdRenderInfo here as well it is ok. The alternative would be OVR_Common_HMDDevice.cpp, but
+// that's specialized per platform... should probably move it there onces the code is in the common base class.
+
+HMDInfo CreateDebugHMDInfo(HmdTypeEnum hmdType)
+{
+ HMDInfo info;
+
+ if ((hmdType != HmdType_DK1) &&
+ (hmdType != HmdType_CrystalCoveProto))
+ {
+ LogText("Debug HMDInfo - HmdType not supported. Defaulting to DK1.\n");
+ hmdType = HmdType_DK1;
+ }
+
+ // The alternative would be to initialize info.HmdType to HmdType_None instead. If we did that,
+ // code wouldn't be "maximally compatible" and devs wouldn't know what device we are
+ // simulating... so if differentiation becomes necessary we better add Debug flag in the future.
+ info.HmdType = hmdType;
+ info.Manufacturer = "Oculus VR";
+
+ switch(hmdType)
+ {
+ case HmdType_DK1:
+ info.ProductName = "Oculus Rift DK1";
+ info.ResolutionInPixels = Sizei ( 1280, 800 );
+ info.ScreenSizeInMeters = Sizef ( 0.1498f, 0.0936f );
+ info.ScreenGapSizeInMeters = 0.0f;
+ info.CenterFromTopInMeters = 0.0468f;
+ info.LensSeparationInMeters = 0.0635f;
+ info.Shutter.Type = HmdShutter_RollingTopToBottom;
+ info.Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ info.Shutter.VsyncToFirstScanline = 0.000052f;
+ info.Shutter.FirstScanlineToLastScanline = 0.016580f;
+ info.Shutter.PixelSettleTime = 0.015f;
+ info.Shutter.PixelPersistence = ( 1.0f / 60.0f );
+ break;
+
+ case HmdType_CrystalCoveProto:
+ info.ProductName = "Oculus Rift Crystal Cove";
+ info.ResolutionInPixels = Sizei ( 1920, 1080 );
+ info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f );
+ info.ScreenGapSizeInMeters = 0.0f;
+ info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f;
+ info.LensSeparationInMeters = 0.0635f;
+ info.Shutter.Type = HmdShutter_RollingRightToLeft;
+ info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ info.Shutter.VsyncToFirstScanline = 0.0000273f;
+ info.Shutter.FirstScanlineToLastScanline = 0.0131033f;
+ info.Shutter.PixelSettleTime = 0.0f;
+ info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync;
+ break;
+
+ case HmdType_DK2:
+ info.ProductName = "Oculus Rift DK2";
+ info.ResolutionInPixels = Sizei ( 1920, 1080 );
+ info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f );
+ info.ScreenGapSizeInMeters = 0.0f;
+ info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f;
+ info.LensSeparationInMeters = 0.0635f;
+ info.Shutter.Type = HmdShutter_RollingRightToLeft;
+ info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
+ info.Shutter.VsyncToFirstScanline = 0.0000273f;
+ info.Shutter.FirstScanlineToLastScanline = 0.0131033f;
+ info.Shutter.PixelSettleTime = 0.0f;
+ info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync;
+ break;
+
+ default:
+ break;
+ }
+
+ return info;
+}
+
+
+
+// profile may be NULL, in which case it uses the hard-coded defaults.
+HmdRenderInfo GenerateHmdRenderInfoFromHmdInfo ( HMDInfo const &hmdInfo,
+ Profile const *profile /*=NULL*/,
+ DistortionEqnType distortionType /*= Distortion_CatmullRom10*/,
+ EyeCupType eyeCupOverride /*= EyeCup_LAST*/ )
+{
+ HmdRenderInfo renderInfo;
+
+ renderInfo.HmdType = hmdInfo.HmdType;
+ renderInfo.ResolutionInPixels = hmdInfo.ResolutionInPixels;
+ renderInfo.ScreenSizeInMeters = hmdInfo.ScreenSizeInMeters;
+ renderInfo.CenterFromTopInMeters = hmdInfo.CenterFromTopInMeters;
+ renderInfo.ScreenGapSizeInMeters = hmdInfo.ScreenGapSizeInMeters;
+ renderInfo.LensSeparationInMeters = hmdInfo.LensSeparationInMeters;
+
+ OVR_ASSERT ( sizeof(renderInfo.Shutter) == sizeof(hmdInfo.Shutter) ); // Try to keep the files in sync!
+ renderInfo.Shutter.Type = hmdInfo.Shutter.Type;
+ renderInfo.Shutter.VsyncToNextVsync = hmdInfo.Shutter.VsyncToNextVsync;
+ renderInfo.Shutter.VsyncToFirstScanline = hmdInfo.Shutter.VsyncToFirstScanline;
+ renderInfo.Shutter.FirstScanlineToLastScanline = hmdInfo.Shutter.FirstScanlineToLastScanline;
+ renderInfo.Shutter.PixelSettleTime = hmdInfo.Shutter.PixelSettleTime;
+ renderInfo.Shutter.PixelPersistence = hmdInfo.Shutter.PixelPersistence;
+
+ renderInfo.LensDiameterInMeters = 0.035f;
+ renderInfo.LensSurfaceToMidplateInMeters = 0.025f;
+ renderInfo.EyeCups = EyeCup_DK1A;
+
+#if 0 // Device settings are out of date - don't use them.
+ if (Contents & Contents_Distortion)
+ {
+ memcpy(renderInfo.DistortionK, DistortionK, sizeof(float)*4);
+ renderInfo.DistortionEqn = Distortion_RecipPoly4;
+ }
+#endif
+
+ // Defaults in case of no user profile.
+ renderInfo.EyeLeft.NoseToPupilInMeters = 0.032f;
+ renderInfo.EyeLeft.ReliefInMeters = 0.012f;
+
+ // 10mm eye-relief laser numbers for DK1 lenses.
+ // These are a decent seed for finding eye-relief and IPD.
+ // These are NOT used for rendering!
+ // Rendering distortions are now in GenerateLensConfigFromEyeRelief()
+ // So, if you're hacking in new distortions, don't do it here!
+ renderInfo.EyeLeft.Distortion.SetToIdentity();
+ renderInfo.EyeLeft.Distortion.MetersPerTanAngleAtCenter = 0.0449f;
+ renderInfo.EyeLeft.Distortion.Eqn = Distortion_RecipPoly4;
+ renderInfo.EyeLeft.Distortion.K[0] = 1.0f;
+ renderInfo.EyeLeft.Distortion.K[1] = -0.494165344f;
+ renderInfo.EyeLeft.Distortion.K[2] = 0.587046423f;
+ renderInfo.EyeLeft.Distortion.K[3] = -0.841887126f;
+ renderInfo.EyeLeft.Distortion.MaxR = 1.0f;
+
+ renderInfo.EyeLeft.Distortion.ChromaticAberration[0] = -0.006f;
+ renderInfo.EyeLeft.Distortion.ChromaticAberration[1] = 0.0f;
+ renderInfo.EyeLeft.Distortion.ChromaticAberration[2] = 0.014f;
+ renderInfo.EyeLeft.Distortion.ChromaticAberration[3] = 0.0f;
+
+ renderInfo.EyeRight = renderInfo.EyeLeft;
+
+
+ // Obtain data from profile.
+ if ( profile != NULL )
+ {
+ char eyecup[16];
+ if (profile->GetValue(OVR_KEY_EYE_CUP, eyecup, 16))
+ SetEyeCup(&renderInfo, eyecup);
+ }
+
+ switch ( hmdInfo.HmdType )
+ {
+ case HmdType_None:
+ case HmdType_DKProto:
+ case HmdType_DK1:
+ // Slight hack to improve usability.
+ // If you have a DKHD-style lens profile enabled,
+ // but you plug in DK1 and forget to change the profile,
+ // obviously you don't want those lens numbers.
+ if ( ( renderInfo.EyeCups != EyeCup_DK1A ) &&
+ ( renderInfo.EyeCups != EyeCup_DK1B ) &&
+ ( renderInfo.EyeCups != EyeCup_DK1C ) )
+ {
+ renderInfo.EyeCups = EyeCup_DK1A;
+ }
+ break;
+
+ case HmdType_DKHD2Proto:
+ renderInfo.EyeCups = EyeCup_DKHD2A;
+ break;
+ case HmdType_CrystalCoveProto:
+ renderInfo.EyeCups = EyeCup_PinkA;
+ break;
+ case HmdType_DK2:
+ renderInfo.EyeCups = EyeCup_DK2A;
+ break;
+ default:
+ break;
+ }
+
+ if ( eyeCupOverride != EyeCup_LAST )
+ {
+ renderInfo.EyeCups = eyeCupOverride;
+ }
+
+ switch ( renderInfo.EyeCups )
+ {
+ case EyeCup_DK1A:
+ case EyeCup_DK1B:
+ case EyeCup_DK1C:
+ renderInfo.LensDiameterInMeters = 0.035f;
+ renderInfo.LensSurfaceToMidplateInMeters = 0.02357f;
+ // Not strictly lens-specific, but still wise to set a reasonable default for relief.
+ renderInfo.EyeLeft.ReliefInMeters = 0.010f;
+ renderInfo.EyeRight.ReliefInMeters = 0.010f;
+ break;
+ case EyeCup_DKHD2A:
+ renderInfo.LensDiameterInMeters = 0.035f;
+ renderInfo.LensSurfaceToMidplateInMeters = 0.02357f;
+ // Not strictly lens-specific, but still wise to set a reasonable default for relief.
+ renderInfo.EyeLeft.ReliefInMeters = 0.010f;
+ renderInfo.EyeRight.ReliefInMeters = 0.010f;
+ break;
+ case EyeCup_PinkA:
+ case EyeCup_DK2A:
+ renderInfo.LensDiameterInMeters = 0.04f; // approximate
+ renderInfo.LensSurfaceToMidplateInMeters = 0.01965f;
+ // Not strictly lens-specific, but still wise to set a reasonable default for relief.
+ renderInfo.EyeLeft.ReliefInMeters = 0.012f;
+ renderInfo.EyeRight.ReliefInMeters = 0.012f;
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+ if ( profile != NULL )
+ {
+ // Set the customized user eye position
+ // TBD: Maybe we should separate custom camera positioning from custom distortion rendering ??
+ if (profile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, true))
+ {
+ float eye2nose[2];
+ if (profile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, eye2nose, 2) == 2)
+ { // Load per-eye half-IPD
+ renderInfo.EyeLeft.NoseToPupilInMeters = eye2nose[0];
+ renderInfo.EyeRight.NoseToPupilInMeters = eye2nose[1];
+ }
+ else
+ { // Use a centered IPD instead
+ float ipd = profile->GetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD);
+ renderInfo.EyeLeft.NoseToPupilInMeters = 0.5f * ipd;
+ renderInfo.EyeRight.NoseToPupilInMeters = 0.5f * ipd;
+ }
+
+ float eye2plate[2];
+ if (profile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, eye2plate, 2) == 2)
+ { // Subtract the eye-cup height from the plate distance to get the eye-to-lens distance
+ // This measurement should be the the distance at maximum dial setting
+ // We still need to adjust with the dial offset
+ renderInfo.EyeLeft.ReliefInMeters = eye2plate[0] - renderInfo.LensSurfaceToMidplateInMeters;
+ renderInfo.EyeRight.ReliefInMeters = eye2plate[1] - renderInfo.LensSurfaceToMidplateInMeters;
+
+ // Adjust the eye relief with the dial setting (from the assumed max eye relief)
+ int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, -1);
+ if (dial >= 0)
+ {
+ renderInfo.EyeLeft.ReliefInMeters -= ((10 - dial) * 0.001f);
+ renderInfo.EyeRight.ReliefInMeters -= ((10 - dial) * 0.001f);
+ }
+ }
+ else
+ {
+ // Set the eye relief with the user configured dial setting
+ int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, -1);
+ if (dial >= 0)
+ { // Assume a default of 7 to 17 mm eye relief based on the dial. This corresponds
+ // to the sampled and tuned distortion range on the DK1.
+ renderInfo.EyeLeft.ReliefInMeters = 0.007f + (dial * 0.001f);
+ renderInfo.EyeRight.ReliefInMeters = 0.007f + (dial * 0.001f);
+ }
+ }
+ }
+ }
+
+ // Now we know where the eyes are relative to the lenses, we can compute a distortion for each.
+ // TODO: incorporate lateral offset in distortion generation.
+ // TODO: we used a distortion to calculate eye-relief, and now we're making a distortion from that eye-relief. Close the loop!
+
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ HmdRenderInfo::EyeConfig *pHmdEyeConfig = ( eyeNum == 0 ) ? &(renderInfo.EyeLeft) : &(renderInfo.EyeRight);
+
+ float eye_relief = pHmdEyeConfig->ReliefInMeters;
+ LensConfig distortionConfig = GenerateLensConfigFromEyeRelief ( eye_relief, renderInfo, distortionType );
+ pHmdEyeConfig->Distortion = distortionConfig;
+ }
+
+ return renderInfo;
+}
+
+
+LensConfig GenerateLensConfigFromEyeRelief ( float eyeReliefInMeters, HmdRenderInfo const &hmd, DistortionEqnType distortionType /*= Distortion_CatmullRom10*/ )
+{
+ struct DistortionDescriptor
+ {
+ float EyeRelief;
+ // The three places we're going to sample & lerp the curve at.
+ // One sample is always at 0.0, and the distortion scale should be 1.0 or else!
+ // Only use for poly4 numbers - CR has an implicit scale.
+ float SampleRadius[3];
+ // Where the distortion has actually been measured/calibrated out to.
+ // Don't try to hallucinate data out beyond here.
+ float MaxRadius;
+ // The config itself.
+ LensConfig Config;
+ };
+
+ DistortionDescriptor distortions[10];
+ for ( unsigned int i = 0; i < sizeof(distortions)/sizeof(distortions[0]); i++ )
+ {
+ distortions[i].Config.SetToIdentity();
+ distortions[i].EyeRelief = 0.0f;
+ distortions[i].MaxRadius = 1.0f;
+ }
+ int numDistortions = 0;
+ int defaultDistortion = 0; // index of the default distortion curve to use if zero eye relief supplied
+
+ if ( ( hmd.EyeCups == EyeCup_DK1A ) ||
+ ( hmd.EyeCups == EyeCup_DK1B ) ||
+ ( hmd.EyeCups == EyeCup_DK1C ) )
+ {
+
+ numDistortions = 0;
+
+ // Tuned at minimum dial setting - extended to r^2 == 1.8
+ distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
+ distortions[numDistortions].EyeRelief = 0.012760465f - 0.005f;
+ distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
+ distortions[numDistortions].Config.K[0] = 1.0000f;
+ distortions[numDistortions].Config.K[1] = 1.06505f;
+ distortions[numDistortions].Config.K[2] = 1.14725f;
+ distortions[numDistortions].Config.K[3] = 1.2705f;
+ distortions[numDistortions].Config.K[4] = 1.48f;
+ distortions[numDistortions].Config.K[5] = 1.87f;
+ distortions[numDistortions].Config.K[6] = 2.534f;
+ distortions[numDistortions].Config.K[7] = 3.6f;
+ distortions[numDistortions].Config.K[8] = 5.1f;
+ distortions[numDistortions].Config.K[9] = 7.4f;
+ distortions[numDistortions].Config.K[10] = 11.0f;
+ distortions[numDistortions].SampleRadius[0] = 0.222717149f;
+ distortions[numDistortions].SampleRadius[1] = 0.512249443f;
+ distortions[numDistortions].SampleRadius[2] = 0.712694878f;
+ distortions[numDistortions].MaxRadius = sqrt(1.8f);
+ defaultDistortion = numDistortions; // this is the default
+ numDistortions++;
+
+ // Tuned at middle dial setting
+ distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
+ distortions[numDistortions].EyeRelief = 0.012760465f; // my average eye-relief
+ distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
+ distortions[numDistortions].Config.K[0] = 1.0f;
+ distortions[numDistortions].Config.K[1] = 1.032407264f;
+ distortions[numDistortions].Config.K[2] = 1.07160462f;
+ distortions[numDistortions].Config.K[3] = 1.11998388f;
+ distortions[numDistortions].Config.K[4] = 1.1808606f;
+ distortions[numDistortions].Config.K[5] = 1.2590494f;
+ distortions[numDistortions].Config.K[6] = 1.361915f;
+ distortions[numDistortions].Config.K[7] = 1.5014339f;
+ distortions[numDistortions].Config.K[8] = 1.6986004f;
+ distortions[numDistortions].Config.K[9] = 1.9940577f;
+ distortions[numDistortions].Config.K[10] = 2.4783147f;
+ distortions[numDistortions].SampleRadius[0] = 0.222717149f;
+ distortions[numDistortions].SampleRadius[1] = 0.512249443f;
+ distortions[numDistortions].SampleRadius[2] = 0.712694878f;
+ distortions[numDistortions].MaxRadius = 1.0f;
+ numDistortions++;
+
+ // Tuned at maximum dial setting
+ distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
+ distortions[numDistortions].EyeRelief = 0.012760465f + 0.005f;
+ distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
+ distortions[numDistortions].Config.K[0] = 1.0102f;
+ distortions[numDistortions].Config.K[1] = 1.0371f;
+ distortions[numDistortions].Config.K[2] = 1.0831f;
+ distortions[numDistortions].Config.K[3] = 1.1353f;
+ distortions[numDistortions].Config.K[4] = 1.2f;
+ distortions[numDistortions].Config.K[5] = 1.2851f;
+ distortions[numDistortions].Config.K[6] = 1.3979f;
+ distortions[numDistortions].Config.K[7] = 1.56f;
+ distortions[numDistortions].Config.K[8] = 1.8f;
+ distortions[numDistortions].Config.K[9] = 2.25f;
+ distortions[numDistortions].Config.K[10] = 3.0f;
+ distortions[numDistortions].SampleRadius[0] = 0.222717149f;
+ distortions[numDistortions].SampleRadius[1] = 0.512249443f;
+ distortions[numDistortions].SampleRadius[2] = 0.712694878f;
+ distortions[numDistortions].MaxRadius = 1.0f;
+ numDistortions++;
+
+ // Chromatic aberration doesn't seem to change with eye relief.
+ for ( int i = 0; i < numDistortions; i++ )
+ {
+ distortions[i].Config.ChromaticAberration[0] = -0.006f;
+ distortions[i].Config.ChromaticAberration[1] = 0.0f;
+ distortions[i].Config.ChromaticAberration[2] = 0.014f;
+ distortions[i].Config.ChromaticAberration[3] = 0.0f;
+ }
+ }
+ else if ( hmd.EyeCups == EyeCup_DKHD2A )
+ {
+ // Tuned DKHD2 lens
+ numDistortions = 0;
+
+ distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
+ distortions[numDistortions].EyeRelief = 0.010f;
+ distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
+ distortions[numDistortions].Config.K[0] = 1.0f;
+ distortions[numDistortions].Config.K[1] = 1.0425f;
+ distortions[numDistortions].Config.K[2] = 1.0826f;
+ distortions[numDistortions].Config.K[3] = 1.130f;
+ distortions[numDistortions].Config.K[4] = 1.185f;
+ distortions[numDistortions].Config.K[5] = 1.250f;
+ distortions[numDistortions].Config.K[6] = 1.338f;
+ distortions[numDistortions].Config.K[7] = 1.455f;
+ distortions[numDistortions].Config.K[8] = 1.620f;
+ distortions[numDistortions].Config.K[9] = 1.840f;
+ distortions[numDistortions].Config.K[10] = 2.200f;
+ distortions[numDistortions].SampleRadius[0] = 0.222717149f;
+ distortions[numDistortions].SampleRadius[1] = 0.512249443f;
+ distortions[numDistortions].SampleRadius[2] = 0.712694878f;
+ distortions[numDistortions].MaxRadius = 1.0f;
+
+ distortions[numDistortions].SampleRadius[0] = 0.405405405f;
+ distortions[numDistortions].SampleRadius[1] = 0.675675676f;
+ distortions[numDistortions].SampleRadius[2] = 0.945945946f;
+ defaultDistortion = numDistortions; // this is the default
+ numDistortions++;
+
+ distortions[numDistortions] = distortions[0];
+ distortions[numDistortions].EyeRelief = 0.020f;
+ numDistortions++;
+
+ // Chromatic aberration doesn't seem to change with eye relief.
+ for ( int i = 0; i < numDistortions; i++ )
+ {
+ distortions[i].Config.ChromaticAberration[0] = -0.006f;
+ distortions[i].Config.ChromaticAberration[1] = 0.0f;
+ distortions[i].Config.ChromaticAberration[2] = 0.014f;
+ distortions[i].Config.ChromaticAberration[3] = 0.0f;
+ }
+ }
+ else if ( hmd.EyeCups == EyeCup_PinkA || hmd.EyeCups == EyeCup_DK2A )
+ {
+ // Tuned Crystal Cove & DK2 Lens (CES & GDC)
+ numDistortions = 0;
+
+ distortions[numDistortions].EyeRelief = 0.010f;
+ distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.036f;
+
+ distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
+ distortions[numDistortions].Config.K[0] = 1.003f;
+ distortions[numDistortions].Config.K[1] = 1.02f;
+ distortions[numDistortions].Config.K[2] = 1.042f;
+ distortions[numDistortions].Config.K[3] = 1.066f;
+ distortions[numDistortions].Config.K[4] = 1.094f; //1.0945f;
+ distortions[numDistortions].Config.K[5] = 1.126f; //1.127f;
+ distortions[numDistortions].Config.K[6] = 1.162f; //1.167f;
+ distortions[numDistortions].Config.K[7] = 1.203f; //1.218f;
+ distortions[numDistortions].Config.K[8] = 1.25f; //1.283f;
+ distortions[numDistortions].Config.K[9] = 1.31f; //1.37f;
+ distortions[numDistortions].Config.K[10] = 1.38f; //1.48f;
+ distortions[numDistortions].MaxRadius = 1.0f;
+
+
+ distortions[numDistortions].SampleRadius[0] = 0.405405405f;
+ distortions[numDistortions].SampleRadius[1] = 0.675675676f;
+ distortions[numDistortions].SampleRadius[2] = 0.945945946f;
+ defaultDistortion = numDistortions; // this is the default
+ numDistortions++;
+
+ distortions[numDistortions] = distortions[0];
+ distortions[numDistortions].EyeRelief = 0.020f;
+ numDistortions++;
+
+ // Chromatic aberration doesn't seem to change with eye relief.
+ for ( int i = 0; i < numDistortions; i++ )
+ {
+ distortions[i].Config.ChromaticAberration[0] = -0.015f;
+ distortions[i].Config.ChromaticAberration[1] = -0.02f;
+ distortions[i].Config.ChromaticAberration[2] = 0.025f;
+ distortions[i].Config.ChromaticAberration[3] = 0.02f;
+ }
+ }
+ else
+ {
+ // Unknown lens.
+ // Use DK1 black lens settings, just so we can continue to run with something.
+ distortions[0].EyeRelief = 0.005f;
+ distortions[0].Config.MetersPerTanAngleAtCenter = 0.043875f;
+ distortions[0].Config.Eqn = Distortion_RecipPoly4;
+ distortions[0].Config.K[0] = 1.0f;
+ distortions[0].Config.K[1] = -0.3999f;
+ distortions[0].Config.K[2] = 0.2408f;
+ distortions[0].Config.K[3] = -0.4589f;
+ distortions[0].SampleRadius[0] = 0.2f;
+ distortions[0].SampleRadius[1] = 0.4f;
+ distortions[0].SampleRadius[2] = 0.6f;
+
+ distortions[1] = distortions[0];
+ distortions[1].EyeRelief = 0.010f;
+ numDistortions = 2;
+
+ // Chromatic aberration doesn't seem to change with eye relief.
+ for ( int i = 0; i < numDistortions; i++ )
+ {
+ // These are placeholder, they have not been tuned!
+ distortions[i].Config.ChromaticAberration[0] = 0.0f;
+ distortions[i].Config.ChromaticAberration[1] = 0.0f;
+ distortions[i].Config.ChromaticAberration[2] = 0.0f;
+ distortions[i].Config.ChromaticAberration[3] = 0.0f;
+ }
+ }
+
+ OVR_ASSERT ( numDistortions < (sizeof(distortions)/sizeof(distortions[0])) );
+
+
+ DistortionDescriptor *pUpper = NULL;
+ DistortionDescriptor *pLower = NULL;
+ float lerpVal = 0.0f;
+ if (eyeReliefInMeters == 0)
+ { // Use a constant default distortion if an invalid eye-relief is supplied
+ pLower = &(distortions[defaultDistortion]);
+ pUpper = &(distortions[defaultDistortion]);
+ lerpVal = 0.0f;
+ }
+ else
+ {
+ for ( int i = 0; i < numDistortions-1; i++ )
+ {
+ OVR_ASSERT ( distortions[i].EyeRelief < distortions[i+1].EyeRelief );
+ if ( ( distortions[i].EyeRelief <= eyeReliefInMeters ) && ( distortions[i+1].EyeRelief > eyeReliefInMeters ) )
+ {
+ pLower = &(distortions[i]);
+ pUpper = &(distortions[i+1]);
+ lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief );
+ // No break here - I want the ASSERT to check everything every time!
+ }
+ }
+ }
+
+ if ( pUpper == NULL )
+ {
+#if 0
+ // Outside the range, so extrapolate rather than interpolate.
+ if ( distortions[0].EyeRelief > eyeReliefInMeters )
+ {
+ pLower = &(distortions[0]);
+ pUpper = &(distortions[1]);
+ }
+ else
+ {
+ OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters );
+ pLower = &(distortions[numDistortions-2]);
+ pUpper = &(distortions[numDistortions-1]);
+ }
+ lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief );
+#else
+ // Do not extrapolate, just clamp - slightly worried about people putting in bogus settings.
+ if ( distortions[0].EyeRelief > eyeReliefInMeters )
+ {
+ pLower = &(distortions[0]);
+ pUpper = &(distortions[0]);
+ }
+ else
+ {
+ OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters );
+ pLower = &(distortions[numDistortions-1]);
+ pUpper = &(distortions[numDistortions-1]);
+ }
+ lerpVal = 0.0f;
+#endif
+ }
+ float invLerpVal = 1.0f - lerpVal;
+
+ pLower->Config.MaxR = pLower->MaxRadius;
+ pUpper->Config.MaxR = pUpper->MaxRadius;
+
+ LensConfig result;
+ // Where is the edge of the lens - no point modelling further than this.
+ float maxValidRadius = invLerpVal * pLower->MaxRadius + lerpVal * pUpper->MaxRadius;
+ result.MaxR = maxValidRadius;
+
+ switch ( distortionType )
+ {
+ case Distortion_Poly4:
+ // Deprecated
+ OVR_ASSERT ( false );
+ break;
+ case Distortion_RecipPoly4:{
+ // Lerp control points and fit an equation to them.
+ float fitX[4];
+ float fitY[4];
+ fitX[0] = 0.0f;
+ fitY[0] = 1.0f;
+ for ( int ctrlPt = 1; ctrlPt < 4; ctrlPt ++ )
+ {
+ float radiusLerp = invLerpVal * pLower->SampleRadius[ctrlPt-1] + lerpVal * pUpper->SampleRadius[ctrlPt-1];
+ float radiusLerpSq = radiusLerp * radiusLerp;
+ float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
+ float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
+ fitX[ctrlPt] = radiusLerpSq;
+ fitY[ctrlPt] = 1.0f / ( invLerpVal * fitYLower + lerpVal * fitYUpper );
+ }
+
+ result.Eqn = Distortion_RecipPoly4;
+ bool bSuccess = FitCubicPolynomial ( result.K, fitX, fitY );
+ OVR_ASSERT ( bSuccess );
+ OVR_UNUSED ( bSuccess );
+
+ // Set up the fast inverse.
+ float maxRDist = result.DistortionFn ( maxValidRadius );
+ result.MaxInvR = maxRDist;
+ result.SetUpInverseApprox();
+
+ }break;
+
+ case Distortion_CatmullRom10:{
+
+ // Evenly sample & lerp points on the curve.
+ const int NumSegments = LensConfig::NumCoefficients;
+ result.MaxR = maxValidRadius;
+ // Directly interpolate the K0 values
+ result.K[0] = invLerpVal * pLower->Config.K[0] + lerpVal * pUpper->Config.K[0];
+
+ // Sample and interpolate the distortion curves to derive K[1] ... K[n]
+ for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
+ {
+ float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
+ float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusSq );
+ float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusSq );
+ float fitLerp = invLerpVal * fitYLower + lerpVal * fitYUpper;
+ result.K[ctrlPt] = fitLerp;
+ }
+
+ result.Eqn = Distortion_CatmullRom10;
+
+ for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
+ {
+ float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
+ float val = result.DistortionFnScaleRadiusSquared ( radiusSq );
+ OVR_ASSERT ( Alg::Abs ( val - result.K[ctrlPt] ) < 0.0001f );
+ OVR_UNUSED1(val); // For release build.
+ }
+
+ // Set up the fast inverse.
+ float maxRDist = result.DistortionFn ( maxValidRadius );
+ result.MaxInvR = maxRDist;
+ result.SetUpInverseApprox();
+
+ }break;
+
+ default: OVR_ASSERT ( false ); break;
+ }
+
+
+ // Chromatic aberration.
+ result.ChromaticAberration[0] = invLerpVal * pLower->Config.ChromaticAberration[0] + lerpVal * pUpper->Config.ChromaticAberration[0];
+ result.ChromaticAberration[1] = invLerpVal * pLower->Config.ChromaticAberration[1] + lerpVal * pUpper->Config.ChromaticAberration[1];
+ result.ChromaticAberration[2] = invLerpVal * pLower->Config.ChromaticAberration[2] + lerpVal * pUpper->Config.ChromaticAberration[2];
+ result.ChromaticAberration[3] = invLerpVal * pLower->Config.ChromaticAberration[3] + lerpVal * pUpper->Config.ChromaticAberration[3];
+
+ // Scale.
+ result.MetersPerTanAngleAtCenter = pLower->Config.MetersPerTanAngleAtCenter * invLerpVal +
+ pUpper->Config.MetersPerTanAngleAtCenter * lerpVal;
+ /*
+ // Commented out - Causes ASSERT with no HMD plugged in
+#ifdef OVR_BUILD_DEBUG
+ if ( distortionType == Distortion_CatmullRom10 )
+ {
+ TestSaveLoadLensConfig ( result );
+ }
+#endif
+ */
+ return result;
+}
+
+
+
+
+
+DistortionRenderDesc CalculateDistortionRenderDesc ( StereoEye eyeType, HmdRenderInfo const &hmd,
+ const LensConfig *pLensOverride /*= NULL */ )
+{
+ // From eye relief, IPD and device characteristics, we get the distortion mapping.
+ // This distortion does the following things:
+ // 1. It undoes the distortion that happens at the edges of the lens.
+ // 2. It maps the undistorted field into "retina" space.
+ // So the input is a pixel coordinate - the physical pixel on the display itself.
+ // The output is the real-world direction of the ray from this pixel as it comes out of the lens and hits the eye.
+ // However we typically think of rays "coming from" the eye, so the direction (TanAngleX,TanAngleY,1) is the direction
+ // that the pixel appears to be in real-world space, where AngleX and AngleY are relative to the straight-ahead vector.
+ // If your renderer is a raytracer, you can use this vector directly (normalize as appropriate).
+ // However in standard rasterisers, we have rendered a 2D image and are putting it in front of the eye,
+ // so we then need a mapping from this space to the [-1,1] UV coordinate space, which depends on exactly
+ // where "in space" the app wants to put that rendertarget.
+ // Where in space, and how large this rendertarget is, is completely up to the app and/or user,
+ // though of course we can provide some useful hints.
+
+ // TODO: Use IPD and eye relief to modify distortion (i.e. non-radial component)
+ // TODO: cope with lenses that don't produce collimated light.
+ // This means that IPD relative to the lens separation changes the light vergence,
+ // and so we actually need to change where the image is displayed.
+
+ const HmdRenderInfo::EyeConfig &hmdEyeConfig = ( eyeType == StereoEye_Left ) ? hmd.EyeLeft : hmd.EyeRight;
+
+ DistortionRenderDesc localDistortion;
+ localDistortion.Lens = hmdEyeConfig.Distortion;
+
+ if ( pLensOverride != NULL )
+ {
+ localDistortion.Lens = *pLensOverride;
+ }
+
+ Sizef pixelsPerMeter(hmd.ResolutionInPixels.w / ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters ),
+ hmd.ResolutionInPixels.h / hmd.ScreenSizeInMeters.h);
+
+ localDistortion.PixelsPerTanAngleAtCenter = (pixelsPerMeter * localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector();
+ // Same thing, scaled to [-1,1] for each eye, rather than pixels.
+
+ localDistortion.TanEyeAngleScale = Vector2f(0.25f, 0.5f).EntrywiseMultiply(
+ (hmd.ScreenSizeInMeters / localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector());
+
+ // <--------------left eye------------------><-ScreenGapSizeInMeters-><--------------right eye----------------->
+ // <------------------------------------------ScreenSizeInMeters.Width----------------------------------------->
+ // <----------------LensSeparationInMeters--------------->
+ // <--centerFromLeftInMeters->
+ // ^
+ // Center of lens
+
+ // Find the lens centers in scale of [-1,+1] (NDC) in left eye.
+ float visibleWidthOfOneEye = 0.5f * ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters );
+ float centerFromLeftInMeters = ( hmd.ScreenSizeInMeters.w - hmd.LensSeparationInMeters ) * 0.5f;
+ localDistortion.LensCenter.x = ( centerFromLeftInMeters / visibleWidthOfOneEye ) * 2.0f - 1.0f;
+ localDistortion.LensCenter.y = ( hmd.CenterFromTopInMeters / hmd.ScreenSizeInMeters.h ) * 2.0f - 1.0f;
+ if ( eyeType == StereoEye_Right )
+ {
+ localDistortion.LensCenter.x = -localDistortion.LensCenter.x;
+ }
+
+ return localDistortion;
+}
+
+FovPort CalculateFovFromEyePosition ( float eyeReliefInMeters,
+ float offsetToRightInMeters,
+ float offsetDownwardsInMeters,
+ float lensDiameterInMeters,
+ float extraEyeRotationInRadians /*= 0.0f*/ )
+{
+ // 2D view of things:
+ // |-| <--- offsetToRightInMeters (in this case, it is negative)
+ // |=======C=======| <--- lens surface (C=center)
+ // \ | _/
+ // \ R _/
+ // \ | _/
+ // \ | _/
+ // \|/
+ // O <--- center of pupil
+
+ // (technically the lens is round rather than square, so it's not correct to
+ // separate vertical and horizontal like this, but it's close enough)
+ float halfLensDiameter = lensDiameterInMeters * 0.5f;
+ FovPort fovPort;
+ fovPort.UpTan = ( halfLensDiameter + offsetDownwardsInMeters ) / eyeReliefInMeters;
+ fovPort.DownTan = ( halfLensDiameter - offsetDownwardsInMeters ) / eyeReliefInMeters;
+ fovPort.LeftTan = ( halfLensDiameter + offsetToRightInMeters ) / eyeReliefInMeters;
+ fovPort.RightTan = ( halfLensDiameter - offsetToRightInMeters ) / eyeReliefInMeters;
+
+ if ( extraEyeRotationInRadians > 0.0f )
+ {
+ // That's the basic looking-straight-ahead eye position relative to the lens.
+ // But if you look left, the pupil moves left as the eyeball rotates, which
+ // means you can see more to the right than this geometry suggests.
+ // So add in the bounds for the extra movement of the pupil.
+
+ // Beyond 30 degrees does not increase FOV because the pupil starts moving backwards more than sideways.
+ extraEyeRotationInRadians = Alg::Min ( DegreeToRad ( 30.0f ), Alg::Max ( 0.0f, extraEyeRotationInRadians ) );
+
+ // The rotation of the eye is a bit more complex than a simple circle. The center of rotation
+ // at 13.5mm from cornea is slightly further back than the actual center of the eye.
+ // Additionally the rotation contains a small lateral component as the muscles pull the eye
+ const float eyeballCenterToPupil = 0.0135f; // center of eye rotation
+ const float eyeballLateralPull = 0.001f * (extraEyeRotationInRadians / DegreeToRad ( 30.0f)); // lateral motion as linear function
+ float extraTranslation = eyeballCenterToPupil * sinf ( extraEyeRotationInRadians ) + eyeballLateralPull;
+ float extraRelief = eyeballCenterToPupil * ( 1.0f - cosf ( extraEyeRotationInRadians ) );
+
+ fovPort.UpTan = Alg::Max ( fovPort.UpTan , ( halfLensDiameter + offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
+ fovPort.DownTan = Alg::Max ( fovPort.DownTan , ( halfLensDiameter - offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
+ fovPort.LeftTan = Alg::Max ( fovPort.LeftTan , ( halfLensDiameter + offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
+ fovPort.RightTan = Alg::Max ( fovPort.RightTan, ( halfLensDiameter - offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
+ }
+
+ return fovPort;
+}
+
+
+
+FovPort CalculateFovFromHmdInfo ( StereoEye eyeType,
+ DistortionRenderDesc const &distortion,
+ HmdRenderInfo const &hmd,
+ float extraEyeRotationInRadians /*= 0.0f*/ )
+{
+ FovPort fovPort;
+ float eyeReliefInMeters;
+ float offsetToRightInMeters;
+ if ( eyeType == StereoEye_Right )
+ {
+ eyeReliefInMeters = hmd.EyeRight.ReliefInMeters;
+ offsetToRightInMeters = hmd.EyeRight.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters;
+ }
+ else
+ {
+ eyeReliefInMeters = hmd.EyeLeft.ReliefInMeters;
+ offsetToRightInMeters = -(hmd.EyeLeft.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters);
+ }
+
+ // Limit the eye-relief to 6 mm for FOV calculations since this just tends to spread off-screen
+ // and get clamped anyways on DK1 (but in Unity it continues to spreads and causes
+ // unnecessarily large render targets)
+ eyeReliefInMeters = Alg::Max(eyeReliefInMeters, 0.006f);
+
+ // Central view.
+ fovPort = CalculateFovFromEyePosition ( eyeReliefInMeters,
+ offsetToRightInMeters,
+ 0.0f,
+ hmd.LensDiameterInMeters,
+ extraEyeRotationInRadians );
+
+ // clamp to the screen
+ fovPort = ClampToPhysicalScreenFov ( eyeType, distortion, fovPort );
+
+ return fovPort;
+}
+
+
+
+FovPort GetPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion )
+{
+ OVR_UNUSED1 ( eyeType );
+
+ FovPort resultFovPort;
+
+ // Figure out the boundaries of the screen. We take the middle pixel of the screen,
+ // move to each of the four screen edges, and transform those back into TanAngle space.
+ Vector2f dmiddle = distortion.LensCenter;
+
+ // The gotcha is that for some distortion functions, the map will "wrap around"
+ // for screen pixels that are not actually visible to the user (especially on DK1,
+ // which has a lot of invisible pixels), and map to pixels that are close to the middle.
+ // This means the edges of the screen will actually be
+ // "closer" than the visible bounds, so we'll clip too aggressively.
+
+ // Solution - step gradually towards the boundary, noting the maximum distance.
+ struct FunctionHider
+ {
+ static FovPort FindRange ( Vector2f from, Vector2f to, int numSteps,
+ DistortionRenderDesc const &distortion )
+ {
+ FovPort result;
+ result.UpTan = 0.0f;
+ result.DownTan = 0.0f;
+ result.LeftTan = 0.0f;
+ result.RightTan = 0.0f;
+
+ float stepScale = 1.0f / ( numSteps - 1 );
+ for ( int step = 0; step < numSteps; step++ )
+ {
+ float lerpFactor = stepScale * (float)step;
+ Vector2f sample = from + (to - from) * lerpFactor;
+ Vector2f tanEyeAngle = TransformScreenNDCToTanFovSpace ( distortion, sample );
+
+ result.LeftTan = Alg::Max ( result.LeftTan, -tanEyeAngle.x );
+ result.RightTan = Alg::Max ( result.RightTan, tanEyeAngle.x );
+ result.UpTan = Alg::Max ( result.UpTan, -tanEyeAngle.y );
+ result.DownTan = Alg::Max ( result.DownTan, tanEyeAngle.y );
+ }
+ return result;
+ }
+ };
+
+ FovPort leftFovPort = FunctionHider::FindRange( dmiddle, Vector2f( -1.0f, dmiddle.y ), 10, distortion );
+ FovPort rightFovPort = FunctionHider::FindRange( dmiddle, Vector2f( 1.0f, dmiddle.y ), 10, distortion );
+ FovPort upFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, -1.0f ), 10, distortion );
+ FovPort downFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, 1.0f ), 10, distortion );
+
+ resultFovPort.LeftTan = leftFovPort.LeftTan;
+ resultFovPort.RightTan = rightFovPort.RightTan;
+ resultFovPort.UpTan = upFovPort.UpTan;
+ resultFovPort.DownTan = downFovPort.DownTan;
+
+ return resultFovPort;
+}
+
+FovPort ClampToPhysicalScreenFov( StereoEye eyeType, DistortionRenderDesc const &distortion,
+ FovPort inputFovPort )
+{
+ FovPort resultFovPort;
+ FovPort phsyicalFovPort = GetPhysicalScreenFov ( eyeType, distortion );
+ resultFovPort.LeftTan = Alg::Min ( inputFovPort.LeftTan, phsyicalFovPort.LeftTan );
+ resultFovPort.RightTan = Alg::Min ( inputFovPort.RightTan, phsyicalFovPort.RightTan );
+ resultFovPort.UpTan = Alg::Min ( inputFovPort.UpTan, phsyicalFovPort.UpTan );
+ resultFovPort.DownTan = Alg::Min ( inputFovPort.DownTan, phsyicalFovPort.DownTan );
+
+ return resultFovPort;
+}
+
+Sizei CalculateIdealPixelSize ( StereoEye eyeType, DistortionRenderDesc const &distortion,
+ FovPort tanHalfFov, float pixelsPerDisplayPixel )
+{
+ OVR_UNUSED(eyeType); // might be useful in the future if we do overlapping fovs
+
+ Sizei result;
+ // TODO: if the app passes in a FOV that doesn't cover the centre, use the distortion values for the nearest edge/corner to match pixel size.
+ result.w = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.x * ( tanHalfFov.LeftTan + tanHalfFov.RightTan ) );
+ result.h = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.y * ( tanHalfFov.UpTan + tanHalfFov.DownTan ) );
+ return result;
+}
+
+Recti GetFramebufferViewport ( StereoEye eyeType, HmdRenderInfo const &hmd )
+{
+ Recti result;
+ result.w = hmd.ResolutionInPixels.w/2;
+ result.h = hmd.ResolutionInPixels.h;
+ result.x = 0;
+ result.y = 0;
+ if ( eyeType == StereoEye_Right )
+ {
+ result.x = (hmd.ResolutionInPixels.w+1)/2; // Round up, not down.
+ }
+ return result;
+}
+
+
+ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort tanHalfFov )
+{
+ float projXScale = 2.0f / ( tanHalfFov.LeftTan + tanHalfFov.RightTan );
+ float projXOffset = ( tanHalfFov.LeftTan - tanHalfFov.RightTan ) * projXScale * 0.5f;
+ float projYScale = 2.0f / ( tanHalfFov.UpTan + tanHalfFov.DownTan );
+ float projYOffset = ( tanHalfFov.UpTan - tanHalfFov.DownTan ) * projYScale * 0.5f;
+
+ ScaleAndOffset2D result;
+ result.Scale = Vector2f(projXScale, projYScale);
+ result.Offset = Vector2f(projXOffset, projYOffset);
+ // Hey - why is that Y.Offset negated?
+ // It's because a projection matrix transforms from world coords with Y=up,
+ // whereas this is from NDC which is Y=down.
+
+ return result;
+}
+
+
+ScaleAndOffset2D CreateUVScaleAndOffsetfromNDCScaleandOffset ( ScaleAndOffset2D scaleAndOffsetNDC,
+ Recti renderedViewport,
+ Sizei renderTargetSize )
+{
+ // scaleAndOffsetNDC takes you to NDC space [-1,+1] within the given viewport on the rendertarget.
+ // We want a scale to instead go to actual UV coordinates you can sample with,
+ // which need [0,1] and ignore the viewport.
+ ScaleAndOffset2D result;
+ // Scale [-1,1] to [0,1]
+ result.Scale = scaleAndOffsetNDC.Scale * 0.5f;
+ result.Offset = scaleAndOffsetNDC.Offset * 0.5f + Vector2f(0.5f);
+
+ // ...but we will have rendered to a subsection of the RT, so scale for that.
+ Vector2f scale( (float)renderedViewport.w / (float)renderTargetSize.w,
+ (float)renderedViewport.h / (float)renderTargetSize.h );
+ Vector2f offset( (float)renderedViewport.x / (float)renderTargetSize.w,
+ (float)renderedViewport.y / (float)renderTargetSize.h );
+
+ result.Scale = result.Scale.EntrywiseMultiply(scale);
+ result.Offset = result.Offset.EntrywiseMultiply(scale) + offset;
+ return result;
+}
+
+
+
+Matrix4f CreateProjection( bool rightHanded, FovPort tanHalfFov,
+ float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/ )
+{
+ // A projection matrix is very like a scaling from NDC, so we can start with that.
+ ScaleAndOffset2D scaleAndOffset = CreateNDCScaleAndOffsetFromFov ( tanHalfFov );
+
+ float handednessScale = 1.0f;
+ if ( rightHanded )
+ {
+ handednessScale = -1.0f;
+ }
+
+ Matrix4f projection;
+ // Produces X result, mapping clip edges to [-w,+w]
+ projection.M[0][0] = scaleAndOffset.Scale.x;
+ projection.M[0][1] = 0.0f;
+ projection.M[0][2] = handednessScale * scaleAndOffset.Offset.x;
+ projection.M[0][3] = 0.0f;
+
+ // Produces Y result, mapping clip edges to [-w,+w]
+ // Hey - why is that YOffset negated?
+ // It's because a projection matrix transforms from world coords with Y=up,
+ // whereas this is derived from an NDC scaling, which is Y=down.
+ projection.M[1][0] = 0.0f;
+ projection.M[1][1] = scaleAndOffset.Scale.y;
+ projection.M[1][2] = handednessScale * -scaleAndOffset.Offset.y;
+ projection.M[1][3] = 0.0f;
+
+ // Produces Z-buffer result - app needs to fill this in with whatever Z range it wants.
+ // We'll just use some defaults for now.
+ projection.M[2][0] = 0.0f;
+ projection.M[2][1] = 0.0f;
+ projection.M[2][2] = -handednessScale * zFar / (zNear - zFar);
+ projection.M[2][3] = (zFar * zNear) / (zNear - zFar);
+
+ // Produces W result (= Z in)
+ projection.M[3][0] = 0.0f;
+ projection.M[3][1] = 0.0f;
+ projection.M[3][2] = handednessScale;
+ projection.M[3][3] = 0.0f;
+
+ return projection;
+}
+
+
+Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType,
+ float tanHalfFovX, float tanHalfFovY,
+ float unitsX, float unitsY,
+ float distanceFromCamera, float interpupillaryDistance,
+ Matrix4f const &projection,
+ float zNear /*= 0.0f*/, float zFar /*= 0.0f*/ )
+{
+ OVR_UNUSED1 ( rightHanded );
+
+ float orthoHorizontalOffset = interpupillaryDistance * 0.5f / distanceFromCamera;
+ switch ( eyeType )
+ {
+ case StereoEye_Center:
+ orthoHorizontalOffset = 0.0f;
+ break;
+ case StereoEye_Left:
+ break;
+ case StereoEye_Right:
+ orthoHorizontalOffset = -orthoHorizontalOffset;
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+ // 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].
+
+ float orthoScaleX = 2.0f * tanHalfFovX / unitsX;
+ float orthoScaleY = 2.0f * tanHalfFovY / unitsY;
+ Matrix4f ortho;
+ ortho.M[0][0] = projection.M[0][0] * orthoScaleX;
+ 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] * orthoScaleY; // 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);
+ }
+
+ // 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;
+}
+
+
+//-----------------------------------------------------------------------------------
+// A set of "forward-mapping" functions, mapping from framebuffer space to real-world and/or texture space.
+
+// This mimics the first half of the distortion shader's function.
+Vector2f TransformScreenNDCToTanFovSpace( DistortionRenderDesc const &distortion,
+ const Vector2f &framebufferNDC )
+{
+ // Scale to TanHalfFov space, but still distorted.
+ Vector2f tanEyeAngleDistorted;
+ tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x;
+ tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y;
+ // Distort.
+ float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x )
+ + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y );
+ float distortionScale = distortion.Lens.DistortionFnScaleRadiusSquared ( radiusSquared );
+ Vector2f tanEyeAngle;
+ tanEyeAngle.x = tanEyeAngleDistorted.x * distortionScale;
+ tanEyeAngle.y = tanEyeAngleDistorted.y * distortionScale;
+
+ return tanEyeAngle;
+}
+
+// Same, with chromatic aberration correction.
+void TransformScreenNDCToTanFovSpaceChroma ( Vector2f *resultR, Vector2f *resultG, Vector2f *resultB,
+ DistortionRenderDesc const &distortion,
+ const Vector2f &framebufferNDC )
+{
+ // Scale to TanHalfFov space, but still distorted.
+ Vector2f tanEyeAngleDistorted;
+ tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x;
+ tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y;
+ // Distort.
+ float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x )
+ + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y );
+ Vector3f distortionScales = distortion.Lens.DistortionFnScaleRadiusSquaredChroma ( radiusSquared );
+ *resultR = tanEyeAngleDistorted * distortionScales.x;
+ *resultG = tanEyeAngleDistorted * distortionScales.y;
+ *resultB = tanEyeAngleDistorted * distortionScales.z;
+}
+
+// This mimics the second half of the distortion shader's function.
+Vector2f TransformTanFovSpaceToRendertargetTexUV( StereoEyeParams const &eyeParams,
+ Vector2f const &tanEyeAngle )
+{
+ Vector2f textureUV;
+ textureUV.x = tanEyeAngle.x * eyeParams.EyeToSourceUV.Scale.x + eyeParams.EyeToSourceUV.Offset.x;
+ textureUV.y = tanEyeAngle.y * eyeParams.EyeToSourceUV.Scale.y + eyeParams.EyeToSourceUV.Offset.y;
+ return textureUV;
+}
+
+Vector2f TransformTanFovSpaceToRendertargetNDC( StereoEyeParams const &eyeParams,
+ Vector2f const &tanEyeAngle )
+{
+ Vector2f textureNDC;
+ textureNDC.x = tanEyeAngle.x * eyeParams.EyeToSourceNDC.Scale.x + eyeParams.EyeToSourceNDC.Offset.x;
+ textureNDC.y = tanEyeAngle.y * eyeParams.EyeToSourceNDC.Scale.y + eyeParams.EyeToSourceNDC.Offset.y;
+ return textureNDC;
+}
+
+Vector2f TransformScreenPixelToScreenNDC( Recti const &distortionViewport,
+ Vector2f const &pixel )
+{
+ // Move to [-1,1] NDC coords.
+ Vector2f framebufferNDC;
+ framebufferNDC.x = -1.0f + 2.0f * ( ( pixel.x - (float)distortionViewport.x ) / (float)distortionViewport.w );
+ framebufferNDC.y = -1.0f + 2.0f * ( ( pixel.y - (float)distortionViewport.y ) / (float)distortionViewport.h );
+ return framebufferNDC;
+}
+
+Vector2f TransformScreenPixelToTanFovSpace( Recti const &distortionViewport,
+ DistortionRenderDesc const &distortion,
+ Vector2f const &pixel )
+{
+ return TransformScreenNDCToTanFovSpace( distortion,
+ TransformScreenPixelToScreenNDC( distortionViewport, pixel ) );
+}
+
+Vector2f TransformScreenNDCToRendertargetTexUV( DistortionRenderDesc const &distortion,
+ StereoEyeParams const &eyeParams,
+ Vector2f const &pixel )
+{
+ return TransformTanFovSpaceToRendertargetTexUV ( eyeParams,
+ TransformScreenNDCToTanFovSpace ( distortion, pixel ) );
+}
+
+Vector2f TransformScreenPixelToRendertargetTexUV( Recti const &distortionViewport,
+ DistortionRenderDesc const &distortion,
+ StereoEyeParams const &eyeParams,
+ Vector2f const &pixel )
+{
+ return TransformTanFovSpaceToRendertargetTexUV ( eyeParams,
+ TransformScreenPixelToTanFovSpace ( distortionViewport, distortion, pixel ) );
+}
+
+
+//-----------------------------------------------------------------------------------
+// A set of "reverse-mapping" functions, mapping from real-world and/or texture space back to the framebuffer.
+
+Vector2f TransformTanFovSpaceToScreenNDC( DistortionRenderDesc const &distortion,
+ const Vector2f &tanEyeAngle, bool usePolyApprox /*= false*/ )
+{
+ float tanEyeAngleRadius = tanEyeAngle.Length();
+ float tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverseApprox ( tanEyeAngleRadius );
+ if ( !usePolyApprox )
+ {
+ tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverse ( tanEyeAngleRadius );
+ }
+ Vector2f tanEyeAngleDistorted = tanEyeAngle;
+ if ( tanEyeAngleRadius > 0.0f )
+ {
+ tanEyeAngleDistorted = tanEyeAngle * ( tanEyeAngleDistortedRadius / tanEyeAngleRadius );
+ }
+
+ Vector2f framebufferNDC;
+ framebufferNDC.x = ( tanEyeAngleDistorted.x / distortion.TanEyeAngleScale.x ) + distortion.LensCenter.x;
+ framebufferNDC.y = ( tanEyeAngleDistorted.y / distortion.TanEyeAngleScale.y ) + distortion.LensCenter.y;
+
+ return framebufferNDC;
+}
+
+Vector2f TransformRendertargetNDCToTanFovSpace( const ScaleAndOffset2D &eyeToSourceNDC,
+ const Vector2f &textureNDC )
+{
+ Vector2f tanEyeAngle = (textureNDC - eyeToSourceNDC.Offset) / eyeToSourceNDC.Scale;
+ return tanEyeAngle;
+}
+
+
+
+} //namespace OVR
+
+//Just want to make a copy disentangled from all these namespaces!
+float ExtEvalCatmullRom10Spline ( float const *K, float scaledVal )
+{
+ return(OVR::EvalCatmullRom10Spline ( K, scaledVal ));
+}
+
+
diff --git a/LibOVR/Src/OVR_Stereo.h b/LibOVR/Src/OVR_Stereo.h
new file mode 100644
index 0000000..dd5499c
--- /dev/null
+++ b/LibOVR/Src/OVR_Stereo.h
@@ -0,0 +1,460 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_Stereo.h
+Content : Stereo rendering functions
+Created : November 30, 2013
+Authors : Tom Fosyth
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Stereo_h
+#define OVR_Stereo_h
+
+#include "OVR_Device.h"
+
+// CAPI Forward declaration.
+typedef struct ovrFovPort_ ovrFovPort;
+typedef struct ovrRecti_ ovrRecti;
+
+namespace OVR {
+
+//-----------------------------------------------------------------------------------
+// ***** Stereo Enumerations
+
+// StereoEye specifies which eye we are rendering for; it is used to
+// retrieve StereoEyeParams.
+enum StereoEye
+{
+ StereoEye_Center,
+ StereoEye_Left,
+ StereoEye_Right
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** FovPort
+
+// FovPort describes Field Of View (FOV) of a viewport.
+// This class has values for up, down, left and right, stored in
+// tangent of the angle units to simplify calculations.
+//
+// As an example, for a standard 90 degree vertical FOV, we would
+// have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }.
+//
+// CreateFromRadians/Degrees helper functions can be used to
+// access FOV in different units.
+
+struct FovPort
+{
+ float UpTan;
+ float DownTan;
+ float LeftTan;
+ float RightTan;
+
+ FovPort ( float sideTan = 0.0f ) :
+ UpTan(sideTan), DownTan(sideTan), LeftTan(sideTan), RightTan(sideTan) { }
+ FovPort ( float u, float d, float l, float r ) :
+ UpTan(u), DownTan(d), LeftTan(l), RightTan(r) { }
+
+ // C-interop support: FovPort <-> ovrFovPort (implementation in OVR_CAPI.cpp).
+ FovPort(const ovrFovPort& src);
+ operator ovrFovPort () const;
+
+
+ static FovPort CreateFromRadians(float horizontalFov, float verticalFov)
+ {
+ FovPort result;
+ result.UpTan = tanf ( verticalFov * 0.5f );
+ result.DownTan = tanf ( verticalFov * 0.5f );
+ result.LeftTan = tanf ( horizontalFov * 0.5f );
+ result.RightTan = tanf ( horizontalFov * 0.5f );
+ return result;
+ }
+
+ static FovPort CreateFromDegrees(float horizontalFovDegrees,
+ float verticalFovDegrees)
+ {
+ return CreateFromRadians(DegreeToRad(horizontalFovDegrees),
+ DegreeToRad(verticalFovDegrees));
+ }
+
+ // Get Horizontal/Vertical components of Fov in radians.
+ float GetVerticalFovRadians() const { return atanf(UpTan) + atanf(DownTan); }
+ float GetHorizontalFovRadians() const { return atanf(LeftTan) + atanf(RightTan); }
+ // Get Horizontal/Vertical components of Fov in degrees.
+ float GetVerticalFovDegrees() const { return RadToDegree(GetVerticalFovRadians()); }
+ float GetHorizontalFovDegrees() const { return RadToDegree(GetHorizontalFovRadians()); }
+
+ // Compute maximum tangent value among all four sides.
+ float GetMaxSideTan() const
+ {
+ return Alg::Max(Alg::Max(UpTan, DownTan), Alg::Max(LeftTan, RightTan));
+ }
+
+ // Converts Fov Tan angle units to [-1,1] render target NDC space
+ Vector2f TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle);
+
+
+ // Compute per-channel minimum and maximum of Fov.
+ static FovPort Min(const FovPort& a, const FovPort& b)
+ {
+ FovPort fov( Alg::Min( a.UpTan , b.UpTan ),
+ Alg::Min( a.DownTan , b.DownTan ),
+ Alg::Min( a.LeftTan , b.LeftTan ),
+ Alg::Min( a.RightTan, b.RightTan ) );
+ return fov;
+ }
+
+ static FovPort Max(const FovPort& a, const FovPort& b)
+ {
+ FovPort fov( Alg::Max( a.UpTan , b.UpTan ),
+ Alg::Max( a.DownTan , b.DownTan ),
+ Alg::Max( a.LeftTan , b.LeftTan ),
+ Alg::Max( a.RightTan, b.RightTan ) );
+ return fov;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** ScaleAndOffset
+
+struct ScaleAndOffset2D
+{
+ Vector2f Scale;
+ Vector2f Offset;
+
+ ScaleAndOffset2D(float sx = 0.0f, float sy = 0.0f, float ox = 0.0f, float oy = 0.0f)
+ : Scale(sx, sy), Offset(ox, oy)
+ { }
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Misc. utility functions.
+
+// Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3])
+// Result is four coefficients in pResults[0] through pResults[3] such that
+// y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) );
+// passes through all four input points.
+// Return is true if it succeeded, false if it failed (because two control points
+// have the same pFitX value).
+bool FitCubicPolynomial ( float *pResult, const float *pFitX, const float *pFitY );
+
+//-----------------------------------------------------------------------------------
+// ***** LensConfig
+
+// LensConfig describes the configuration of a single lens in an HMD.
+// - Eqn and K[] describe a distortion function.
+// - MetersPerTanAngleAtCenter is the relationship between distance on a
+// screen (at the center of the lens), and the angle variance of the light after it
+// has passed through the lens.
+// - ChromaticAberration is an array of parameters for controlling
+// additional Red and Blue scaling in order to reduce chromatic aberration
+// caused by the Rift lenses.
+struct LensConfig
+{
+ // The result is a scaling applied to the distance from the center of the lens.
+ float DistortionFnScaleRadiusSquared (float rsq) const;
+ // x,y,z components map to r,g,b scales.
+ Vector3f DistortionFnScaleRadiusSquaredChroma (float rsq) const;
+
+ // DistortionFn applies distortion to the argument.
+ // Input: the distance in TanAngle/NIC space from the optical center to the input pixel.
+ // Output: the resulting distance after distortion.
+ float DistortionFn(float r) const
+ {
+ return r * DistortionFnScaleRadiusSquared ( r * r );
+ }
+
+ // DistortionFnInverse computes the inverse of the distortion function on an argument.
+ float DistortionFnInverse(float r) const;
+
+ // Also computes the inverse, but using a polynomial approximation. Warning - it's just an approximation!
+ float DistortionFnInverseApprox(float r) const;
+ // Sets up InvK[].
+ void SetUpInverseApprox();
+
+ // Sets a bunch of sensible defaults.
+ void SetToIdentity();
+
+
+
+ enum { NumCoefficients = 11 };
+
+ DistortionEqnType Eqn;
+ float K[NumCoefficients];
+ float MaxR; // The highest R you're going to query for - the curve is unpredictable beyond it.
+
+ float MetersPerTanAngleAtCenter;
+
+ // Additional per-channel scaling is applied after distortion:
+ // Index [0] - Red channel constant coefficient.
+ // Index [1] - Red channel r^2 coefficient.
+ // Index [2] - Blue channel constant coefficient.
+ // Index [3] - Blue channel r^2 coefficient.
+ float ChromaticAberration[4];
+
+ float InvK[NumCoefficients];
+ float MaxInvR;
+};
+
+
+// For internal use - storing and loading lens config data
+
+// Returns true on success.
+bool LoadLensConfig ( LensConfig *presult, UByte const *pbuffer, int bufferSizeInBytes );
+
+// Returns number of bytes needed.
+int SaveLensConfigSizeInBytes ( LensConfig const &config );
+// Returns true on success.
+bool SaveLensConfig ( UByte *pbuffer, int bufferSizeInBytes, LensConfig const &config );
+
+
+//-----------------------------------------------------------------------------------
+// ***** DistortionRenderDesc
+
+// This describes distortion for a single eye in an HMD with a display, not just the lens by itself.
+struct DistortionRenderDesc
+{
+ // The raw lens values.
+ LensConfig Lens;
+
+ // These map from [-1,1] across the eye being rendered into TanEyeAngle space (but still distorted)
+ Vector2f LensCenter;
+ Vector2f TanEyeAngleScale;
+ // Computed from device characteristics, IPD and eye-relief.
+ // (not directly used for rendering, but very useful)
+ Vector2f PixelsPerTanAngleAtCenter;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** HmdRenderInfo
+
+// All the parts of the HMD info that are needed to set up the rendering system.
+
+struct HmdRenderInfo
+{
+ // The start of this sturucture is intentionally very similar to HMDInfo in OVER_Device.h
+ // However to reduce interdependencies, one does not simply #include the other.
+
+ HmdTypeEnum HmdType;
+
+ // Size of the entire screen
+ Size<int> ResolutionInPixels;
+ Size<float> ScreenSizeInMeters;
+ float ScreenGapSizeInMeters;
+
+ // Characteristics of the lenses.
+ float CenterFromTopInMeters;
+ float LensSeparationInMeters;
+ float LensDiameterInMeters;
+ float LensSurfaceToMidplateInMeters;
+ EyeCupType EyeCups;
+
+ // Timing & shutter data. All values in seconds.
+ struct ShutterInfo
+ {
+ HmdShutterTypeEnum Type;
+ float VsyncToNextVsync; // 1/framerate
+ float VsyncToFirstScanline; // for global shutter, vsync->shutter open.
+ float FirstScanlineToLastScanline; // for global shutter, will be zero.
+ float PixelSettleTime; // estimated.
+ float PixelPersistence; // Full persistence = 1/framerate.
+ } Shutter;
+
+
+ // These are all set from the user's profile.
+ struct EyeConfig
+ {
+ // Distance from center of eyeball to front plane of lens.
+ float ReliefInMeters;
+ // Distance from nose (technically, center of Rift) to the middle of the eye.
+ float NoseToPupilInMeters;
+
+ LensConfig Distortion;
+ } EyeLeft, EyeRight;
+
+
+ HmdRenderInfo()
+ {
+ HmdType = HmdType_None;
+ ResolutionInPixels.w = 0;
+ ResolutionInPixels.h = 0;
+ ScreenSizeInMeters.w = 0.0f;
+ ScreenSizeInMeters.h = 0.0f;
+ ScreenGapSizeInMeters = 0.0f;
+ CenterFromTopInMeters = 0.0f;
+ LensSeparationInMeters = 0.0f;
+ LensDiameterInMeters = 0.0f;
+ LensSurfaceToMidplateInMeters = 0.0f;
+ Shutter.Type = HmdShutter_LAST;
+ Shutter.VsyncToNextVsync = 0.0f;
+ Shutter.VsyncToFirstScanline = 0.0f;
+ Shutter.FirstScanlineToLastScanline = 0.0f;
+ Shutter.PixelSettleTime = 0.0f;
+ Shutter.PixelPersistence = 0.0f;
+ EyeCups = EyeCup_DK1A;
+ EyeLeft.ReliefInMeters = 0.0f;
+ EyeLeft.NoseToPupilInMeters = 0.0f;
+ EyeLeft.Distortion.SetToIdentity();
+ EyeRight = EyeLeft;
+ }
+
+ // The "center eye" is the position the HMD tracking returns,
+ // and games will also usually use it for audio, aiming reticles, some line-of-sight tests, etc.
+ EyeConfig GetEyeCenter() const
+ {
+ EyeConfig result;
+ result.ReliefInMeters = 0.5f * ( EyeLeft.ReliefInMeters + EyeRight.ReliefInMeters );
+ result.NoseToPupilInMeters = 0.0f;
+ result.Distortion.SetToIdentity();
+ return result;
+ }
+
+};
+
+
+
+
+//-----------------------------------------------------------------------------------
+
+// Stateless computation functions, in somewhat recommended execution order.
+// For examples on how to use many of them, see the StereoConfig::UpdateComputedState function.
+
+const float OVR_DEFAULT_EXTRA_EYE_ROTATION = 30.0f * Math<float>::DegreeToRadFactor;
+
+// Creates a dummy debug HMDInfo matching a particular HMD model.
+// Useful for development without an actual HMD attached.
+HMDInfo CreateDebugHMDInfo(HmdTypeEnum hmdType);
+
+
+// profile may be NULL, in which case it uses the hard-coded defaults.
+// distortionType should be left at the default unless you require something specific for your distortion shaders.
+// eyeCupOverride can be EyeCup_LAST, in which case it uses the one in the profile.
+HmdRenderInfo GenerateHmdRenderInfoFromHmdInfo ( HMDInfo const &hmdInfo,
+ Profile const *profile = NULL,
+ DistortionEqnType distortionType = Distortion_CatmullRom10,
+ EyeCupType eyeCupOverride = EyeCup_LAST );
+
+LensConfig GenerateLensConfigFromEyeRelief ( float eyeReliefInMeters, HmdRenderInfo const &hmd,
+ DistortionEqnType distortionType = Distortion_CatmullRom10 );
+
+DistortionRenderDesc CalculateDistortionRenderDesc ( StereoEye eyeType, HmdRenderInfo const &hmd,
+ LensConfig const *pLensOverride = NULL );
+
+FovPort CalculateFovFromEyePosition ( float eyeReliefInMeters,
+ float offsetToRightInMeters,
+ float offsetDownwardsInMeters,
+ float lensDiameterInMeters,
+ float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION);
+
+FovPort CalculateFovFromHmdInfo ( StereoEye eyeType,
+ DistortionRenderDesc const &distortion,
+ HmdRenderInfo const &hmd,
+ float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION );
+
+FovPort GetPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion );
+
+FovPort ClampToPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion,
+ FovPort inputFovPort );
+
+Sizei CalculateIdealPixelSize ( StereoEye eyeType, DistortionRenderDesc const &distortion,
+ FovPort fov, float pixelsPerDisplayPixel );
+
+Recti GetFramebufferViewport ( StereoEye eyeType, HmdRenderInfo const &hmd );
+
+Matrix4f CreateProjection ( bool rightHanded, FovPort fov,
+ float zNear = 0.01f, float zFar = 10000.0f );
+
+Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType,
+ float tanHalfFovX, float tanHalfFovY,
+ float unitsX, float unitsY, float distanceFromCamera,
+ float interpupillaryDistance, Matrix4f const &projection,
+ float zNear = 0.0f, float zFar = 0.0f );
+
+ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort fov );
+
+ScaleAndOffset2D CreateUVScaleAndOffsetfromNDCScaleandOffset ( ScaleAndOffset2D scaleAndOffsetNDC,
+ Recti renderedViewport,
+ Sizei renderTargetSize );
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoEyeParams
+
+// StereoEyeParams describes RenderDevice configuration needed to render
+// the scene for one eye.
+struct StereoEyeParams
+{
+ StereoEye Eye;
+ Matrix4f ViewAdjust; // Translation to be applied to view matrix.
+
+ // Distortion and the VP on the physical display - the thing to run the distortion shader on.
+ DistortionRenderDesc Distortion;
+ Recti DistortionViewport;
+
+ // Projection and VP of a particular view (you could have multiple of these).
+ Recti RenderedViewport; // Viewport that we render the standard scene to.
+ FovPort Fov; // The FOVs of this scene.
+ Matrix4f RenderedProjection; // Projection matrix used with this eye.
+ ScaleAndOffset2D EyeToSourceNDC; // Mapping from TanEyeAngle space to [-1,+1] on the rendered image.
+ ScaleAndOffset2D EyeToSourceUV; // Mapping from TanEyeAngle space to actual texture UV coords.
+};
+
+
+//-----------------------------------------------------------------------------------
+// A set of "forward-mapping" functions, mapping from framebuffer space to real-world and/or texture space.
+Vector2f TransformScreenNDCToTanFovSpace ( DistortionRenderDesc const &distortion,
+ const Vector2f &framebufferNDC );
+void TransformScreenNDCToTanFovSpaceChroma ( Vector2f *resultR, Vector2f *resultG, Vector2f *resultB,
+ DistortionRenderDesc const &distortion,
+ const Vector2f &framebufferNDC );
+Vector2f TransformTanFovSpaceToRendertargetTexUV ( StereoEyeParams const &eyeParams,
+ Vector2f const &tanEyeAngle );
+Vector2f TransformTanFovSpaceToRendertargetNDC ( StereoEyeParams const &eyeParams,
+ Vector2f const &tanEyeAngle );
+Vector2f TransformScreenPixelToScreenNDC( Recti const &distortionViewport,
+ Vector2f const &pixel );
+Vector2f TransformScreenPixelToTanFovSpace ( Recti const &distortionViewport,
+ DistortionRenderDesc const &distortion,
+ Vector2f const &pixel );
+Vector2f TransformScreenNDCToRendertargetTexUV( DistortionRenderDesc const &distortion,
+ StereoEyeParams const &eyeParams,
+ Vector2f const &pixel );
+Vector2f TransformScreenPixelToRendertargetTexUV( Recti const &distortionViewport,
+ DistortionRenderDesc const &distortion,
+ StereoEyeParams const &eyeParams,
+ Vector2f const &pixel );
+
+// A set of "reverse-mapping" functions, mapping from real-world and/or texture space back to the framebuffer.
+// Be aware that many of these are significantly slower than their forward-mapping counterparts.
+Vector2f TransformTanFovSpaceToScreenNDC( DistortionRenderDesc const &distortion,
+ const Vector2f &tanEyeAngle, bool usePolyApprox = false );
+Vector2f TransformRendertargetNDCToTanFovSpace( const ScaleAndOffset2D &eyeToSourceNDC,
+ const Vector2f &textureNDC );
+
+} //namespace OVR
+
+#endif // OVR_Stereo_h \ No newline at end of file
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.cpp b/LibOVR/Src/OVR_ThreadCommandQueue.cpp
new file mode 100644
index 0000000..bc0d7dc
--- /dev/null
+++ b/LibOVR/Src/OVR_ThreadCommandQueue.cpp
@@ -0,0 +1,380 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.cpp
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+
+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_ThreadCommandQueue.h"
+
+namespace OVR {
+
+
+//------------------------------------------------------------------------
+// ***** CircularBuffer
+
+// CircularBuffer is a FIFO buffer implemented in a single block of memory,
+// which allows writing and reading variable-size data chucks. Write fails
+// if buffer is full.
+
+class CircularBuffer
+{
+ enum {
+ AlignSize = 16,
+ AlignMask = AlignSize - 1
+ };
+
+ UByte* pBuffer;
+ UPInt Size;
+ UPInt Tail; // Byte offset of next item to be popped.
+ UPInt Head; // Byte offset of where next push will take place.
+ UPInt End; // When Head < Tail, this is used instead of Size.
+
+ inline UPInt roundUpSize(UPInt size)
+ { return (size + AlignMask) & ~(UPInt)AlignMask; }
+
+public:
+
+ CircularBuffer(UPInt size)
+ : Size(size), Tail(0), Head(0), End(0)
+ {
+ pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize);
+ }
+ ~CircularBuffer()
+ {
+ // For ThreadCommands, we must consume everything before shutdown.
+ OVR_ASSERT(IsEmpty());
+ OVR_FREE_ALIGNED(pBuffer);
+ }
+
+ bool IsEmpty() const { return (Head == Tail); }
+
+ // Allocates a state block of specified size and advances pointers,
+ // returning 0 if buffer is full.
+ UByte* Write(UPInt size);
+
+ // Returns a pointer to next available data block; 0 if none available.
+ UByte* ReadBegin()
+ { return (Head != Tail) ? (pBuffer + Tail) : 0; }
+ // Consumes data of specified size; this must match size passed to Write.
+ void ReadEnd(UPInt size);
+};
+
+
+// Allocates a state block of specified size and advances pointers,
+// returning 0 if buffer is full.
+UByte* CircularBuffer::Write(UPInt size)
+{
+ UByte* p = 0;
+
+ size = roundUpSize(size);
+ // Since this is circular buffer, always allow at least one item.
+ OVR_ASSERT(size < Size/2);
+
+ if (Head >= Tail)
+ {
+ OVR_ASSERT(End == 0);
+
+ if (size <= (Size - Head))
+ {
+ p = pBuffer + Head;
+ Head += size;
+ }
+ else if (size < Tail)
+ {
+ p = pBuffer;
+ End = Head;
+ Head = size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+ else
+ {
+ OVR_ASSERT(End != 0);
+
+ if ((Tail - Head) > size)
+ {
+ p = pBuffer + Head;
+ Head += size;
+ OVR_ASSERT(Head != Tail);
+ }
+ }
+
+ return p;
+}
+
+void CircularBuffer::ReadEnd(UPInt size)
+{
+ OVR_ASSERT(Head != Tail);
+ size = roundUpSize(size);
+
+ Tail += size;
+ if (Tail == End)
+ {
+ Tail = End = 0;
+ }
+ else if (Tail == Head)
+ {
+ OVR_ASSERT(End == 0);
+ Tail = Head = 0;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+ThreadCommand::PopBuffer::~PopBuffer()
+{
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+}
+
+void ThreadCommand::PopBuffer::InitFromBuffer(void* data)
+{
+ ThreadCommand* cmd = (ThreadCommand*)data;
+ OVR_ASSERT(cmd->Size <= MaxSize);
+
+ if (Size)
+ Destruct<ThreadCommand>(toCommand());
+ Size = cmd->Size;
+ memcpy(Buffer, (void*)cmd, Size);
+}
+
+void ThreadCommand::PopBuffer::Execute()
+{
+ ThreadCommand* command = toCommand();
+ OVR_ASSERT(command);
+ command->Execute();
+ if (NeedsWait())
+ GetEvent()->PulseEvent();
+}
+
+//-------------------------------------------------------------------------------------
+
+class ThreadCommandQueueImpl : public NewOverrideBase
+{
+ typedef ThreadCommand::NotifyEvent NotifyEvent;
+ friend class ThreadCommandQueue;
+
+public:
+
+ ThreadCommandQueueImpl(ThreadCommandQueue* queue)
+ : pQueue(queue), ExitEnqueued(false), ExitProcessed(false), CommandBuffer(2048)
+ {
+ }
+ ~ThreadCommandQueueImpl();
+
+
+ bool PushCommand(const ThreadCommand& command);
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+
+ // ExitCommand is used by notify us that Thread is shutting down.
+ struct ExitCommand : public ThreadCommand
+ {
+ ThreadCommandQueueImpl* pImpl;
+
+ ExitCommand(ThreadCommandQueueImpl* impl, bool wait)
+ : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { }
+
+ virtual void Execute() const
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ pImpl->ExitProcessed = true;
+ }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ExitCommand>(p, *this); }
+ };
+
+
+ NotifyEvent* AllocNotifyEvent_NTS()
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+
+ if (!AvailableEvents.IsNull(p))
+ p->RemoveNode();
+ else
+ p = new NotifyEvent;
+ return p;
+ }
+
+ void FreeNotifyEvent_NTS(NotifyEvent* p)
+ {
+ AvailableEvents.PushBack(p);
+ }
+
+ void FreeNotifyEvents_NTS()
+ {
+ while(!AvailableEvents.IsEmpty())
+ {
+ NotifyEvent* p = AvailableEvents.GetFirst();
+ p->RemoveNode();
+ delete p;
+ }
+ }
+
+ ThreadCommandQueue* pQueue;
+ Lock QueueLock;
+ volatile bool ExitEnqueued;
+ volatile bool ExitProcessed;
+ List<NotifyEvent> AvailableEvents;
+ List<NotifyEvent> BlockedProducers;
+ CircularBuffer CommandBuffer;
+};
+
+
+
+ThreadCommandQueueImpl::~ThreadCommandQueueImpl()
+{
+ Lock::Locker lock(&QueueLock);
+ OVR_ASSERT(BlockedProducers.IsEmpty());
+ FreeNotifyEvents_NTS();
+}
+
+bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command)
+{
+ ThreadCommand::NotifyEvent* completeEvent = 0;
+ ThreadCommand::NotifyEvent* queueAvailableEvent = 0;
+
+ // Repeat writing command into buffer until it is available.
+ do {
+
+ { // Lock Scope
+ Lock::Locker lock(&QueueLock);
+
+ if (queueAvailableEvent)
+ {
+ FreeNotifyEvent_NTS(queueAvailableEvent);
+ queueAvailableEvent = 0;
+ }
+
+ // Don't allow any commands after PushExitCommand() is called.
+ if (ExitEnqueued && !command.ExitFlag)
+ return false;
+
+
+ bool bufferWasEmpty = CommandBuffer.IsEmpty();
+ UByte* buffer = CommandBuffer.Write(command.GetSize());
+ if (buffer)
+ {
+ ThreadCommand* c = command.CopyConstruct(buffer);
+ if (c->NeedsWait())
+ completeEvent = c->pEvent = AllocNotifyEvent_NTS();
+ // Signal-waker consumer when we add data to buffer.
+ if (bufferWasEmpty)
+ pQueue->OnPushNonEmpty_Locked();
+ break;
+ }
+
+ queueAvailableEvent = AllocNotifyEvent_NTS();
+ BlockedProducers.PushBack(queueAvailableEvent);
+ } // Lock Scope
+
+ queueAvailableEvent->Wait();
+
+ } while(1);
+
+ // Command was enqueued, wait if necessary.
+ if (completeEvent)
+ {
+ completeEvent->Wait();
+ Lock::Locker lock(&QueueLock);
+ FreeNotifyEvent_NTS(completeEvent);
+ }
+
+ return true;
+}
+
+
+// Pops the next command from the thread queue, if any is available.
+bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ Lock::Locker lock(&QueueLock);
+
+ UByte* buffer = CommandBuffer.ReadBegin();
+ if (!buffer)
+ {
+ // Notify thread while in lock scope, enabling initialization of wait.
+ pQueue->OnPopEmpty_Locked();
+ return false;
+ }
+
+ popBuffer->InitFromBuffer(buffer);
+ CommandBuffer.ReadEnd(popBuffer->GetSize());
+
+ if (!BlockedProducers.IsEmpty())
+ {
+ ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst();
+ queueAvailableEvent->RemoveNode();
+ queueAvailableEvent->PulseEvent();
+ // Event is freed later by waiter.
+ }
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+
+ThreadCommandQueue::ThreadCommandQueue()
+{
+ pImpl = new ThreadCommandQueueImpl(this);
+}
+ThreadCommandQueue::~ThreadCommandQueue()
+{
+ delete pImpl;
+}
+
+bool ThreadCommandQueue::PushCommand(const ThreadCommand& command)
+{
+ return pImpl->PushCommand(command);
+}
+
+bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer)
+{
+ return pImpl->PopCommand(popBuffer);
+}
+
+void ThreadCommandQueue::PushExitCommand(bool wait)
+{
+ // Exit is processed in two stages:
+ // - First, ExitEnqueued flag is set to block further commands from queuing up.
+ // - Second, the actual exit call is processed on the consumer thread, flushing
+ // any prior commands.
+ // IsExiting() only returns true after exit has flushed.
+ {
+ Lock::Locker lock(&pImpl->QueueLock);
+ if (pImpl->ExitEnqueued)
+ return;
+ pImpl->ExitEnqueued = true;
+ }
+
+ PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait));
+}
+
+bool ThreadCommandQueue::IsExiting() const
+{
+ return pImpl->ExitProcessed;
+}
+
+
+} // namespace OVR
diff --git a/LibOVR/Src/OVR_ThreadCommandQueue.h b/LibOVR/Src/OVR_ThreadCommandQueue.h
new file mode 100644
index 0000000..9774212
--- /dev/null
+++ b/LibOVR/Src/OVR_ThreadCommandQueue.h
@@ -0,0 +1,319 @@
+/************************************************************************************
+
+PublicHeader: None
+Filename : OVR_ThreadCommandQueue.h
+Content : Command queue for operations executed on a thread
+Created : October 29, 2012
+Author : 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.
+
+************************************************************************************/
+
+#ifndef OVR_ThreadCommandQueue_h
+#define OVR_ThreadCommandQueue_h
+
+#include "Kernel/OVR_Types.h"
+#include "Kernel/OVR_List.h"
+#include "Kernel/OVR_Atomic.h"
+#include "Kernel/OVR_Threads.h"
+
+namespace OVR {
+
+class ThreadCommand;
+class ThreadCommandQueue;
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommand
+
+// ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue.
+class ThreadCommand
+{
+public:
+
+ // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the
+ // calling (producer) thread when command is completed or queue slot is available.
+ class NotifyEvent : public ListNode<NotifyEvent>, public NewOverrideBase
+ {
+ Event E;
+ public:
+ NotifyEvent() { }
+
+ void Wait() { E.Wait(); }
+ void PulseEvent() { E.PulseEvent(); }
+ };
+
+ // ThreadCommand::PopBuffer is temporary storage for a command popped off
+ // by ThreadCommandQueue::PopCommand.
+ class PopBuffer
+ {
+ enum { MaxSize = 256 };
+
+ UPInt Size;
+ union {
+ UByte Buffer[MaxSize];
+ UPInt Align;
+ };
+
+ ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; }
+
+ public:
+ PopBuffer() : Size(0) { }
+ ~PopBuffer();
+
+ void InitFromBuffer(void* data);
+
+ bool HasCommand() const { return Size != 0; }
+ UPInt GetSize() const { return Size; }
+ bool NeedsWait() const { return toCommand()->NeedsWait(); }
+ NotifyEvent* GetEvent() const { return toCommand()->pEvent; }
+
+ // Execute the command and also notifies caller to finish waiting,
+ // if necessary.
+ void Execute();
+ };
+
+ UInt16 Size;
+ bool WaitFlag;
+ bool ExitFlag; // Marks the last exit command.
+ NotifyEvent* pEvent;
+
+ ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false)
+ : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { }
+ virtual ~ThreadCommand() { }
+
+ bool NeedsWait() const { return WaitFlag; }
+ UPInt GetSize() const { return Size; }
+
+ virtual void Execute() const = 0;
+ // Copy constructor used for serializing this to memory buffer.
+ virtual ThreadCommand* CopyConstruct(void* p) const = 0;
+};
+
+
+//-------------------------------------------------------------------------------------
+
+// CleanType is a template that strips 'const' and '&' modifiers from the argument type;
+// for example, typename CleanType<A&>::Type is equivalent to A.
+template<class T> struct CleanType { typedef T Type; };
+template<class T> struct CleanType<T&> { typedef T Type; };
+template<class T> struct CleanType<const T> { typedef T Type; };
+template<class T> struct CleanType<const T&> { typedef T Type; };
+
+// SelfType is a template that yields the argument type. This helps avoid conflicts with
+// automatic template argument deduction for function calls when identical argument
+// is already defined.
+template<class T> struct SelfType { typedef T Type; };
+
+
+
+//-------------------------------------------------------------------------------------
+// ThreadCommand specializations for member functions with different number of
+// arguments and argument types.
+
+// Used to return nothing from a ThreadCommand, to avoid problems with 'void'.
+struct Void
+{
+ Void() {}
+ Void(int) {}
+};
+
+// ThreadCommand for member function with 0 arguments.
+template<class C, class R>
+class ThreadCommandMF0 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)();
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)()) :
+ (void)(pClass->*pFn)();
+ }
+
+public:
+ ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF0), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF0>(p, *this); }
+};
+
+
+// ThreadCommand for member function with 1 argument.
+template<class C, class R, class A0>
+class ThreadCommandMF1 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) :
+ (void)(pClass->*pFn)(AVal0);
+ }
+
+public:
+ ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF1), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF1>(p, *this); }
+};
+
+// ThreadCommand for member function with 2 arguments.
+template<class C, class R, class A0, class A1>
+class ThreadCommandMF2 : public ThreadCommand
+{
+ typedef R (C::*FnPtr)(A0, A1);
+ C* pClass;
+ FnPtr pFn;
+ R* pRet;
+ typename CleanType<A0>::Type AVal0;
+ typename CleanType<A1>::Type AVal1;
+
+ void executeImpl() const
+ {
+ pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) :
+ (void)(pClass->*pFn)(AVal0, AVal1);
+ }
+
+public:
+ ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait)
+ : ThreadCommand(sizeof(ThreadCommandMF2), needsWait),
+ pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { }
+
+ virtual void Execute() const { executeImpl(); }
+ virtual ThreadCommand* CopyConstruct(void* p) const
+ { return Construct<ThreadCommandMF2>(p, *this); }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** ThreadCommandQueue
+
+// ThreadCommandQueue is a queue of executable function-call commands intended to be
+// serviced by a single consumer thread. Commands are added to the queue with PushCall
+// and removed with PopCall; they are processed in FIFO order. Multiple producer threads
+// are supported and will be blocked if internal data buffer is full.
+
+class ThreadCommandQueue
+{
+public:
+
+ ThreadCommandQueue();
+ virtual ~ThreadCommandQueue();
+
+
+ // Pops the next command from the thread queue, if any is available.
+ // The command should be executed by calling popBuffer->Execute().
+ // Returns 'false' if no command is available at the time of the call.
+ bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
+
+ // Generic implementaion of PushCommand; enqueues a command for execution.
+ // Returns 'false' if push failed, usually indicating thread shutdown.
+ bool PushCommand(const ThreadCommand& command);
+
+ //
+ void PushExitCommand(bool wait);
+
+ // Returns 'true' once ExitCommand has been processed, so the thread can shut down.
+ bool IsExiting() const;
+
+
+ // These two virtual functions serve as notifications for derived
+ // thread waiting.
+ virtual void OnPushNonEmpty_Locked() { }
+ virtual void OnPopEmpty_Locked() { }
+
+
+ // *** PushCall with no result
+
+ // Enqueue a member function of 'this' class to be called on consumer thread.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, 0, a0, a1, wait)); }
+ // Enqueue a specified member function call of class C.
+ // By default the function returns immediately; set 'wait' argument to 'true' to
+ // wait for completion.
+ template<class C, class R>
+ bool PushCall(C* p, R (C::*fn)(), bool wait = false)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, 0, wait)); }
+ template<class C, class R, class A0>
+ bool PushCall(C* p, R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, 0, a0, wait)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCall(C* p, R (C::*fn)(A0, A1),
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, 0, a0, a1, wait)); }
+
+
+ // *** PushCall with Result
+
+ // Enqueue a member function of 'this' class call and wait for call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, ret, a0, a1, true)); }
+ // Enqueue a member function call for class C and wait for the call to complete
+ // on consumer thread before returning.
+ template<class C, class R>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret)
+ { return PushCommand(ThreadCommandMF0<C,R>(p, fn, ret, true)); }
+ template<class C, class R, class A0>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
+ { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, ret, a0, true)); }
+ template<class C, class R, class A0, class A1>
+ bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret,
+ typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
+ { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, ret, a0, a1, true)); }
+
+private:
+ class ThreadCommandQueueImpl* pImpl;
+};
+
+
+}
+
+#endif // OVR_ThreadCommandQueue_h
diff --git a/LibOVR/Src/Util/Util_ImageWindow.cpp b/LibOVR/Src/Util/Util_ImageWindow.cpp
new file mode 100644
index 0000000..cb091c7
--- /dev/null
+++ b/LibOVR/Src/Util/Util_ImageWindow.cpp
@@ -0,0 +1,511 @@
+/************************************************************************************
+
+Filename : Util_ImageWindow.cpp
+Content : An output object for windows that can display raw images for testing
+Created : March 13, 2014
+Authors : Dean Beeler
+
+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 "../../Include/OVR.h"
+
+#include "Util_ImageWindow.h"
+
+#if defined(OVR_OS_WIN32)
+
+#include <Windows.h>
+
+#include "DWrite.h"
+
+typedef HRESULT (WINAPI *D2D1CreateFactoryFn)(
+ _In_ D2D1_FACTORY_TYPE,
+ _In_ REFIID,
+ _In_opt_ const D2D1_FACTORY_OPTIONS*,
+ _Out_ ID2D1Factory **
+ );
+
+typedef HRESULT (WINAPI *DWriteCreateFactoryFn)(
+ _In_ DWRITE_FACTORY_TYPE factoryType,
+ _In_ REFIID iid,
+ _Out_ IUnknown **factory
+ );
+
+
+namespace OVR { namespace Util {
+
+ID2D1Factory* ImageWindow::pD2DFactory = NULL;
+IDWriteFactory* ImageWindow::pDWriteFactory = NULL;
+ImageWindow* ImageWindow::globalWindow[4];
+int ImageWindow::windowCount = 0;
+
+LRESULT CALLBACK MainWndProc(
+ HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ return 0;
+
+ case WM_PAINT:
+ {
+ LONG_PTR ptr = GetWindowLongPtr( hwnd, GWLP_USERDATA );
+ if( ptr )
+ {
+ ImageWindow* iw = (ImageWindow*)ptr;
+ iw->OnPaint();
+ }
+ }
+
+ return 0;
+
+ case WM_SIZE:
+ // Set the size and position of the window.
+ return 0;
+
+ case WM_DESTROY:
+ // Clean up window-specific data objects.
+ return 0;
+
+ //
+ // Process other messages.
+ //
+
+ default:
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+ //return 0;
+}
+
+ImageWindow::ImageWindow( uint32_t width, uint32_t height ) :
+ frontBufferMutex( new Mutex() )
+{
+
+ HINSTANCE hInst = LoadLibrary( L"d2d1.dll" );
+ HINSTANCE hInstWrite = LoadLibrary( L"Dwrite.dll" );
+
+ D2D1CreateFactoryFn createFactory = NULL;
+ DWriteCreateFactoryFn writeFactory = NULL;
+
+ if( hInst )
+ {
+ createFactory = (D2D1CreateFactoryFn)GetProcAddress( hInst, "D2D1CreateFactory" );
+ }
+
+ if( hInstWrite )
+ {
+ writeFactory = (DWriteCreateFactoryFn)GetProcAddress( hInstWrite, "DWriteCreateFactory" );
+ }
+
+ globalWindow[windowCount] = this;
+
+ ++windowCount;
+
+ if( pD2DFactory == NULL && createFactory && writeFactory )
+ {
+ createFactory(
+ D2D1_FACTORY_TYPE_MULTI_THREADED,
+ __uuidof(ID2D1Factory),
+ NULL,
+ &pD2DFactory
+ );
+
+ // Create a DirectWrite factory.
+ writeFactory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(pDWriteFactory),
+ reinterpret_cast<IUnknown **>(&pDWriteFactory)
+ );
+
+ }
+
+ resolution = D2D1::SizeU( width, height );
+
+ SetWindowLongPtr( hWindow, GWLP_USERDATA, (LONG_PTR)this );
+
+ pRT = NULL;
+ greyBitmap = NULL;
+ colorBitmap = NULL;
+}
+
+ImageWindow::~ImageWindow()
+{
+ for( int i = 0; i < MaxWindows; ++i )
+ {
+ if( globalWindow[i] == this )
+ {
+ globalWindow[i] = NULL;
+ break;
+ }
+}
+
+ if( greyBitmap )
+ greyBitmap->Release();
+
+ if( colorBitmap )
+ colorBitmap->Release();
+
+ if( pRT )
+ pRT->Release();
+
+ {
+ Mutex::Locker locker( frontBufferMutex );
+
+ while( frames.GetSize() )
+ {
+ Ptr<Frame> aFrame = frames.PopBack();
+ }
+ }
+
+ delete frontBufferMutex;
+
+ ShowWindow( hWindow, SW_HIDE );
+ DestroyWindow( hWindow );
+}
+
+void ImageWindow::AssociateSurface( void* surface )
+{
+ // Assume an IUnknown
+ IUnknown* unknown = (IUnknown*)surface;
+
+ IDXGISurface *pDxgiSurface = NULL;
+ HRESULT hr = unknown->QueryInterface(&pDxgiSurface);
+ if( hr == S_OK )
+ {
+ D2D1_RENDER_TARGET_PROPERTIES props =
+ D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
+ 96,
+ 96
+ );
+
+
+ pRT = NULL;
+ ID2D1RenderTarget* tmpTarget;
+
+ hr = pD2DFactory->CreateDxgiSurfaceRenderTarget( pDxgiSurface, &props, &tmpTarget );
+
+ if( hr == S_OK )
+ {
+ DXGI_SURFACE_DESC desc = {0};
+ pDxgiSurface->GetDesc( &desc );
+ int width = desc.Width;
+ int height = desc.Height;
+
+ D2D1_SIZE_U size = D2D1::SizeU( width, height );
+
+ D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
+ DXGI_FORMAT_A8_UNORM,
+ D2D1_ALPHA_MODE_PREMULTIPLIED
+ );
+
+ D2D1_PIXEL_FORMAT colorPixelFormat = D2D1::PixelFormat(
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_PREMULTIPLIED
+ );
+
+ D2D1_BITMAP_PROPERTIES bitmapProps;
+ bitmapProps.dpiX = 96;
+ bitmapProps.dpiY = 96;
+ bitmapProps.pixelFormat = pixelFormat;
+
+ D2D1_BITMAP_PROPERTIES colorBitmapProps;
+ colorBitmapProps.dpiX = 96;
+ colorBitmapProps.dpiY = 96;
+ colorBitmapProps.pixelFormat = colorPixelFormat;
+
+ HRESULT result = tmpTarget->CreateBitmap( size, bitmapProps, &greyBitmap );
+ if( result != S_OK )
+ {
+ tmpTarget->Release();
+ tmpTarget = NULL;
+ }
+
+ result = tmpTarget->CreateBitmap( size, colorBitmapProps, &colorBitmap );
+ if( result != S_OK )
+ {
+ greyBitmap->Release();
+ greyBitmap = NULL;
+
+ tmpTarget->Release();
+ tmpTarget = NULL;
+ }
+ pRT = tmpTarget;
+ }
+ }
+}
+
+void ImageWindow::Process()
+{
+ if( pRT && greyBitmap )
+ {
+ OnPaint();
+
+ pRT->Flush();
+ }
+}
+
+void ImageWindow::Complete()
+{
+ Mutex::Locker locker( frontBufferMutex );
+
+ if( frames.IsEmpty() )
+ return;
+
+ if( frames.PeekBack(0)->ready )
+ return;
+
+ Ptr<Frame> frame = frames.PeekBack(0);
+
+ frame->ready = true;
+}
+
+void ImageWindow::OnPaint()
+{
+ Mutex::Locker locker( frontBufferMutex );
+
+ // Nothing to do
+ if( frames.IsEmpty() )
+ return;
+
+ if( !frames.PeekFront(0)->ready )
+ return;
+
+ Ptr<Frame> currentFrame = frames.PopFront();
+
+ Ptr<Frame> nextFrame = NULL;
+
+ if( !frames.IsEmpty() )
+ nextFrame = frames.PeekFront(0);
+
+ while( nextFrame && nextFrame->ready )
+ {
+ // Free up the current frame since it's been removed from the deque
+ currentFrame = frames.PopFront();
+
+ if( frames.IsEmpty() )
+ break;
+
+ nextFrame = frames.PeekFront(0);
+ }
+
+ if( currentFrame->imageData )
+ greyBitmap->CopyFromMemory( NULL, currentFrame->imageData, currentFrame->width );
+
+ if( currentFrame->colorImageData )
+ colorBitmap->CopyFromMemory( NULL, currentFrame->colorImageData, currentFrame->colorPitch );
+
+ pRT->BeginDraw();
+
+ pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ pRT->Clear( D2D1::ColorF(D2D1::ColorF::Black) );
+
+ // This will mirror our image
+ D2D1_MATRIX_3X2_F m;
+ m._11 = -1; m._12 = 0;
+ m._21 = 0; m._22 = 1;
+ m._31 = 0; m._32 = 0;
+ pRT->SetTransform( m );
+
+ ID2D1SolidColorBrush* whiteBrush;
+
+ pRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White, 1.0f), &whiteBrush );
+
+ if( currentFrame->imageData )
+ {
+ pRT->FillOpacityMask( greyBitmap, whiteBrush,
+ D2D1_OPACITY_MASK_CONTENT_TEXT_NATURAL,
+ D2D1::RectF( -(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ),
+ //D2D1::RectF( 0.0f, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ),
+ D2D1::RectF( 0.0f, 0.0f, (FLOAT)resolution.width, (FLOAT)resolution.height ) );
+ }
+ else if( currentFrame->colorImageData )
+ {
+ pRT->DrawBitmap( colorBitmap,
+ D2D1::RectF( -(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ) );
+
+ }
+
+ pRT->SetTransform(D2D1::Matrix3x2F::Identity());
+
+ whiteBrush->Release();
+
+ Array<CirclePlot>::Iterator it;
+
+ for( it = currentFrame->plots.Begin(); it != currentFrame->plots.End(); ++it )
+ {
+ ID2D1SolidColorBrush* aBrush;
+
+ pRT->CreateSolidColorBrush( D2D1::ColorF( it->r, it->g, it->b), &aBrush );
+
+ D2D1_ELLIPSE ellipse;
+ ellipse.point.x = it->x;
+ ellipse.point.y = it->y;
+ ellipse.radiusX = it->radius;
+ ellipse.radiusY = it->radius;
+
+ if( it->fill )
+ pRT->FillEllipse( &ellipse, aBrush );
+ else
+ pRT->DrawEllipse( &ellipse, aBrush );
+
+ aBrush->Release();
+ }
+
+ static const WCHAR msc_fontName[] = L"Verdana";
+ static const FLOAT msc_fontSize = 20;
+
+ IDWriteTextFormat* textFormat = NULL;
+
+ // Create a DirectWrite text format object.
+ pDWriteFactory->CreateTextFormat(
+ msc_fontName,
+ NULL,
+ DWRITE_FONT_WEIGHT_NORMAL,
+ DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL,
+ msc_fontSize,
+ L"", //locale
+ &textFormat
+ );
+
+ D2D1_SIZE_F renderTargetSize = pRT->GetSize();
+
+ Array<TextPlot>::Iterator textIt;
+ for( textIt = currentFrame->textLines.Begin(); textIt != currentFrame->textLines.End(); ++textIt )
+ {
+ ID2D1SolidColorBrush* aBrush;
+
+ pRT->CreateSolidColorBrush( D2D1::ColorF( textIt->r, textIt->g, textIt->b), &aBrush );
+
+ WCHAR* tmpString = (WCHAR*)calloc( textIt->text.GetLength(), sizeof( WCHAR ) );
+ for( unsigned i = 0; i < textIt->text.GetLength(); ++i )
+ {
+ tmpString[i] = (WCHAR)textIt->text.GetCharAt( i );
+ }
+
+ pRT->DrawTextW( tmpString, (UINT32)textIt->text.GetLength(), textFormat,
+ D2D1::RectF(textIt->x, textIt->y, renderTargetSize.width, renderTargetSize.height), aBrush );
+
+ free( tmpString );
+
+ aBrush->Release();
+ }
+
+ if( textFormat )
+ textFormat->Release();
+
+ pRT->EndDraw();
+
+ pRT->Flush();
+}
+
+Ptr<Frame> ImageWindow::lastUnreadyFrame()
+{
+ static int framenumber = 0;
+
+ if( frames.GetSize() && !frames.PeekBack( 0 )->ready )
+ return frames.PeekBack( 0 );
+
+ // Create a new frame if an unready one doesn't already exist
+ Ptr<Frame> tmpFrame = *new Frame( framenumber );
+ frames.PushBack( tmpFrame );
+
+ ++framenumber;
+
+ return tmpFrame;
+}
+
+void ImageWindow::UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height )
+{
+ if( pRT && greyBitmap )
+ {
+ Mutex::Locker locker( frontBufferMutex );
+
+ Ptr<Frame> frame = lastUnreadyFrame();
+ frame->imageData = malloc( width * height );
+ frame->width = width;
+ frame->height = height;
+ memcpy( frame->imageData, imageData, width * height );
+ }
+}
+
+void ImageWindow::UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch )
+{
+ if( pRT && colorBitmap )
+ {
+ Mutex::Locker locker( frontBufferMutex );
+
+ Ptr<Frame> frame = lastUnreadyFrame();
+ frame->colorImageData = malloc( pitch * height );
+ frame->width = width;
+ frame->height = height;
+ frame->colorPitch = pitch;
+ memcpy( frame->colorImageData, imageData, pitch * height );
+ }
+}
+
+void ImageWindow::addCircle( float x, float y, float radius, float r, float g, float b, bool fill )
+{
+ if( pRT )
+ {
+ CirclePlot cp;
+
+ cp.x = x;
+ cp.y = y;
+ cp.radius = radius;
+ cp.r = r;
+ cp.g = g;
+ cp.b = b;
+ cp.fill = fill;
+
+ Mutex::Locker locker( frontBufferMutex );
+
+ Ptr<Frame> frame = lastUnreadyFrame();
+ frame->plots.PushBack( cp );
+ }
+
+}
+
+void ImageWindow::addText( float x, float y, float r, float g, float b, OVR::String text )
+{
+ if( pRT )
+ {
+ TextPlot tp;
+
+ tp.x = x;
+ tp.y = y;
+ tp.r = r;
+ tp.g = g;
+ tp.b = b;
+ tp.text = text;
+
+ Mutex::Locker locker( frontBufferMutex );
+ Ptr<Frame> frame = lastUnreadyFrame();
+ frame->textLines.PushBack( tp );
+ }
+}
+
+}}
+
+#endif //defined(OVR_OS_WIN32) \ No newline at end of file
diff --git a/LibOVR/Src/Util/Util_ImageWindow.h b/LibOVR/Src/Util/Util_ImageWindow.h
new file mode 100644
index 0000000..4b88959
--- /dev/null
+++ b/LibOVR/Src/Util/Util_ImageWindow.h
@@ -0,0 +1,200 @@
+/************************************************************************************
+
+Filename : Util_ImageWindow.h
+Content : An output object for windows that can display raw images for testing
+Created : March 13, 2014
+Authors : Dean Beeler
+
+Copyright : Copyright 2014 Oculus, 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.
+
+*************************************************************************************/
+
+#ifndef UTIL_IMAGEWINDOW_H
+#define UTIL_IMAGEWINDOW_H
+
+#if defined(OVR_OS_WIN32)
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#include <d2d1.h>
+#include <dwrite.h>
+#endif
+
+#include "../../Include/OVR.h"
+#include "../Kernel/OVR_Hash.h"
+#include "../Kernel/OVR_Array.h"
+#include "../Kernel/OVR_Threads.h"
+#include "../Kernel/OVR_Deque.h"
+
+#include <stdint.h>
+
+namespace OVR { namespace Util {
+
+ typedef struct
+ {
+ float x;
+ float y;
+ float radius;
+ float r;
+ float g;
+ float b;
+ bool fill;
+ } CirclePlot;
+
+ typedef struct
+ {
+ float x;
+ float y;
+ float r;
+ float g;
+ float b;
+ OVR::String text;
+ } TextPlot;
+
+class Frame : virtual public RefCountBaseV<Frame>
+ {
+public:
+
+ Frame( int frame ) :
+ frameNumber( frame ),
+ imageData( NULL ),
+ colorImageData( NULL ),
+ plots(),
+ textLines(),
+ width( 0 ),
+ height( 0 ),
+ colorPitch( 0 ),
+ ready( false )
+ {
+
+ }
+
+ ~Frame()
+ {
+ if( imageData )
+ free( imageData );
+ if( colorImageData )
+ free( colorImageData );
+
+ plots.ClearAndRelease();
+ textLines.ClearAndRelease();
+ }
+
+ int frameNumber;
+
+ Array<CirclePlot> plots;
+ Array<TextPlot> textLines;
+ void* imageData;
+ void* colorImageData;
+ int width;
+ int height;
+ int colorPitch;
+ bool ready;
+};
+
+#if defined(OVR_OS_WIN32)
+class ImageWindow
+{
+ HWND hWindow;
+ ID2D1RenderTarget* pRT;
+ D2D1_SIZE_U resolution;
+
+ Mutex* frontBufferMutex;
+
+ InPlaceMutableDeque< Ptr<Frame> > frames;
+
+ ID2D1Bitmap* greyBitmap;
+ ID2D1Bitmap* colorBitmap;
+
+public:
+ // constructors
+ ImageWindow();
+ ImageWindow( uint32_t width, uint32_t height );
+ virtual ~ImageWindow();
+
+ void GetResolution( size_t& width, size_t& height ) { width = resolution.width; height = resolution.height; }
+
+ void OnPaint(); // Called by Windows when it receives a WM_PAINT message
+
+ void UpdateImage( const uint8_t* imageData, uint32_t width, uint32_t height ) { UpdateImageBW( imageData, width, height ); }
+ void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height );
+ void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch );
+ void Complete(); // Called by drawing thread to submit a frame
+
+ void Process(); // Called by rendering thread to do window processing
+
+ void AssociateSurface( void* surface );
+
+ void addCircle( float x , float y, float radius, float r, float g, float b, bool fill );
+ void addText( float x, float y, float r, float g, float b, OVR::String text );
+
+ static ImageWindow* GlobalWindow( int window ) { return globalWindow[window]; }
+ static int WindowCount() { return windowCount; }
+
+private:
+
+ Ptr<Frame> lastUnreadyFrame();
+
+ static const int MaxWindows = 4;
+ static ImageWindow* globalWindow[MaxWindows];
+ static int windowCount;
+ static ID2D1Factory* pD2DFactory;
+ static IDWriteFactory* pDWriteFactory;
+};
+
+#else
+
+class ImageWindow
+{
+public:
+ // constructors
+ ImageWindow() {}
+ ImageWindow( uint32_t width, uint32_t height ) { OVR_UNUSED( width ); OVR_UNUSED( height ); }
+ virtual ~ImageWindow() { }
+
+ void GetResolution( size_t& width, size_t& height ) { width = 0; height = 0; }
+
+ void OnPaint() { }
+
+ void UpdateImage( const uint8_t* imageData, uint32_t width, uint32_t height ) { UpdateImageBW( imageData, width, height ); }
+ void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ) { }
+ void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ) { }
+ void Complete() { }
+
+ void Process() { }
+
+ void AssociateSurface( void* surface ) { }
+
+ void addCircle( float x , float y, float radius, float r, float g, float b, bool fill ) { }
+ void addText( float x, float y, float r, float g, float b, OVR::String text ) { }
+
+ static ImageWindow* GlobalWindow( int window ) { return globalWindow[window]; }
+ static int WindowCount() { return windowCount; }
+
+private:
+
+ static const int MaxWindows = 4;
+ static ImageWindow* globalWindow[4];
+ static int windowCount;
+};
+
+#endif
+
+}} // namespace OVR::Util
+
+
+#endif \ No newline at end of file
diff --git a/LibOVR/Src/Util/Util_Interface.cpp b/LibOVR/Src/Util/Util_Interface.cpp
new file mode 100644
index 0000000..d96423c
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Interface.cpp
@@ -0,0 +1,34 @@
+/************************************************************************************
+
+Filename : Util_Interface.cpp
+Content : Simple interface, utilised by internal demos,
+ with access to wider SDK as needed.
+ Located in the body of the SDK to ensure updated
+ when new SDK features are added.
+Created : February 20, 2014
+Authors : Tom Heath
+
+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 "Util_Interface.h"
+
+
+
+//Files left in to ease its possible return...... \ No newline at end of file
diff --git a/LibOVR/Src/Util/Util_Interface.h b/LibOVR/Src/Util/Util_Interface.h
new file mode 100644
index 0000000..1bbf638
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Interface.h
@@ -0,0 +1,37 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_Interface.h
+Content : Simple interface, utilised by internal demos,
+ with access to wider SDK as needed.
+ Located in the body of the SDK to ensure updated
+ when new SDK features are added.
+Created : February 20, 2014
+Authors : Tom Heath
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_Interface_h
+#define OVR_Util_Interface_h
+#include "../../Src/OVR_CAPI.h"
+
+//Files left in to ease its possible return......
+
+#endif
diff --git a/LibOVR/Src/Util/Util_LatencyTest.cpp b/LibOVR/Src/Util/Util_LatencyTest.cpp
new file mode 100644
index 0000000..3017c72
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest.cpp
@@ -0,0 +1,570 @@
+/************************************************************************************
+
+Filename : Util_LatencyTest.cpp
+Content : Wraps the lower level LatencyTester interface and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+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 "Util_LatencyTest.h"
+
+#include "../Kernel/OVR_Log.h"
+#include "../Kernel/OVR_Timer.h"
+
+namespace OVR { namespace Util {
+
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5;
+static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5;
+static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions.
+static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4;
+static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000;
+static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000;
+static const Color CALIBRATE_BLACK(0, 0, 0);
+static const Color CALIBRATE_WHITE(255, 255, 255);
+static const Color COLOR1(0, 0, 0);
+static const Color COLOR2(255, 255, 255);
+static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255);
+static const float BIG_FLOAT = 1000000.0f;
+static const float SMALL_FLOAT = -1000000.0f;
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+
+LatencyTest::LatencyTest(LatencyTestDevice* device)
+ : Handler(getThis())
+{
+ if (device != NULL)
+ {
+ SetDevice(device);
+ }
+
+ reset();
+
+ srand(Timer::GetTicksMs());
+}
+
+LatencyTest::~LatencyTest()
+{
+ clearMeasurementResults();
+}
+
+bool LatencyTest::SetDevice(LatencyTestDevice* device)
+{
+
+ if (device != Device)
+ {
+ Handler.RemoveHandlerFromDevices();
+
+ Device = device;
+
+ if (Device != NULL)
+ {
+ Device->AddMessageHandler(&Handler);
+
+ // Set trigger threshold.
+ LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming.
+ Device->SetConfiguration(configuration, true);
+
+ // Set display to initial (3 dashes).
+ LatencyTestDisplay ltd(2, 0x40400040);
+ Device->SetDisplay(ltd);
+ }
+ }
+
+ return true;
+}
+
+UInt32 LatencyTest::getRandomComponent(UInt32 range)
+{
+ UInt32 val = rand() % range;
+ return val;
+}
+
+void LatencyTest::BeginTest()
+{
+ if (State == State_WaitingForButton)
+ {
+ // Set color to black and wait a while.
+ RenderColor = CALIBRATE_BLACK;
+
+ State = State_WaitingForSettlePreCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+}
+
+void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage)
+{
+ // For debugging.
+/* if (msg.Type == Message_LatencyTestSamples)
+ {
+ MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg;
+
+ if (pSamples->Samples.GetSize() > 0)
+ {
+ // Just show the first one for now.
+ Color c = pSamples->Samples[0];
+ OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B));
+ }
+ return;
+ }
+*/
+
+ if (latencyTestMessage == LatencyTest_Timer)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ if (State == State_WaitingForSettlePreCalibrationColorBlack)
+ {
+ // Send calibrate message to device and wait a while.
+ Device->SetCalibrate(CALIBRATE_BLACK);
+
+ State = State_WaitingForSettlePostCalibrationColorBlack;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorBlack)
+ {
+ // Change color to white and wait a while.
+ RenderColor = CALIBRATE_WHITE;
+
+ State = State_WaitingForSettlePreCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePreCalibrationColorWhite)
+ {
+ // Send calibrate message to device and wait a while.
+ Device->SetCalibrate(CALIBRATE_WHITE);
+
+ State = State_WaitingForSettlePostCalibrationColorWhite;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite."));
+
+ setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION);
+ }
+ else if (State == State_WaitingForSettlePostCalibrationColorWhite)
+ {
+ // Calibration is done. Switch to color 1 and wait for it to settle.
+ RenderColor = COLOR1;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForSettlePostMeasurement)
+ {
+ // Prepare for next measurement.
+
+ // Create a new result object.
+ MeasurementResult* pResult = new MeasurementResult();
+ Results.PushBack(pResult);
+
+ State = State_WaitingToTakeMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement."));
+ }
+ else if (State == State_WaitingForTestStarted)
+ {
+ // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForTestStarted = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'."));
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ else if (State == State_WaitingForColorDetected)
+ {
+ // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next.
+ getActiveResult()->TimedOutWaitingForColorDetected = true;
+
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'."));
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+ }
+ }
+ else if (latencyTestMessage == LatencyTest_ProcessInputs)
+ {
+ if (State == State_WaitingToTakeMeasurement)
+ {
+ if (!Device)
+ {
+ reset();
+ return;
+ }
+
+ // Send 'StartTest' feature report with opposite target color.
+ if (RenderColor == COLOR1)
+ {
+ RenderColor = COLOR2;
+ }
+ else
+ {
+ RenderColor = COLOR1;
+ }
+
+ getActiveResult()->TargetColor = RenderColor;
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->StartTestSeconds = Timer::GetSeconds();
+
+ Device->SetStartTest(RenderColor);
+
+ State = State_WaitingForTestStarted;
+ OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted."));
+
+ setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED);
+
+ LatencyTestDisplay ltd(2, 0x40090040);
+ Device->SetDisplay(ltd);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestButton)
+ {
+ BeginTest();
+ }
+ else if (msg.Type == Message_LatencyTestStarted)
+ {
+ if (State == State_WaitingForTestStarted)
+ {
+ clearTimer();
+
+ // Record time so we can determine usb roundtrip time.
+ getActiveResult()->TestStartedSeconds = Timer::GetSeconds();
+
+ State = State_WaitingForColorDetected;
+ OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected."));
+
+ setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED);
+ }
+ }
+ else if (msg.Type == Message_LatencyTestColorDetected)
+ {
+ if (State == State_WaitingForColorDetected)
+ {
+ // Record time to detect color.
+ MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg;
+ UInt16 elapsedTime = pDetected->Elapsed;
+ OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime));
+
+ getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime;
+
+ if (areResultsComplete())
+ {
+ // We're done.
+ processResults();
+ reset();
+ }
+ else
+ {
+ // Run another measurement.
+ State = State_WaitingForSettlePostMeasurement;
+ OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement."));
+
+ UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS);
+ setTimer(waitTime);
+
+ LatencyTestDisplay ltd(2, 0x40400040);
+ Device->SetDisplay(ltd);
+ }
+ }
+ }
+ else if (msg.Type == Message_DeviceRemoved)
+ {
+ reset();
+ }
+}
+
+LatencyTest::MeasurementResult* LatencyTest::getActiveResult()
+{
+ OVR_ASSERT(!Results.IsEmpty());
+ return Results.GetLast();
+}
+
+void LatencyTest::setTimer(UInt32 timeMilliS)
+{
+ ActiveTimerMilliS = timeMilliS;
+}
+
+void LatencyTest::clearTimer()
+{
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::reset()
+{
+ clearMeasurementResults();
+ State = State_WaitingForButton;
+
+ HaveOldTime = false;
+ ActiveTimerMilliS = 0;
+}
+
+void LatencyTest::clearMeasurementResults()
+{
+ while(!Results.IsEmpty())
+ {
+ MeasurementResult* pElem = Results.GetFirst();
+ pElem->RemoveNode();
+ delete pElem;
+ }
+}
+
+LatencyTest::LatencyTestHandler::~LatencyTestHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg)
+{
+ pLatencyTestUtil->handleMessage(msg);
+}
+
+void LatencyTest::ProcessInputs()
+{
+ updateForTimeouts();
+ handleMessage(Message(), LatencyTest_ProcessInputs);
+}
+
+bool LatencyTest::DisplayScreenColor(Color& colorToDisplay)
+{
+ updateForTimeouts();
+
+ if (State == State_WaitingForButton)
+ {
+ return false;
+ }
+
+ colorToDisplay = RenderColor;
+ return true;
+}
+
+const char* LatencyTest::GetResultsString()
+{
+ if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr())
+ {
+ ReturnedResultString = ResultsString;
+ return ReturnedResultString.ToCStr();
+ }
+
+ return NULL;
+}
+
+bool LatencyTest::areResultsComplete()
+{
+ UInt32 initialMeasurements = 0;
+
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ while(true)
+ {
+ // Process.
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ initialMeasurements++;
+
+ if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+ }
+ else
+ {
+ measurements2to1++;
+ }
+ }
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void LatencyTest::processResults()
+{
+
+ UInt32 minTime1To2 = UINT_MAX;
+ UInt32 maxTime1To2 = 0;
+ float averageTime1To2 = 0.0f;
+ UInt32 minTime2To1 = UINT_MAX;
+ UInt32 maxTime2To1 = 0;
+ float averageTime2To1 = 0.0f;
+
+ float minUSBTripMilliS = BIG_FLOAT;
+ float maxUSBTripMilliS = SMALL_FLOAT;
+ float averageUSBTripMilliS = 0.0f;
+ UInt32 countUSBTripTime = 0;
+
+ UInt32 measurementsCount = 0;
+ UInt32 measurements1to2 = 0;
+ UInt32 measurements2to1 = 0;
+
+ MeasurementResult* pCurr = Results.GetFirst();
+ UInt32 count = 0;
+ while(true)
+ {
+ count++;
+
+ if (!pCurr->TimedOutWaitingForTestStarted &&
+ !pCurr->TimedOutWaitingForColorDetected)
+ {
+ measurementsCount++;
+
+ if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE)
+ {
+ if (pCurr->TargetColor == COLOR2)
+ {
+ measurements1to2++;
+
+ if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime1To2 = Alg::Min(elapsed, minTime1To2);
+ maxTime1To2 = Alg::Max(elapsed, maxTime1To2);
+
+ averageTime1To2 += (float) elapsed;
+ }
+ }
+ else
+ {
+ measurements2to1++;
+
+ if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS;
+
+ minTime2To1 = Alg::Min(elapsed, minTime2To1);
+ maxTime2To1 = Alg::Max(elapsed, maxTime2To1);
+
+ averageTime2To1 += (float) elapsed;
+ }
+ }
+
+ float usbRountripElapsedMilliS = Timer::MsPerSecond * (float) (pCurr->TestStartedSeconds - pCurr->StartTestSeconds);
+ minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS);
+ maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS);
+ averageUSBTripMilliS += usbRountripElapsedMilliS;
+ countUSBTripTime++;
+ }
+ }
+
+ if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES &&
+ measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES)
+ {
+ break;
+ }
+
+ if (Results.IsLast(pCurr))
+ {
+ break;
+ }
+ pCurr = Results.GetNext(pCurr);
+ }
+
+ averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+ averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES;
+
+ averageUSBTripMilliS /= countUSBTripTime;
+
+ float finalResult = 0.5f * (averageTime1To2 + averageTime2To1);
+ finalResult += averageUSBTripMilliS;
+
+ ResultsString.Clear();
+ ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]",
+ finalResult,
+ minTime1To2, averageTime1To2, maxTime1To2,
+ minTime2To1, averageTime2To1, maxTime2To1,
+ minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS,
+ DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount);
+
+ // Display result on latency tester display.
+ LatencyTestDisplay ltd(1, (int)finalResult);
+ Device->SetDisplay(ltd);
+}
+
+void LatencyTest::updateForTimeouts()
+{
+ if (!HaveOldTime)
+ {
+ HaveOldTime = true;
+ OldTime = Timer::GetTicksMs();
+ return;
+ }
+
+ UInt32 newTime = Timer::GetTicksMs();
+ UInt32 elapsedMilliS = newTime - OldTime;
+ if (newTime < OldTime)
+ {
+ elapsedMilliS = OldTime - newTime;
+ elapsedMilliS = UINT_MAX - elapsedMilliS;
+ }
+ OldTime = newTime;
+
+ elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often.
+
+
+ if (ActiveTimerMilliS == 0)
+ {
+ return;
+ }
+
+ if (elapsedMilliS >= ActiveTimerMilliS)
+ {
+ ActiveTimerMilliS = 0;
+ handleMessage(Message(), LatencyTest_Timer);
+ return;
+ }
+
+ ActiveTimerMilliS -= elapsedMilliS;
+}
+
+}} // namespace OVR::Util
diff --git a/LibOVR/Src/Util/Util_LatencyTest.h b/LibOVR/Src/Util/Util_LatencyTest.h
new file mode 100644
index 0000000..0844603
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest.h
@@ -0,0 +1,173 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_LatencyTest.h
+Content : Wraps the lower level LatencyTesterDevice and adds functionality.
+Created : February 14, 2013
+Authors : Lee Cooper
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_LatencyTest_h
+#define OVR_Util_LatencyTest_h
+
+#include "../OVR_Device.h"
+
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_List.h"
+
+namespace OVR { namespace Util {
+
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest
+//
+// LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling
+// of a latency test. A single test is composed of a series of individual latency measurements
+// which are used to derive min, max, and an average latency value.
+//
+// Developers are required to call the following methods:
+// SetDevice - Sets the LatencyTestDevice to be used for the tests.
+// ProcessInputs - This should be called at the same place in the code where the game engine
+// reads the headset orientation from LibOVR (typically done by calling
+// 'GetOrientation' on the SensorFusion object). Calling this at the right time
+// enables us to measure the same latency that occurs for headset orientation
+// changes.
+// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly
+// beneath it. The color of these pixels can be set by drawing a small
+// quad at the end of the rendering stage. The quad should be small
+// such that it doesn't significantly impact the rendering of the scene,
+// but large enough to be 'seen' by the sensor. See the SDK
+// documentation for more information.
+// GetResultsString - Call this to get a string containing the most recent results.
+// If the string has already been gotten then NULL will be returned.
+// The string pointer will remain valid until the next time this
+// method is called.
+//
+
+class LatencyTest : public NewOverrideBase
+{
+public:
+ LatencyTest(LatencyTestDevice* device = NULL);
+ ~LatencyTest();
+
+ // Set the Latency Tester device that we'll use to send commands to and receive
+ // notification messages from.
+ bool SetDevice(LatencyTestDevice* device);
+
+ // Returns true if this LatencyTestUtil has a Latency Tester device.
+ bool HasDevice() const
+ { return Handler.IsHandlerInstalled(); }
+
+ void ProcessInputs();
+ bool DisplayScreenColor(Color& colorToDisplay);
+ const char* GetResultsString();
+
+ bool IsMeasuringNow() const { return (State != State_WaitingForButton); }
+
+ // Begin test. Equivalent to pressing the button on the latency tester.
+ void BeginTest();
+
+private:
+ LatencyTest* getThis() { return this; }
+
+ enum LatencyTestMessageType
+ {
+ LatencyTest_None,
+ LatencyTest_Timer,
+ LatencyTest_ProcessInputs,
+ };
+
+ UInt32 getRandomComponent(UInt32 range);
+ void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None);
+ void reset();
+ void setTimer(UInt32 timeMilliS);
+ void clearTimer();
+
+ class LatencyTestHandler : public MessageHandler
+ {
+ LatencyTest* pLatencyTestUtil;
+ public:
+ LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { }
+ ~LatencyTestHandler();
+
+ virtual void OnMessage(const Message& msg);
+ };
+
+ bool areResultsComplete();
+ void processResults();
+ void updateForTimeouts();
+
+ Ptr<LatencyTestDevice> Device;
+ LatencyTestHandler Handler;
+
+ enum TesterState
+ {
+ State_WaitingForButton,
+ State_WaitingForSettlePreCalibrationColorBlack,
+ State_WaitingForSettlePostCalibrationColorBlack,
+ State_WaitingForSettlePreCalibrationColorWhite,
+ State_WaitingForSettlePostCalibrationColorWhite,
+ State_WaitingToTakeMeasurement,
+ State_WaitingForTestStarted,
+ State_WaitingForColorDetected,
+ State_WaitingForSettlePostMeasurement
+ };
+ TesterState State;
+
+ bool HaveOldTime;
+ UInt32 OldTime;
+ UInt32 ActiveTimerMilliS;
+
+ Color RenderColor;
+
+ struct MeasurementResult : public ListNode<MeasurementResult>, public NewOverrideBase
+ {
+ MeasurementResult()
+ : DeviceMeasuredElapsedMilliS(0),
+ TimedOutWaitingForTestStarted(false),
+ TimedOutWaitingForColorDetected(false),
+ StartTestSeconds(0.0),
+ TestStartedSeconds(0.0)
+ {}
+
+ Color TargetColor;
+
+ UInt32 DeviceMeasuredElapsedMilliS;
+
+ bool TimedOutWaitingForTestStarted;
+ bool TimedOutWaitingForColorDetected;
+
+ double StartTestSeconds;
+ double TestStartedSeconds;
+ };
+
+ List<MeasurementResult> Results;
+ void clearMeasurementResults();
+
+ MeasurementResult* getActiveResult();
+
+ StringBuffer ResultsString;
+ String ReturnedResultString;
+};
+
+}} // namespace OVR::Util
+
+#endif // OVR_Util_LatencyTest_h
diff --git a/LibOVR/Src/Util/Util_LatencyTest2.cpp b/LibOVR/Src/Util/Util_LatencyTest2.cpp
new file mode 100644
index 0000000..6fc8b1f
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest2.cpp
@@ -0,0 +1,191 @@
+/************************************************************************************
+
+Filename : Util_LatencyTest2.cpp
+Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality.
+Created : March 10, 2014
+Authors : Volga Aksoy
+
+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 "Util_LatencyTest2.h"
+
+#include "../OVR_CAPI.h"
+#include "../Kernel/OVR_Log.h"
+#include "../Kernel/OVR_Timer.h"
+
+
+namespace OVR { namespace Util {
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest2
+
+LatencyTest2::LatencyTest2(SensorDevice* device)
+ : Handler(getThis())
+ , TestActive(false)
+ , StartTiming(-1)
+ , LatencyMeasuredInSeconds(-1)
+ , LastPixelReadMsg(NULL)
+ , RenderColorValue(0)
+ , NumMsgsBeforeSettle(0)
+ , NumTestsSuccessful(0)
+{
+ if (device != NULL)
+ {
+ SetSensorDevice(device);
+ }
+}
+
+LatencyTest2::~LatencyTest2()
+{
+ HmdDevice = NULL;
+ LatencyTesterDev = NULL;
+
+ Handler.RemoveHandlerFromDevices();
+}
+
+bool LatencyTest2::SetSensorDevice(SensorDevice* device)
+{
+ Lock::Locker devLocker(&TesterLock);
+
+ // Enable/Disable pixel read from HMD
+ if (device != HmdDevice)
+ {
+ Handler.RemoveHandlerFromDevices();
+
+ HmdDevice = device;
+
+ if (HmdDevice != NULL)
+ {
+ HmdDevice->AddMessageHandler(&Handler);
+ }
+ }
+
+ return true;
+}
+
+bool LatencyTest2::SetDisplayDevice(LatencyTestDevice* device)
+{
+ Lock::Locker devLocker(&TesterLock);
+
+ if (device != LatencyTesterDev)
+ {
+ LatencyTesterDev = device;
+ if (LatencyTesterDev != NULL)
+ {
+ // Set display to initial (3 dashes).
+ LatencyTestDisplay ltd(2, 0x40400040);
+ LatencyTesterDev->SetDisplay(ltd);
+ }
+ }
+
+ return true;
+}
+
+void LatencyTest2::BeginTest(double startTime)
+{
+ Lock::Locker devLocker(&TesterLock);
+
+ if (!TestActive)
+ {
+ TestActive = true;
+ NumMsgsBeforeSettle = 0;
+
+ // Go to next pixel value
+ //RenderColorValue = (RenderColorValue == 0) ? 255 : 0;
+ RenderColorValue = (RenderColorValue + LT2_ColorIncrement) % 256;
+ RawStartTiming = LastPixelReadMsg.RawSensorTime;
+
+ if (startTime > 0.0)
+ StartTiming = startTime;
+ else
+ StartTiming = ovr_GetTimeInSeconds();
+
+ }
+}
+
+void LatencyTest2::handleMessage(const MessagePixelRead& msg)
+{
+ Lock::Locker devLocker(&TesterLock);
+
+ // Hold onto the last message as we will use this when we start a new test
+ LastPixelReadMsg = msg;
+
+ // If color readback index is valid, store it in the lock-less queue.
+ int readbackIndex = 0;
+ if (FrameTimeRecord::ColorToReadbackIndex(&readbackIndex, msg.PixelReadValue))
+ {
+ RecentFrameSet.AddValue(readbackIndex, msg.FrameTimeSeconds);
+ LockessRecords.SetState(RecentFrameSet);
+ }
+
+ NumMsgsBeforeSettle++;
+
+ if (TestActive)
+ {
+ int pixelValueDiff = RenderColorValue - LastPixelReadMsg.PixelReadValue;
+ int rawTimeDiff = LastPixelReadMsg.RawFrameTime - RawStartTiming;
+
+ if (pixelValueDiff < LT2_PixelTestThreshold && pixelValueDiff > -LT2_PixelTestThreshold)
+ {
+ TestActive = false;
+
+ LatencyMeasuredInSeconds = LastPixelReadMsg.FrameTimeSeconds - StartTiming;
+ RawLatencyMeasured = rawTimeDiff;
+ //LatencyMeasuredInSeconds = RawLatencyMeasured / 1000000.0;
+
+ if(LatencyTesterDev && (NumTestsSuccessful % 5) == 0)
+ {
+ int displayNum = (int)(RawLatencyMeasured / 100.0);
+ //int displayNum = NumMsgsBeforeSettle;
+ //int displayNum = (int)(LatencyMeasuredInSeconds * 1000.0);
+ LatencyTestDisplay ltd(1, displayNum);
+ LatencyTesterDev->SetDisplay(ltd);
+ }
+
+ NumTestsSuccessful++;
+ }
+ else if (TestActive && (rawTimeDiff / 1000) > LT2_TimeoutWaitingForColorDetected)
+ {
+ TestActive = false;
+ LatencyMeasuredInSeconds = -1;
+ }
+ }
+}
+
+LatencyTest2::PixelReadHandler::~PixelReadHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+void LatencyTest2::PixelReadHandler::OnMessage(const Message& msg)
+{
+ if(msg.Type == Message_PixelRead)
+ pLatencyTestUtil->handleMessage(static_cast<const MessagePixelRead&>(msg));
+}
+
+bool LatencyTest2::DisplayScreenColor(Color& colorToDisplay)
+{
+ Lock::Locker devLocker(&TesterLock);
+ colorToDisplay = Color(RenderColorValue, RenderColorValue, RenderColorValue, 255);
+
+ return TestActive;
+}
+
+}} // namespace OVR::Util
diff --git a/LibOVR/Src/Util/Util_LatencyTest2.h b/LibOVR/Src/Util/Util_LatencyTest2.h
new file mode 100644
index 0000000..61e8477
--- /dev/null
+++ b/LibOVR/Src/Util/Util_LatencyTest2.h
@@ -0,0 +1,238 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_LatencyTest2.h
+Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality.
+Created : March 10, 2014
+Authors : Volga Aksoy
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_LatencyTest2_h
+#define OVR_Util_LatencyTest2_h
+
+#include "../OVR_Device.h"
+
+#include "../Kernel/OVR_String.h"
+#include "../Kernel/OVR_List.h"
+#include "../Kernel/OVR_Lockless.h"
+
+namespace OVR { namespace Util {
+
+
+enum {
+ LT2_ColorIncrement = 32,
+ LT2_PixelTestThreshold = LT2_ColorIncrement / 3,
+ LT2_IncrementCount = 256 / LT2_ColorIncrement,
+ LT2_TimeoutWaitingForColorDetected = 1000 // 1 second
+};
+
+//-------------------------------------------------------------------------------------
+
+// Describes frame scanout time used for latency testing.
+struct FrameTimeRecord
+{
+ int ReadbackIndex;
+ double TimeSeconds;
+
+ // Utility functions to convert color to readBack indices and back.
+ // The purpose of ReadbackIndex is to allow direct comparison by value.
+
+ static bool ColorToReadbackIndex(int *readbackIndex, unsigned char color)
+ {
+ int compareColor = color - LT2_ColorIncrement/2;
+ int index = color / LT2_ColorIncrement; // Use color without subtraction due to rounding.
+ int delta = compareColor - index * LT2_ColorIncrement;
+
+ if ((delta < LT2_PixelTestThreshold) && (delta > -LT2_PixelTestThreshold))
+ {
+ *readbackIndex = index;
+ return true;
+ }
+ return false;
+ }
+
+ static unsigned char ReadbackIndexToColor(int readbackIndex)
+ {
+ OVR_ASSERT(readbackIndex < LT2_IncrementCount);
+ return (unsigned char)(readbackIndex * LT2_ColorIncrement + LT2_ColorIncrement/2);
+ }
+};
+
+// FrameTimeRecordSet is a container holding multiple consecutive frame timing records
+// returned from the lock-less state. Used by FrameTimeManager.
+
+struct FrameTimeRecordSet
+{
+ enum {
+ RecordCount = 4,
+ RecordMask = RecordCount - 1
+ };
+ FrameTimeRecord Records[RecordCount];
+ int NextWriteIndex;
+
+ FrameTimeRecordSet()
+ {
+ NextWriteIndex = 0;
+ memset(this, 0, sizeof(FrameTimeRecordSet));
+ }
+
+ void AddValue(int readValue, double timeSeconds)
+ {
+ Records[NextWriteIndex].ReadbackIndex = readValue;
+ Records[NextWriteIndex].TimeSeconds = timeSeconds;
+ NextWriteIndex ++;
+ if (NextWriteIndex == RecordCount)
+ NextWriteIndex = 0;
+ }
+ // Matching should be done starting from NextWrite index
+ // until wrap-around
+
+ const FrameTimeRecord& operator [] (int i) const
+ {
+ return Records[(NextWriteIndex + i) & RecordMask];
+ }
+
+ const FrameTimeRecord& GetMostRecentFrame()
+ {
+ return Records[(NextWriteIndex - 1) & RecordMask];
+ }
+
+ // Advances I to absolute color index
+ bool FindReadbackIndex(int* i, int readbackIndex) const
+ {
+ for (; *i < RecordCount; (*i)++)
+ {
+ if ((*this)[*i].ReadbackIndex == readbackIndex)
+ return true;
+ }
+ return false;
+ }
+
+ bool IsAllZeroes() const
+ {
+ for (int i = 0; i < RecordCount; i++)
+ if (Records[i].ReadbackIndex != 0)
+ return false;
+ return true;
+ }
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** LatencyTest2
+//
+// LatencyTest2 utility class wraps the low level SensorDevice and manages the scheduling
+// of a latency test. A single test is composed of a series of individual latency measurements
+// which are used to derive min, max, and an average latency value.
+//
+// Developers are required to call the following methods:
+// SetDevice - Sets the SensorDevice to be used for the tests.
+// ProcessInputs - This should be called at the same place in the code where the game engine
+// reads the headset orientation from LibOVR (typically done by calling
+// 'GetOrientation' on the SensorFusion object). Calling this at the right time
+// enables us to measure the same latency that occurs for headset orientation
+// changes.
+// DisplayScreenColor - The latency tester works by sensing the color of the pixels directly
+// beneath it. The color of these pixels can be set by drawing a small
+// quad at the end of the rendering stage. The quad should be small
+// such that it doesn't significantly impact the rendering of the scene,
+// but large enough to be 'seen' by the sensor. See the SDK
+// documentation for more information.
+// GetResultsString - Call this to get a string containing the most recent results.
+// If the string has already been gotten then NULL will be returned.
+// The string pointer will remain valid until the next time this
+// method is called.
+//
+
+class LatencyTest2 : public NewOverrideBase
+{
+public:
+ LatencyTest2(SensorDevice* device = NULL);
+ ~LatencyTest2();
+
+ // Set the Latency Tester device that we'll use to send commands to and receive
+ // notification messages from.
+ bool SetSensorDevice(SensorDevice* device);
+ bool SetDisplayDevice(LatencyTestDevice* device);
+
+ // Returns true if this LatencyTestUtil has a Latency Tester device.
+ bool HasDisplayDevice() const { return LatencyTesterDev.GetPtr() != NULL; }
+ bool HasDevice() const { return Handler.IsHandlerInstalled(); }
+
+ bool DisplayScreenColor(Color& colorToDisplay);
+ //const char* GetResultsString();
+
+ // Begin test. Equivalent to pressing the button on the latency tester.
+ void BeginTest(double startTime = -1.0f);
+ bool IsMeasuringNow() const { return TestActive; }
+ double GetMeasuredLatency() const { return LatencyMeasuredInSeconds; }
+
+//
+ FrameTimeRecordSet GetLocklessState() { return LockessRecords.GetState(); }
+
+private:
+ LatencyTest2* getThis() { return this; }
+
+ enum LatencyTestMessageType
+ {
+ LatencyTest_None,
+ LatencyTest_Timer,
+ LatencyTest_ProcessInputs,
+ };
+
+ void handleMessage(const MessagePixelRead& msg);
+
+ class PixelReadHandler : public MessageHandler
+ {
+ LatencyTest2* pLatencyTestUtil;
+ public:
+ PixelReadHandler(LatencyTest2* latencyTester) : pLatencyTestUtil(latencyTester) { }
+ ~PixelReadHandler();
+
+ virtual void OnMessage(const Message& msg);
+ };
+ PixelReadHandler Handler;
+
+ Ptr<SensorDevice> HmdDevice;
+ Ptr<LatencyTestDevice> LatencyTesterDev;
+
+ Lock TesterLock;
+ bool TestActive;
+ unsigned char RenderColorValue;
+ MessagePixelRead LastPixelReadMsg;
+ double StartTiming;
+ unsigned int RawStartTiming;
+ UInt32 RawLatencyMeasured;
+ double LatencyMeasuredInSeconds;
+ int NumMsgsBeforeSettle;
+ unsigned int NumTestsSuccessful;
+
+ // MA:
+ // Frames are added here, then copied into lockess state
+ FrameTimeRecordSet RecentFrameSet;
+ LocklessUpdater<FrameTimeRecordSet> LockessRecords;
+};
+
+
+
+}} // namespace OVR::Util
+
+#endif // OVR_Util_LatencyTest2_h
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.cpp b/LibOVR/Src/Util/Util_Render_Stereo.cpp
new file mode 100644
index 0000000..e84381e
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Render_Stereo.cpp
@@ -0,0 +1,1472 @@
+/************************************************************************************
+
+Filename : Util_Render_Stereo.cpp
+Content : Stereo rendering configuration implementation
+Created : October 22, 2012
+Authors : Michael Antonov, Andrew Reisse, Tom Forsyth
+
+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 "Util_Render_Stereo.h"
+#include "../OVR_SensorFusion.h"
+
+namespace OVR { namespace Util { namespace Render {
+
+
+//-----------------------------------------------------------------------------------
+// **** Useful debug functions.
+
+char const* GetDebugNameEyeCupType ( EyeCupType eyeCupType )
+{
+ switch ( eyeCupType )
+ {
+ case EyeCup_DK1A: return "DK1 A"; break;
+ case EyeCup_DK1B: return "DK1 B"; break;
+ case EyeCup_DK1C: return "DK1 C"; break;
+ case EyeCup_DKHD2A: return "DKHD2 A"; break;
+ case EyeCup_OrangeA: return "Orange A"; break;
+ case EyeCup_RedA: return "Red A"; break;
+ case EyeCup_PinkA: return "Pink A"; break;
+ case EyeCup_BlueA: return "Blue A"; break;
+ case EyeCup_Delilah1A: return "Delilah 1 A"; break;
+ case EyeCup_Delilah2A: return "Delilah 2 A"; break;
+ case EyeCup_JamesA: return "James A"; break;
+ case EyeCup_SunMandalaA: return "Sun Mandala A"; break;
+ case EyeCup_DK2A: return "DK2 A"; break;
+ case EyeCup_LAST: return "LAST"; break;
+ default: OVR_ASSERT ( false ); return "Error"; break;
+ }
+}
+
+char const* GetDebugNameHmdType ( HmdTypeEnum hmdType )
+{
+ switch ( hmdType )
+ {
+ case HmdType_None: return "None"; break;
+ case HmdType_DK1: return "DK1"; break;
+ case HmdType_DKProto: return "DK1 prototype"; break;
+ case HmdType_DKHDProto: return "DK HD prototype 1"; break;
+ case HmdType_DKHDProto566Mi: return "DK HD prototype 566 Mi"; break;
+ case HmdType_DKHD2Proto: return "DK HD prototype 585"; break;
+ case HmdType_CrystalCoveProto: return "Crystal Cove"; break;
+ case HmdType_DK2: return "DK2"; break;
+ case HmdType_Unknown: return "Unknown"; break;
+ case HmdType_LAST: return "LAST"; break;
+ default: OVR_ASSERT ( false ); return "Error"; break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------------
+// **** Internal pipeline functions.
+
+struct DistortionAndFov
+{
+ DistortionRenderDesc Distortion;
+ FovPort Fov;
+};
+
+static DistortionAndFov CalculateDistortionAndFovInternal ( StereoEye eyeType, HmdRenderInfo const &hmd,
+ LensConfig const *pLensOverride = NULL,
+ FovPort const *pTanHalfFovOverride = NULL,
+ float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION )
+{
+ // pLensOverride can be NULL, which means no override.
+
+ DistortionRenderDesc localDistortion = CalculateDistortionRenderDesc ( eyeType, hmd, pLensOverride );
+ FovPort fov = CalculateFovFromHmdInfo ( eyeType, localDistortion, hmd, extraEyeRotationInRadians );
+ // Here the app or the user would optionally clamp this visible fov to a smaller number if
+ // they want more perf or resolution and are willing to give up FOV.
+ // They may also choose to clamp UDLR differently e.g. to get cinemascope-style views.
+ if ( pTanHalfFovOverride != NULL )
+ {
+ fov = *pTanHalfFovOverride;
+ }
+
+ // Here we could call ClampToPhysicalScreenFov(), but we do want people
+ // to be able to play with larger-than-screen views.
+ // The calling app can always do the clamping itself.
+ DistortionAndFov result;
+ result.Distortion = localDistortion;
+ result.Fov = fov;
+
+ return result;
+}
+
+
+static Recti CalculateViewportInternal ( StereoEye eyeType,
+ Sizei const actualRendertargetSurfaceSize,
+ Sizei const requestedRenderedPixelSize,
+ bool bRendertargetSharedByBothEyes,
+ bool bMonoRenderingMode = false )
+{
+ Recti renderedViewport;
+ if ( bMonoRenderingMode || !bRendertargetSharedByBothEyes || (eyeType == StereoEye_Center) )
+ {
+ // One eye per RT.
+ renderedViewport.x = 0;
+ renderedViewport.y = 0;
+ renderedViewport.w = Alg::Min ( actualRendertargetSurfaceSize.w, requestedRenderedPixelSize.w );
+ renderedViewport.h = Alg::Min ( actualRendertargetSurfaceSize.h, requestedRenderedPixelSize.h );
+ }
+ else
+ {
+ // Both eyes share the RT.
+ renderedViewport.x = 0;
+ renderedViewport.y = 0;
+ renderedViewport.w = Alg::Min ( actualRendertargetSurfaceSize.w/2, requestedRenderedPixelSize.w );
+ renderedViewport.h = Alg::Min ( actualRendertargetSurfaceSize.h, requestedRenderedPixelSize.h );
+ if ( eyeType == StereoEye_Right )
+ {
+ renderedViewport.x = (actualRendertargetSurfaceSize.w+1)/2; // Round up, not down.
+ }
+ }
+ return renderedViewport;
+}
+
+static Recti CalculateViewportDensityInternal ( StereoEye eyeType,
+ DistortionRenderDesc const &distortion,
+ FovPort const &fov,
+ Sizei const &actualRendertargetSurfaceSize,
+ bool bRendertargetSharedByBothEyes,
+ float desiredPixelDensity = 1.0f,
+ bool bMonoRenderingMode = false )
+{
+ OVR_ASSERT ( actualRendertargetSurfaceSize.w > 0 );
+ OVR_ASSERT ( actualRendertargetSurfaceSize.h > 0 );
+
+ // What size RT do we need to get 1:1 mapping?
+ Sizei idealPixelSize = CalculateIdealPixelSize ( eyeType, distortion, fov, desiredPixelDensity );
+ // ...but we might not actually get that size.
+ return CalculateViewportInternal ( eyeType,
+ actualRendertargetSurfaceSize,
+ idealPixelSize,
+ bRendertargetSharedByBothEyes, bMonoRenderingMode );
+}
+
+static ViewportScaleAndOffset CalculateViewportScaleAndOffsetInternal (
+ ScaleAndOffset2D const &eyeToSourceNDC,
+ Recti const &renderedViewport,
+ Sizei const &actualRendertargetSurfaceSize )
+{
+ ViewportScaleAndOffset result;
+ result.RenderedViewport = renderedViewport;
+ result.EyeToSourceUV = CreateUVScaleAndOffsetfromNDCScaleandOffset(
+ eyeToSourceNDC, renderedViewport, actualRendertargetSurfaceSize );
+ return result;
+}
+
+
+static StereoEyeParams CalculateStereoEyeParamsInternal ( StereoEye eyeType, HmdRenderInfo const &hmd,
+ DistortionRenderDesc const &distortion,
+ FovPort const &fov,
+ Sizei const &actualRendertargetSurfaceSize,
+ Recti const &renderedViewport,
+ bool bRightHanded = true, float zNear = 0.01f, float zFar = 10000.0f,
+ bool bMonoRenderingMode = false,
+ float zoomFactor = 1.0f )
+{
+ // Generate the projection matrix for intermediate rendertarget.
+ // Z range can also be inserted later by the app (though not in this particular case)
+ float fovScale = 1.0f / zoomFactor;
+ FovPort zoomedFov = fov;
+ zoomedFov.LeftTan *= fovScale;
+ zoomedFov.RightTan *= fovScale;
+ zoomedFov.UpTan *= fovScale;
+ zoomedFov.DownTan *= fovScale;
+ Matrix4f projection = CreateProjection ( bRightHanded, zoomedFov, zNear, zFar );
+
+ // Find the mapping from TanAngle space to target NDC space.
+ // Note this does NOT take the zoom factor into account because
+ // this is the mapping of actual physical eye FOV (and our eyes do not zoom!)
+ // to screen space.
+ ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov ( fov );
+
+ // The size of the final FB, which is fixed and determined by the physical size of the device display.
+ Recti distortedViewport = GetFramebufferViewport ( eyeType, hmd );
+ Vector3f virtualCameraOffset = CalculateEyeVirtualCameraOffset(hmd, eyeType, bMonoRenderingMode);
+
+ StereoEyeParams result;
+ result.Eye = eyeType;
+ result.ViewAdjust = Matrix4f::Translation(virtualCameraOffset);
+ result.Distortion = distortion;
+ result.DistortionViewport = distortedViewport;
+ result.Fov = fov;
+ result.RenderedProjection = projection;
+ result.EyeToSourceNDC = eyeToSourceNDC;
+ ViewportScaleAndOffset vsao = CalculateViewportScaleAndOffsetInternal ( eyeToSourceNDC, renderedViewport, actualRendertargetSurfaceSize );
+ result.RenderedViewport = vsao.RenderedViewport;
+ result.EyeToSourceUV = vsao.EyeToSourceUV;
+
+ return result;
+}
+
+
+Vector3f CalculateEyeVirtualCameraOffset(HmdRenderInfo const &hmd,
+ StereoEye eyeType, bool bmonoRenderingMode)
+{
+ Vector3f virtualCameraOffset(0);
+
+ if (!bmonoRenderingMode)
+ {
+ float eyeCenterRelief = hmd.GetEyeCenter().ReliefInMeters;
+
+ if (eyeType == StereoEye_Left)
+ {
+ virtualCameraOffset.x = hmd.EyeLeft.NoseToPupilInMeters;
+ virtualCameraOffset.z = eyeCenterRelief - hmd.EyeLeft.ReliefInMeters;
+ }
+ else if (eyeType == StereoEye_Right)
+ {
+ virtualCameraOffset.x = -hmd.EyeRight.NoseToPupilInMeters;
+ virtualCameraOffset.z = eyeCenterRelief - hmd.EyeRight.ReliefInMeters;
+ }
+ }
+
+ return virtualCameraOffset;
+}
+
+
+//-----------------------------------------------------------------------------------
+// **** Higher-level utility functions.
+
+Sizei CalculateRecommendedTextureSize ( HmdRenderInfo const &hmd,
+ bool bRendertargetSharedByBothEyes,
+ float pixelDensityInCenter /*= 1.0f*/ )
+{
+ Sizei idealPixelSize[2];
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ StereoEye eyeType = ( eyeNum == 0 ) ? StereoEye_Left : StereoEye_Right;
+
+ DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION );
+
+ idealPixelSize[eyeNum] = CalculateIdealPixelSize ( eyeType,
+ distortionAndFov.Distortion,
+ distortionAndFov.Fov,
+ pixelDensityInCenter );
+ }
+
+ Sizei result;
+ result.w = Alg::Max ( idealPixelSize[0].w, idealPixelSize[1].w );
+ result.h = Alg::Max ( idealPixelSize[0].h, idealPixelSize[1].h );
+ if ( bRendertargetSharedByBothEyes )
+ {
+ result.w *= 2;
+ }
+ return result;
+}
+
+StereoEyeParams CalculateStereoEyeParams ( HmdRenderInfo const &hmd,
+ StereoEye eyeType,
+ Sizei const &actualRendertargetSurfaceSize,
+ bool bRendertargetSharedByBothEyes,
+ bool bRightHanded /*= true*/,
+ float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/,
+ Sizei const *pOverrideRenderedPixelSize /* = NULL*/,
+ FovPort const *pOverrideFovport /*= NULL*/,
+ float zoomFactor /*= 1.0f*/ )
+{
+ DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION );
+ if ( pOverrideFovport != NULL )
+ {
+ distortionAndFov.Fov = *pOverrideFovport;
+ }
+
+ Recti viewport;
+ if ( pOverrideRenderedPixelSize != NULL )
+ {
+ viewport = CalculateViewportInternal ( eyeType, actualRendertargetSurfaceSize, *pOverrideRenderedPixelSize, bRendertargetSharedByBothEyes, false );
+ }
+ else
+ {
+ viewport = CalculateViewportDensityInternal ( eyeType,
+ distortionAndFov.Distortion,
+ distortionAndFov.Fov,
+ actualRendertargetSurfaceSize, bRendertargetSharedByBothEyes, 1.0f, false );
+ }
+
+ return CalculateStereoEyeParamsInternal (
+ eyeType, hmd,
+ distortionAndFov.Distortion,
+ distortionAndFov.Fov,
+ actualRendertargetSurfaceSize, viewport,
+ bRightHanded, zNear, zFar, false, zoomFactor );
+}
+
+
+FovPort CalculateRecommendedFov ( HmdRenderInfo const &hmd,
+ StereoEye eyeType,
+ bool bMakeFovSymmetrical /* = false */ )
+{
+ DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION );
+ FovPort fov = distortionAndFov.Fov;
+ if ( bMakeFovSymmetrical )
+ {
+ // Deal with engines that cannot support an off-center projection.
+ // Unfortunately this means they will be rendering pixels that the user can't actually see.
+ float fovTanH = Alg::Max ( fov.LeftTan, fov.RightTan );
+ float fovTanV = Alg::Max ( fov.UpTan, fov.DownTan );
+ fov.LeftTan = fovTanH;
+ fov.RightTan = fovTanH;
+ fov.UpTan = fovTanV;
+ fov.DownTan = fovTanV;
+ }
+ return fov;
+}
+
+ViewportScaleAndOffset ModifyRenderViewport ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ Recti const &renderViewport )
+{
+ return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize );
+}
+
+ViewportScaleAndOffset ModifyRenderSize ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ Sizei const &requestedRenderSize,
+ bool bRendertargetSharedByBothEyes /*= false*/ )
+{
+ Recti renderViewport = CalculateViewportInternal ( params.Eye, actualRendertargetSurfaceSize, requestedRenderSize, bRendertargetSharedByBothEyes, false );
+ return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize );
+}
+
+ViewportScaleAndOffset ModifyRenderDensity ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ float pixelDensity /*= 1.0f*/,
+ bool bRendertargetSharedByBothEyes /*= false*/ )
+{
+ Recti renderViewport = CalculateViewportDensityInternal ( params.Eye, params.Distortion, params.Fov, actualRendertargetSurfaceSize, bRendertargetSharedByBothEyes, pixelDensity, false );
+ return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize );
+}
+
+
+//-----------------------------------------------------------------------------------
+// **** StereoConfig Implementation
+
+StereoConfig::StereoConfig(StereoMode mode)
+ : Mode(mode),
+ DirtyFlag(true)
+{
+ // Initialize "fake" default HMD values for testing without HMD plugged in.
+ // These default values match those returned by DK1
+ // (at least they did at time of writing - certainly good enough for debugging)
+ Hmd.HmdType = HmdType_None;
+ Hmd.ResolutionInPixels = Sizei(1280, 800);
+ Hmd.ScreenSizeInMeters = Sizef(0.1498f, 0.0936f);
+ Hmd.ScreenGapSizeInMeters = 0.0f;
+ Hmd.CenterFromTopInMeters = 0.0468f;
+ Hmd.LensSeparationInMeters = 0.0635f;
+ Hmd.LensDiameterInMeters = 0.035f;
+ Hmd.LensSurfaceToMidplateInMeters = 0.025f;
+ Hmd.EyeCups = EyeCup_DK1A;
+ Hmd.Shutter.Type = HmdShutter_RollingTopToBottom;
+ Hmd.Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
+ Hmd.Shutter.VsyncToFirstScanline = 0.000052f;
+ Hmd.Shutter.FirstScanlineToLastScanline = 0.016580f;
+ Hmd.Shutter.PixelSettleTime = 0.015f;
+ Hmd.Shutter.PixelPersistence = ( 1.0f / 60.0f );
+ Hmd.EyeLeft.Distortion.SetToIdentity();
+ Hmd.EyeLeft.Distortion.MetersPerTanAngleAtCenter = 0.043875f;
+ Hmd.EyeLeft.Distortion.Eqn = Distortion_RecipPoly4;
+ Hmd.EyeLeft.Distortion.K[0] = 1.0f;
+ Hmd.EyeLeft.Distortion.K[1] = -0.3999f;
+ Hmd.EyeLeft.Distortion.K[2] = 0.2408f;
+ Hmd.EyeLeft.Distortion.K[3] = -0.4589f;
+ Hmd.EyeLeft.Distortion.MaxR = 1.0f;
+ Hmd.EyeLeft.Distortion.ChromaticAberration[0] = 0.006f;
+ Hmd.EyeLeft.Distortion.ChromaticAberration[1] = 0.0f;
+ Hmd.EyeLeft.Distortion.ChromaticAberration[2] = -0.014f;
+ Hmd.EyeLeft.Distortion.ChromaticAberration[3] = 0.0f;
+ Hmd.EyeLeft.NoseToPupilInMeters = 0.62f;
+ Hmd.EyeLeft.ReliefInMeters = 0.013f;
+ Hmd.EyeRight = Hmd.EyeLeft;
+
+ SetViewportMode = SVPM_Density;
+ SetViewportPixelsPerDisplayPixel = 1.0f;
+ // Not used in this mode, but init them anyway.
+ SetViewportSize[0] = Sizei(0,0);
+ SetViewportSize[1] = Sizei(0,0);
+ SetViewport[0] = Recti(0,0,0,0);
+ SetViewport[1] = Recti(0,0,0,0);
+
+ OverrideLens = false;
+ OverrideTanHalfFov = false;
+ OverrideZeroIpd = false;
+ ExtraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION;
+ IsRendertargetSharedByBothEyes = true;
+ RightHandedProjection = true;
+
+ // This should cause an assert if the app does not call SetRendertargetSize()
+ RendertargetSize = Sizei ( 0, 0 );
+
+ ZNear = 0.01f;
+ ZFar = 10000.0f;
+
+ Set2DAreaFov(DegreeToRad(85.0f));
+}
+
+void StereoConfig::SetHmdRenderInfo(const HmdRenderInfo& hmd)
+{
+ Hmd = hmd;
+ DirtyFlag = true;
+}
+
+void StereoConfig::Set2DAreaFov(float fovRadians)
+{
+ Area2DFov = fovRadians;
+ DirtyFlag = true;
+}
+
+const StereoEyeParamsWithOrtho& StereoConfig::GetEyeRenderParams(StereoEye eye)
+{
+ if ( DirtyFlag )
+ {
+ UpdateComputedState();
+ }
+
+ static const UByte eyeParamIndices[3] = { 0, 0, 1 };
+
+ OVR_ASSERT(eye < sizeof(eyeParamIndices));
+ return EyeRenderParams[eyeParamIndices[eye]];
+}
+
+void StereoConfig::SetLensOverride ( LensConfig const *pLensOverrideLeft /*= NULL*/,
+ LensConfig const *pLensOverrideRight /*= NULL*/ )
+{
+ if ( pLensOverrideLeft == NULL )
+ {
+ OverrideLens = false;
+ }
+ else
+ {
+ OverrideLens = true;
+ LensOverrideLeft = *pLensOverrideLeft;
+ LensOverrideRight = *pLensOverrideLeft;
+ if ( pLensOverrideRight != NULL )
+ {
+ LensOverrideRight = *pLensOverrideRight;
+ }
+ }
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetRendertargetSize (Size<int> const rendertargetSize,
+ bool rendertargetIsSharedByBothEyes )
+{
+ RendertargetSize = rendertargetSize;
+ IsRendertargetSharedByBothEyes = rendertargetIsSharedByBothEyes;
+ DirtyFlag = true;
+}
+
+void StereoConfig::SetFov ( FovPort const *pfovLeft /*= NULL*/,
+ FovPort const *pfovRight /*= NULL*/ )
+{
+ DirtyFlag = true;
+ if ( pfovLeft == NULL )
+ {
+ OverrideTanHalfFov = false;
+ }
+ else
+ {
+ OverrideTanHalfFov = true;
+ FovOverrideLeft = *pfovLeft;
+ FovOverrideRight = *pfovLeft;
+ if ( pfovRight != NULL )
+ {
+ FovOverrideRight = *pfovRight;
+ }
+ }
+}
+
+
+void StereoConfig::SetZeroVirtualIpdOverride ( bool enableOverride )
+{
+ DirtyFlag = true;
+ OverrideZeroIpd = enableOverride;
+}
+
+
+void StereoConfig::SetZClipPlanesAndHandedness ( float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/, bool rightHandedProjection /*= true*/ )
+{
+ DirtyFlag = true;
+ ZNear = zNear;
+ ZFar = zFar;
+ RightHandedProjection = rightHandedProjection;
+}
+
+void StereoConfig::SetExtraEyeRotation ( float extraEyeRotationInRadians )
+{
+ DirtyFlag = true;
+ ExtraEyeRotationInRadians = extraEyeRotationInRadians;
+}
+
+Sizei StereoConfig::CalculateRecommendedTextureSize ( bool rendertargetSharedByBothEyes,
+ float pixelDensityInCenter /*= 1.0f*/ )
+{
+ return Render::CalculateRecommendedTextureSize ( Hmd, rendertargetSharedByBothEyes, pixelDensityInCenter );
+}
+
+
+
+void StereoConfig::UpdateComputedState()
+{
+ int numEyes = 2;
+ StereoEye eyeTypes[2];
+
+ switch ( Mode )
+ {
+ case Stereo_None:
+ numEyes = 1;
+ eyeTypes[0] = StereoEye_Center;
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ numEyes = 2;
+ eyeTypes[0] = StereoEye_Left;
+ eyeTypes[1] = StereoEye_Right;
+ break;
+
+ default:
+ OVR_ASSERT( false ); break;
+ }
+
+ // If either of these fire, you've probably forgotten to call SetRendertargetSize()
+ OVR_ASSERT ( RendertargetSize.w > 0 );
+ OVR_ASSERT ( RendertargetSize.h > 0 );
+
+ for ( int eyeNum = 0; eyeNum < numEyes; eyeNum++ )
+ {
+ StereoEye eyeType = eyeTypes[eyeNum];
+ LensConfig *pLensOverride = NULL;
+ if ( OverrideLens )
+ {
+ if ( eyeType == StereoEye_Right )
+ {
+ pLensOverride = &LensOverrideRight;
+ }
+ else
+ {
+ pLensOverride = &LensOverrideLeft;
+ }
+ }
+
+ FovPort *pTanHalfFovOverride = NULL;
+ if ( OverrideTanHalfFov )
+ {
+ if ( eyeType == StereoEye_Right )
+ {
+ pTanHalfFovOverride = &FovOverrideRight;
+ }
+ else
+ {
+ pTanHalfFovOverride = &FovOverrideLeft;
+ }
+ }
+
+ DistortionAndFov distortionAndFov =
+ CalculateDistortionAndFovInternal ( eyeType, Hmd,
+ pLensOverride, pTanHalfFovOverride,
+ ExtraEyeRotationInRadians );
+
+ EyeRenderParams[eyeNum].StereoEye.Distortion = distortionAndFov.Distortion;
+ EyeRenderParams[eyeNum].StereoEye.Fov = distortionAndFov.Fov;
+ }
+
+ if ( OverrideZeroIpd )
+ {
+ // Take the union of the calculated eye FOVs.
+ FovPort fov;
+ fov.UpTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.UpTan , EyeRenderParams[1].StereoEye.Fov.UpTan );
+ fov.DownTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.DownTan , EyeRenderParams[1].StereoEye.Fov.DownTan );
+ fov.LeftTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.LeftTan , EyeRenderParams[1].StereoEye.Fov.LeftTan );
+ fov.RightTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.RightTan, EyeRenderParams[1].StereoEye.Fov.RightTan );
+ EyeRenderParams[0].StereoEye.Fov = fov;
+ EyeRenderParams[1].StereoEye.Fov = fov;
+ }
+
+ for ( int eyeNum = 0; eyeNum < numEyes; eyeNum++ )
+ {
+ StereoEye eyeType = eyeTypes[eyeNum];
+
+ DistortionRenderDesc localDistortion = EyeRenderParams[eyeNum].StereoEye.Distortion;
+ FovPort fov = EyeRenderParams[eyeNum].StereoEye.Fov;
+
+ // Use a placeholder - will be overridden later.
+ Recti tempViewport = Recti ( 0, 0, 1, 1 );
+
+ EyeRenderParams[eyeNum].StereoEye = CalculateStereoEyeParamsInternal (
+ eyeType, Hmd, localDistortion, fov,
+ RendertargetSize, tempViewport,
+ RightHandedProjection, ZNear, ZFar,
+ OverrideZeroIpd );
+
+ // We want to create a virtual 2D surface we can draw debug text messages to.
+ // We'd like it to be a fixed distance (OrthoDistance) away,
+ // and to cover a specific FOV (Area2DFov). We need to find the projection matrix for this,
+ // and also to know how large it is in pixels to achieve a 1:1 mapping at the center of the screen.
+ float orthoDistance = 0.8f;
+ float orthoHalfFov = tanf ( Area2DFov * 0.5f );
+ Vector2f unityOrthoPixelSize = localDistortion.PixelsPerTanAngleAtCenter * ( orthoHalfFov * 2.0f );
+ float localInterpupillaryDistance = Hmd.EyeLeft.NoseToPupilInMeters + Hmd.EyeRight.NoseToPupilInMeters;
+ if ( OverrideZeroIpd )
+ {
+ localInterpupillaryDistance = 0.0f;
+ }
+ Matrix4f ortho = CreateOrthoSubProjection ( true, eyeType,
+ orthoHalfFov, orthoHalfFov,
+ unityOrthoPixelSize.x, unityOrthoPixelSize.y,
+ orthoDistance, localInterpupillaryDistance,
+ EyeRenderParams[eyeNum].StereoEye.RenderedProjection );
+ EyeRenderParams[eyeNum].OrthoProjection = ortho;
+ }
+
+ // ...and now set up the viewport, scale & offset the way the app wanted.
+ setupViewportScaleAndOffsets();
+
+ if ( OverrideZeroIpd )
+ {
+ // Monocular rendering has some fragile parts... don't break any by accident.
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.UpTan == EyeRenderParams[1].StereoEye.Fov.UpTan );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.DownTan == EyeRenderParams[1].StereoEye.Fov.DownTan );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.LeftTan == EyeRenderParams[1].StereoEye.Fov.LeftTan );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.RightTan == EyeRenderParams[1].StereoEye.Fov.RightTan );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[0][0] == EyeRenderParams[1].StereoEye.RenderedProjection.M[0][0] );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[1][1] == EyeRenderParams[1].StereoEye.RenderedProjection.M[1][1] );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[0][2] == EyeRenderParams[1].StereoEye.RenderedProjection.M[0][2] );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[1][2] == EyeRenderParams[1].StereoEye.RenderedProjection.M[1][2] );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedViewport == EyeRenderParams[1].StereoEye.RenderedViewport );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceUV.Offset == EyeRenderParams[1].StereoEye.EyeToSourceUV.Offset );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceUV.Scale == EyeRenderParams[1].StereoEye.EyeToSourceUV.Scale );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceNDC.Offset == EyeRenderParams[1].StereoEye.EyeToSourceNDC.Offset );
+ OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceNDC.Scale == EyeRenderParams[1].StereoEye.EyeToSourceNDC.Scale );
+ OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[0][0] == EyeRenderParams[1].OrthoProjection.M[0][0] );
+ OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[1][1] == EyeRenderParams[1].OrthoProjection.M[1][1] );
+ OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[0][2] == EyeRenderParams[1].OrthoProjection.M[0][2] );
+ OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[1][2] == EyeRenderParams[1].OrthoProjection.M[1][2] );
+ }
+
+ DirtyFlag = false;
+}
+
+
+
+ViewportScaleAndOffsetBothEyes StereoConfig::setupViewportScaleAndOffsets()
+{
+ for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
+ {
+ StereoEye eyeType = ( eyeNum == 0 ) ? StereoEye_Left : StereoEye_Right;
+
+ DistortionRenderDesc localDistortion = EyeRenderParams[eyeNum].StereoEye.Distortion;
+ FovPort fov = EyeRenderParams[eyeNum].StereoEye.Fov;
+
+ Recti renderedViewport;
+ switch ( SetViewportMode )
+ {
+ case SVPM_Density:
+ renderedViewport = CalculateViewportDensityInternal (
+ eyeType, localDistortion, fov,
+ RendertargetSize, IsRendertargetSharedByBothEyes,
+ SetViewportPixelsPerDisplayPixel, OverrideZeroIpd );
+ break;
+ case SVPM_Size:
+ if ( ( eyeType == StereoEye_Right ) && !OverrideZeroIpd )
+ {
+ renderedViewport = CalculateViewportInternal (
+ eyeType, RendertargetSize,
+ SetViewportSize[1],
+ IsRendertargetSharedByBothEyes, OverrideZeroIpd );
+ }
+ else
+ {
+ renderedViewport = CalculateViewportInternal (
+ eyeType, RendertargetSize,
+ SetViewportSize[0],
+ IsRendertargetSharedByBothEyes, OverrideZeroIpd );
+ }
+ break;
+ case SVPM_Viewport:
+ if ( ( eyeType == StereoEye_Right ) && !OverrideZeroIpd )
+ {
+ renderedViewport = SetViewport[1];
+ }
+ else
+ {
+ renderedViewport = SetViewport[0];
+ }
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+ ViewportScaleAndOffset vpsao = CalculateViewportScaleAndOffsetInternal (
+ EyeRenderParams[eyeNum].StereoEye.EyeToSourceNDC,
+ renderedViewport,
+ RendertargetSize );
+ EyeRenderParams[eyeNum].StereoEye.RenderedViewport = vpsao.RenderedViewport;
+ EyeRenderParams[eyeNum].StereoEye.EyeToSourceUV = vpsao.EyeToSourceUV;
+ }
+
+ ViewportScaleAndOffsetBothEyes result;
+ result.Left.EyeToSourceUV = EyeRenderParams[0].StereoEye.EyeToSourceUV;
+ result.Left.RenderedViewport = EyeRenderParams[0].StereoEye.RenderedViewport;
+ result.Right.EyeToSourceUV = EyeRenderParams[1].StereoEye.EyeToSourceUV;
+ result.Right.RenderedViewport = EyeRenderParams[1].StereoEye.RenderedViewport;
+ return result;
+}
+
+// Specify a pixel density - how many rendered pixels per pixel in the physical display.
+ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderDensity ( float pixelsPerDisplayPixel )
+{
+ SetViewportMode = SVPM_Density;
+ SetViewportPixelsPerDisplayPixel = pixelsPerDisplayPixel;
+ return setupViewportScaleAndOffsets();
+}
+
+// Supply the size directly. Will be clamped to the physical rendertarget size.
+ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderSize ( Sizei const &renderSizeLeft, Sizei const &renderSizeRight )
+{
+ SetViewportMode = SVPM_Size;
+ SetViewportSize[0] = renderSizeLeft;
+ SetViewportSize[1] = renderSizeRight;
+ return setupViewportScaleAndOffsets();
+}
+
+// Supply the viewport directly. This is not clamped to the physical rendertarget - careful now!
+ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderViewport ( Recti const &renderViewportLeft, Recti const &renderViewportRight )
+{
+ SetViewportMode = SVPM_Viewport;
+ SetViewport[0] = renderViewportLeft;
+ SetViewport[1] = renderViewportRight;
+ return setupViewportScaleAndOffsets();
+}
+
+Matrix4f StereoConfig::GetProjectionWithZoom ( StereoEye eye, float fovZoom ) const
+{
+ int eyeNum = ( eye == StereoEye_Right ) ? 1 : 0;
+ float fovScale = 1.0f / fovZoom;
+ FovPort fovPort = EyeRenderParams[eyeNum].StereoEye.Fov;
+ fovPort.LeftTan *= fovScale;
+ fovPort.RightTan *= fovScale;
+ fovPort.UpTan *= fovScale;
+ fovPort.DownTan *= fovScale;
+ return CreateProjection ( RightHandedProjection, fovPort, ZNear, ZFar );
+}
+
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Distortion Mesh Rendering
+
+
+// Pow2 for the Morton order to work!
+// 4 is too low - it is easy to see the "wobbles" in the HMD.
+// 5 is realllly close but you can see pixel differences with even/odd frame checking.
+// 6 is indistinguishable on a monitor on even/odd frames.
+static const int DMA_GridSizeLog2 = 6;
+static const int DMA_GridSize = 1<<DMA_GridSizeLog2;
+static const int DMA_NumVertsPerEye = (DMA_GridSize+1)*(DMA_GridSize+1);
+static const int DMA_NumTrisPerEye = (DMA_GridSize)*(DMA_GridSize)*2;
+
+
+
+void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices )
+{
+ OVR_FREE ( pVertices );
+ OVR_FREE ( pTriangleMeshIndices );
+}
+
+void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo )
+{
+ bool rightEye = ( stereoParams.Eye == StereoEye_Right );
+ int vertexCount = 0;
+ int triangleCount = 0;
+
+ // Generate mesh into allocated data and return result.
+ DistortionMeshCreate(ppVertices, ppTriangleListIndices, &vertexCount, &triangleCount,
+ rightEye, hmdRenderInfo, stereoParams.Distortion, stereoParams.EyeToSourceNDC);
+
+ *pNumVertices = vertexCount;
+ *pNumTriangles = triangleCount;
+}
+
+
+// Generate distortion mesh for a eye.
+void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ bool rightEye,
+ const HmdRenderInfo &hmdRenderInfo,
+ const DistortionRenderDesc &distortion, const ScaleAndOffset2D &eyeToSourceNDC )
+{
+ *pNumVertices = DMA_NumVertsPerEye;
+ *pNumTriangles = DMA_NumTrisPerEye;
+
+ *ppVertices = (DistortionMeshVertexData*)
+ OVR_ALLOC( sizeof(DistortionMeshVertexData) * (*pNumVertices) );
+ *ppTriangleListIndices = (UInt16*) OVR_ALLOC( sizeof(UInt16) * (*pNumTriangles) * 3 );
+
+ if (!*ppVertices || !*ppTriangleListIndices)
+ {
+ if (*ppVertices)
+ {
+ OVR_FREE(*ppVertices);
+ }
+ if (*ppTriangleListIndices)
+ {
+ OVR_FREE(*ppTriangleListIndices);
+ }
+ *ppVertices = NULL;
+ *ppTriangleListIndices = NULL;
+ *pNumTriangles = 0;
+ *pNumVertices = 0;
+ return;
+ }
+
+ // When does the fade-to-black edge start? Chosen heuristically.
+ const float fadeOutBorderFraction = 0.075f;
+
+
+ // Populate vertex buffer info
+ float xOffset = 0.0f;
+ float uOffset = 0.0f;
+ OVR_UNUSED(uOffset);
+
+ if (rightEye)
+ {
+ xOffset = 1.0f;
+ uOffset = 0.5f;
+ }
+
+ // First pass - build up raw vertex data.
+ DistortionMeshVertexData* pcurVert = *ppVertices;
+
+ for ( int y = 0; y <= DMA_GridSize; y++ )
+ {
+ for ( int x = 0; x <= DMA_GridSize; x++ )
+ {
+
+ Vector2f sourceCoordNDC;
+ // NDC texture coords [-1,+1]
+ sourceCoordNDC.x = 2.0f * ( (float)x / (float)DMA_GridSize ) - 1.0f;
+ sourceCoordNDC.y = 2.0f * ( (float)y / (float)DMA_GridSize ) - 1.0f;
+ Vector2f tanEyeAngle = TransformRendertargetNDCToTanFovSpace ( eyeToSourceNDC, sourceCoordNDC );
+
+ // This is the function that does the really heavy lifting.
+ Vector2f screenNDC = TransformTanFovSpaceToScreenNDC ( distortion, tanEyeAngle, false );
+
+ // We then need RGB UVs. Since chromatic aberration is generated from screen coords, not
+ // directly from texture NDCs, we can't just use tanEyeAngle, we need to go the long way round.
+ Vector2f tanEyeAnglesR, tanEyeAnglesG, tanEyeAnglesB;
+ TransformScreenNDCToTanFovSpaceChroma ( &tanEyeAnglesR, &tanEyeAnglesG, &tanEyeAnglesB,
+ distortion, screenNDC );
+
+ pcurVert->TanEyeAnglesR = tanEyeAnglesR;
+ pcurVert->TanEyeAnglesG = tanEyeAnglesG;
+ pcurVert->TanEyeAnglesB = tanEyeAnglesB;
+
+
+ HmdShutterTypeEnum shutterType = hmdRenderInfo.Shutter.Type;
+ switch ( shutterType )
+ {
+ case HmdShutter_Global:
+ pcurVert->TimewarpLerp = 0.0f;
+ break;
+ case HmdShutter_RollingLeftToRight:
+ // Retrace is left to right - left eye goes 0.0 -> 0.5, then right goes 0.5 -> 1.0
+ pcurVert->TimewarpLerp = screenNDC.x * 0.25f + 0.25f;
+ if (rightEye)
+ {
+ pcurVert->TimewarpLerp += 0.5f;
+ }
+ break;
+ case HmdShutter_RollingRightToLeft:
+ // Retrace is right to left - right eye goes 0.0 -> 0.5, then left goes 0.5 -> 1.0
+ pcurVert->TimewarpLerp = 0.75f - screenNDC.x * 0.25f;
+ if (rightEye)
+ {
+ pcurVert->TimewarpLerp -= 0.5f;
+ }
+ break;
+ case HmdShutter_RollingTopToBottom:
+ // Retrace is top to bottom on both eyes at the same time.
+ pcurVert->TimewarpLerp = screenNDC.y * 0.5f + 0.5f;
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+ // Fade out at texture edges.
+ float edgeFadeIn = ( 1.0f / fadeOutBorderFraction ) *
+ ( 1.0f - Alg::Max ( Alg::Abs ( sourceCoordNDC.x ), Alg::Abs ( sourceCoordNDC.y ) ) );
+ // Also fade out at screen edges.
+ float edgeFadeInScreen = ( 2.0f / fadeOutBorderFraction ) *
+ ( 1.0f - Alg::Max ( Alg::Abs ( screenNDC.x ), Alg::Abs ( screenNDC.y ) ) );
+ edgeFadeIn = Alg::Min ( edgeFadeInScreen, edgeFadeIn );
+
+ // Don't let verts overlap to the other eye.
+ screenNDC.x = Alg::Max ( -1.0f, Alg::Min ( screenNDC.x, 1.0f ) );
+ screenNDC.y = Alg::Max ( -1.0f, Alg::Min ( screenNDC.y, 1.0f ) );
+
+ pcurVert->Shade = Alg::Max ( 0.0f, Alg::Min ( edgeFadeIn, 1.0f ) );
+ pcurVert->ScreenPosNDC.x = 0.5f * screenNDC.x - 0.5f + xOffset;
+ pcurVert->ScreenPosNDC.y = -screenNDC.y;
+
+ pcurVert++;
+ }
+ }
+
+
+ // Populate index buffer info
+ UInt16 *pcurIndex = *ppTriangleListIndices;
+
+ for ( int triNum = 0; triNum < DMA_GridSize * DMA_GridSize; triNum++ )
+ {
+ // Use a Morton order to help locality of FB, texture and vertex cache.
+ // (0.325ms raster order -> 0.257ms Morton order)
+ OVR_ASSERT ( DMA_GridSize <= 256 );
+ int x = ( ( triNum & 0x0001 ) >> 0 ) |
+ ( ( triNum & 0x0004 ) >> 1 ) |
+ ( ( triNum & 0x0010 ) >> 2 ) |
+ ( ( triNum & 0x0040 ) >> 3 ) |
+ ( ( triNum & 0x0100 ) >> 4 ) |
+ ( ( triNum & 0x0400 ) >> 5 ) |
+ ( ( triNum & 0x1000 ) >> 6 ) |
+ ( ( triNum & 0x4000 ) >> 7 );
+ int y = ( ( triNum & 0x0002 ) >> 1 ) |
+ ( ( triNum & 0x0008 ) >> 2 ) |
+ ( ( triNum & 0x0020 ) >> 3 ) |
+ ( ( triNum & 0x0080 ) >> 4 ) |
+ ( ( triNum & 0x0200 ) >> 5 ) |
+ ( ( triNum & 0x0800 ) >> 6 ) |
+ ( ( triNum & 0x2000 ) >> 7 ) |
+ ( ( triNum & 0x8000 ) >> 8 );
+ int FirstVertex = x * (DMA_GridSize+1) + y;
+ // Another twist - we want the top-left and bottom-right quadrants to
+ // have the triangles split one way, the other two split the other.
+ // +---+---+---+---+
+ // | /| /|\ |\ |
+ // | / | / | \ | \ |
+ // |/ |/ | \| \|
+ // +---+---+---+---+
+ // | /| /|\ |\ |
+ // | / | / | \ | \ |
+ // |/ |/ | \| \|
+ // +---+---+---+---+
+ // |\ |\ | /| /|
+ // | \ | \ | / | / |
+ // | \| \|/ |/ |
+ // +---+---+---+---+
+ // |\ |\ | /| /|
+ // | \ | \ | / | / |
+ // | \| \|/ |/ |
+ // +---+---+---+---+
+ // This way triangle edges don't span long distances over the distortion function,
+ // so linear interpolation works better & we can use fewer tris.
+ if ( ( x < DMA_GridSize/2 ) != ( y < DMA_GridSize/2 ) ) // != is logical XOR
+ {
+ *pcurIndex++ = (UInt16)FirstVertex;
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1;
+
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1);
+ *pcurIndex++ = (UInt16)FirstVertex;
+ }
+ else
+ {
+ *pcurIndex++ = (UInt16)FirstVertex;
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1);
+
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Heightmap Mesh Rendering
+
+
+static const int HMA_GridSizeLog2 = 7;
+static const int HMA_GridSize = 1<<HMA_GridSizeLog2;
+static const int HMA_NumVertsPerEye = (HMA_GridSize+1)*(HMA_GridSize+1);
+static const int HMA_NumTrisPerEye = (HMA_GridSize)*(HMA_GridSize)*2;
+
+
+void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices )
+{
+ OVR_FREE ( pVertices );
+ OVR_FREE ( pTriangleMeshIndices );
+}
+
+void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo )
+{
+ bool rightEye = ( stereoParams.Eye == StereoEye_Right );
+ int vertexCount = 0;
+ int triangleCount = 0;
+
+ // Generate mesh into allocated data and return result.
+ HeightmapMeshCreate(ppVertices, ppTriangleListIndices, &vertexCount, &triangleCount,
+ rightEye, hmdRenderInfo, stereoParams.EyeToSourceNDC);
+
+ *pNumVertices = vertexCount;
+ *pNumTriangles = triangleCount;
+}
+
+
+// Generate heightmap mesh for one eye.
+void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles, bool rightEye,
+ const HmdRenderInfo &hmdRenderInfo,
+ const ScaleAndOffset2D &eyeToSourceNDC )
+{
+ *pNumVertices = HMA_NumVertsPerEye;
+ *pNumTriangles = HMA_NumTrisPerEye;
+
+ *ppVertices = (HeightmapMeshVertexData*) OVR_ALLOC( sizeof(HeightmapMeshVertexData) * (*pNumVertices) );
+ *ppTriangleListIndices = (UInt16*) OVR_ALLOC( sizeof(UInt16) * (*pNumTriangles) * 3 );
+
+ if (!*ppVertices || !*ppTriangleListIndices)
+ {
+ if (*ppVertices)
+ {
+ OVR_FREE(*ppVertices);
+ }
+ if (*ppTriangleListIndices)
+ {
+ OVR_FREE(*ppTriangleListIndices);
+ }
+ *ppVertices = NULL;
+ *ppTriangleListIndices = NULL;
+ *pNumTriangles = 0;
+ *pNumVertices = 0;
+ return;
+ }
+
+ // Populate vertex buffer info
+ float xOffset = 0.0f;
+ float uOffset = 0.0f;
+
+ if (rightEye)
+ {
+ xOffset = 1.0f;
+ uOffset = 0.5f;
+ }
+
+ // First pass - build up raw vertex data.
+ HeightmapMeshVertexData* pcurVert = *ppVertices;
+
+ for ( int y = 0; y <= HMA_GridSize; y++ )
+ {
+ for ( int x = 0; x <= HMA_GridSize; x++ )
+ {
+ Vector2f sourceCoordNDC;
+ // NDC texture coords [-1,+1]
+ sourceCoordNDC.x = 2.0f * ( (float)x / (float)HMA_GridSize ) - 1.0f;
+ sourceCoordNDC.y = 2.0f * ( (float)y / (float)HMA_GridSize ) - 1.0f;
+ Vector2f tanEyeAngle = TransformRendertargetNDCToTanFovSpace ( eyeToSourceNDC, sourceCoordNDC );
+
+ pcurVert->TanEyeAngles = tanEyeAngle;
+
+ HmdShutterTypeEnum shutterType = hmdRenderInfo.Shutter.Type;
+ switch ( shutterType )
+ {
+ case HmdShutter_Global:
+ pcurVert->TimewarpLerp = 0.0f;
+ break;
+ case HmdShutter_RollingLeftToRight:
+ // Retrace is left to right - left eye goes 0.0 -> 0.5, then right goes 0.5 -> 1.0
+ pcurVert->TimewarpLerp = sourceCoordNDC.x * 0.25f + 0.25f;
+ if (rightEye)
+ {
+ pcurVert->TimewarpLerp += 0.5f;
+ }
+ break;
+ case HmdShutter_RollingRightToLeft:
+ // Retrace is right to left - right eye goes 0.0 -> 0.5, then left goes 0.5 -> 1.0
+ pcurVert->TimewarpLerp = 0.75f - sourceCoordNDC.x * 0.25f;
+ if (rightEye)
+ {
+ pcurVert->TimewarpLerp -= 0.5f;
+ }
+ break;
+ case HmdShutter_RollingTopToBottom:
+ // Retrace is top to bottom on both eyes at the same time.
+ pcurVert->TimewarpLerp = sourceCoordNDC.y * 0.5f + 0.5f;
+ break;
+ default: OVR_ASSERT ( false ); break;
+ }
+
+ // Don't let verts overlap to the other eye.
+ //sourceCoordNDC.x = Alg::Max ( -1.0f, Alg::Min ( sourceCoordNDC.x, 1.0f ) );
+ //sourceCoordNDC.y = Alg::Max ( -1.0f, Alg::Min ( sourceCoordNDC.y, 1.0f ) );
+
+ //pcurVert->ScreenPosNDC.x = 0.5f * sourceCoordNDC.x - 0.5f + xOffset;
+ pcurVert->ScreenPosNDC.x = sourceCoordNDC.x;
+ pcurVert->ScreenPosNDC.y = -sourceCoordNDC.y;
+
+ pcurVert++;
+ }
+ }
+
+
+ // Populate index buffer info
+ UInt16 *pcurIndex = *ppTriangleListIndices;
+
+ for ( int triNum = 0; triNum < HMA_GridSize * HMA_GridSize; triNum++ )
+ {
+ // Use a Morton order to help locality of FB, texture and vertex cache.
+ // (0.325ms raster order -> 0.257ms Morton order)
+ OVR_ASSERT ( HMA_GridSize < 256 );
+ int x = ( ( triNum & 0x0001 ) >> 0 ) |
+ ( ( triNum & 0x0004 ) >> 1 ) |
+ ( ( triNum & 0x0010 ) >> 2 ) |
+ ( ( triNum & 0x0040 ) >> 3 ) |
+ ( ( triNum & 0x0100 ) >> 4 ) |
+ ( ( triNum & 0x0400 ) >> 5 ) |
+ ( ( triNum & 0x1000 ) >> 6 ) |
+ ( ( triNum & 0x4000 ) >> 7 );
+ int y = ( ( triNum & 0x0002 ) >> 1 ) |
+ ( ( triNum & 0x0008 ) >> 2 ) |
+ ( ( triNum & 0x0020 ) >> 3 ) |
+ ( ( triNum & 0x0080 ) >> 4 ) |
+ ( ( triNum & 0x0200 ) >> 5 ) |
+ ( ( triNum & 0x0800 ) >> 6 ) |
+ ( ( triNum & 0x2000 ) >> 7 ) |
+ ( ( triNum & 0x8000 ) >> 8 );
+ int FirstVertex = x * (HMA_GridSize+1) + y;
+ // Another twist - we want the top-left and bottom-right quadrants to
+ // have the triangles split one way, the other two split the other.
+ // +---+---+---+---+
+ // | /| /|\ |\ |
+ // | / | / | \ | \ |
+ // |/ |/ | \| \|
+ // +---+---+---+---+
+ // | /| /|\ |\ |
+ // | / | / | \ | \ |
+ // |/ |/ | \| \|
+ // +---+---+---+---+
+ // |\ |\ | /| /|
+ // | \ | \ | / | / |
+ // | \| \|/ |/ |
+ // +---+---+---+---+
+ // |\ |\ | /| /|
+ // | \ | \ | / | / |
+ // | \| \|/ |/ |
+ // +---+---+---+---+
+ // This way triangle edges don't span long distances over the distortion function,
+ // so linear interpolation works better & we can use fewer tris.
+ if ( ( x < HMA_GridSize/2 ) != ( y < HMA_GridSize/2 ) ) // != is logical XOR
+ {
+ *pcurIndex++ = (UInt16)FirstVertex;
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1;
+
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1);
+ *pcurIndex++ = (UInt16)FirstVertex;
+ }
+ else
+ {
+ *pcurIndex++ = (UInt16)FirstVertex;
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1);
+
+ *pcurIndex++ = (UInt16)FirstVertex+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1;
+ *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------------
+// ***** Prediction and timewarp.
+//
+
+// Calculates the values from the HMD info.
+PredictionValues PredictionGetDeviceValues ( const HmdRenderInfo &hmdRenderInfo,
+ bool withTimewarp /*= true*/,
+ bool withVsync /*= true*/ )
+{
+ PredictionValues result;
+
+ result.WithTimewarp = withTimewarp;
+ result.WithVsync = withVsync;
+
+ // For unclear reasons, most graphics systems add an extra frame of latency
+ // somewhere along the way. In time we'll debug this and figure it out, but
+ // for now this gets prediction a little bit better.
+ const float extraFramesOfBufferingKludge = 1.0f;
+
+ if ( withVsync )
+ {
+ // These are the times from the Present+Flush to when the middle of the scene is "averagely visible" (without timewarp)
+ // So if you had no timewarp, this, plus the time until the next vsync, is how much to predict by.
+ result.PresentFlushToRenderedScene = extraFramesOfBufferingKludge * hmdRenderInfo.Shutter.FirstScanlineToLastScanline;
+ // Predict to the middle of the screen being scanned out.
+ result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.VsyncToFirstScanline + 0.5f * hmdRenderInfo.Shutter.FirstScanlineToLastScanline;
+ // Time for pixels to get half-way to settling.
+ result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelSettleTime * 0.5f;
+ // Predict to half-way through persistence
+ result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelPersistence * 0.5f;
+
+ // The time from the Present+Flush to when the first scanline is "averagely visible".
+ result.PresentFlushToTimewarpStart = extraFramesOfBufferingKludge * hmdRenderInfo.Shutter.FirstScanlineToLastScanline;
+ // Predict to the first line being scanned out.
+ result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.VsyncToFirstScanline;
+ // Time for pixels to get half-way to settling.
+ result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.PixelSettleTime * 0.5f;
+ // Predict to half-way through persistence
+ result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.PixelPersistence * 0.5f;
+
+ // Time to the the last scanline.
+ result.PresentFlushToTimewarpEnd = result.PresentFlushToTimewarpStart + hmdRenderInfo.Shutter.FirstScanlineToLastScanline;
+
+ // Ideal framerate.
+ result.PresentFlushToPresentFlush = hmdRenderInfo.Shutter.VsyncToNextVsync;
+ }
+ else
+ {
+ // Timewarp without vsync is a little odd.
+ // Currently, we assume that without vsync, we have no idea which scanline
+ // is currently being sent to the display. So we can't do lerping timewarp,
+ // we can just do a full-screen late-stage fixup.
+
+ // "PresentFlushToRenderedScene" means the time from the Present+Flush to when the middle of the scene is "averagely visible" (without timewarp)
+ // So if you had no timewarp, this, plus the time until the next flush (which is usually the time to render the frame), is how much to predict by.
+ // Time for pixels to get half-way to settling.
+ result.PresentFlushToRenderedScene = hmdRenderInfo.Shutter.PixelSettleTime * 0.5f;
+ // Predict to half-way through persistence
+ result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelPersistence * 0.5f;
+
+ // Without vsync, you don't know timings, and so can't do anything useful with lerped warping.
+ result.PresentFlushToTimewarpStart = result.PresentFlushToRenderedScene;
+ result.PresentFlushToTimewarpEnd = result.PresentFlushToRenderedScene;
+
+ // There's no concept of "ideal" when vsync is off.
+ result.PresentFlushToPresentFlush = 0.0f;
+ }
+
+ return result;
+}
+
+Matrix4f TimewarpComputePoseDelta ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust )
+{
+ Matrix4f worldFromPredictedView = (eyeViewAdjust * predictedViewFromWorld).InvertedHomogeneousTransform();
+ Matrix4f matRenderFromNowStart = (eyeViewAdjust * renderedViewFromWorld) * worldFromPredictedView;
+
+ // The sensor-predicted orientations have: X=right, Y=up, Z=backwards.
+ // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards.
+ // So we need to perform a similarity transform on this delta matrix.
+ // The verbose code would look like this:
+ /*
+ Matrix4f matBasisChange;
+ matBasisChange.SetIdentity();
+ matBasisChange.M[0][0] = 1.0f;
+ matBasisChange.M[1][1] = -1.0f;
+ matBasisChange.M[2][2] = -1.0f;
+ Matrix4f matBasisChangeInv = matBasisChange.Inverted();
+ matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange;
+ */
+ // ...but of course all the above is a constant transform and much more easily done.
+ // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column,
+ // and of course most of the flips cancel:
+ // +++ +-- +--
+ // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++
+ // +++ +-- -++
+ matRenderFromNowStart.M[0][1] = -matRenderFromNowStart.M[0][1];
+ matRenderFromNowStart.M[0][2] = -matRenderFromNowStart.M[0][2];
+ matRenderFromNowStart.M[1][0] = -matRenderFromNowStart.M[1][0];
+ matRenderFromNowStart.M[2][0] = -matRenderFromNowStart.M[2][0];
+ matRenderFromNowStart.M[1][3] = -matRenderFromNowStart.M[1][3];
+ matRenderFromNowStart.M[2][3] = -matRenderFromNowStart.M[2][3];
+
+ return matRenderFromNowStart;
+}
+
+Matrix4f TimewarpComputePoseDeltaPosition ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust )
+{
+ Matrix4f worldFromPredictedView = (eyeViewAdjust * predictedViewFromWorld).InvertedHomogeneousTransform();
+ Matrix4f matRenderXform = (eyeViewAdjust * renderedViewFromWorld) * worldFromPredictedView;
+
+ return matRenderXform.Inverted();
+}
+
+TimewarpMachine::TimewarpMachine()
+{
+ for ( int i = 0; i < 2; i++ )
+ {
+ EyeRenderPoses[i] = Transformf();
+ }
+ DistortionTimeCount = 0;
+ VsyncEnabled = false;
+}
+
+void TimewarpMachine::Reset(HmdRenderInfo& renderInfo, bool vsyncEnabled, double timeNow)
+{
+ RenderInfo = renderInfo;
+ VsyncEnabled = vsyncEnabled;
+ CurrentPredictionValues = PredictionGetDeviceValues ( renderInfo, true, VsyncEnabled );
+ PresentFlushToPresentFlushSeconds = 0.0f;
+ DistortionTimeCount = 0;
+ DistortionTimeAverage = 0.0f;
+ LastFramePresentFlushTime = timeNow;
+ AfterPresentAndFlush(timeNow);
+}
+
+void TimewarpMachine::AfterPresentAndFlush(double timeNow)
+{
+ PresentFlushToPresentFlushSeconds = (float)(timeNow - LastFramePresentFlushTime);
+ LastFramePresentFlushTime = timeNow;
+ NextFramePresentFlushTime = timeNow + (double)PresentFlushToPresentFlushSeconds;
+}
+
+double TimewarpMachine::GetViewRenderPredictionTime()
+{
+ // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us.
+ return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToRenderedScene;
+}
+
+Transformf TimewarpMachine::GetViewRenderPredictionPose(SensorFusion &sfusion)
+{
+ double predictionTime = GetViewRenderPredictionTime();
+ return sfusion.GetPoseAtTime(predictionTime);
+}
+
+double TimewarpMachine::GetVisiblePixelTimeStart()
+{
+ // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us.
+ return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToTimewarpStart;
+}
+double TimewarpMachine::GetVisiblePixelTimeEnd()
+{
+ // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us.
+ return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToTimewarpEnd;
+}
+Transformf TimewarpMachine::GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion)
+{
+ double predictionTime = GetVisiblePixelTimeStart();
+ return sfusion.GetPoseAtTime(predictionTime);
+}
+Transformf TimewarpMachine::GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion)
+{
+ double predictionTime = GetVisiblePixelTimeEnd();
+ return sfusion.GetPoseAtTime(predictionTime);
+}
+Matrix4f TimewarpMachine::GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose)
+{
+ Transformf visiblePose = GetPredictedVisiblePixelPoseStart ( sfusion );
+ Matrix4f visibleMatrix(visiblePose);
+ Matrix4f renderedMatrix(renderedPose);
+ Matrix4f identity; // doesn't matter for orientation-only timewarp
+ return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity );
+}
+Matrix4f TimewarpMachine::GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose)
+{
+ Transformf visiblePose = GetPredictedVisiblePixelPoseEnd ( sfusion );
+ Matrix4f visibleMatrix(visiblePose);
+ Matrix4f renderedMatrix(renderedPose);
+ Matrix4f identity; // doesn't matter for orientation-only timewarp
+ return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity );
+}
+
+
+// What time should the app wait until before starting distortion?
+double TimewarpMachine::JustInTime_GetDistortionWaitUntilTime()
+{
+ if ( !VsyncEnabled || ( DistortionTimeCount < NumDistortionTimes ) )
+ {
+ // Don't wait.
+ return LastFramePresentFlushTime;
+ }
+
+ const float fudgeFactor = 0.002f; // Found heuristically - 1ms is too short because of timing granularity - may need further tweaking!
+ float howLongBeforePresent = DistortionTimeAverage + fudgeFactor;
+ // Subtlety here. Technically, the correct time is NextFramePresentFlushTime - howLongBeforePresent.
+ // However, if the app drops a frame, this then perpetuates it,
+ // i.e. if the display is running at 60fps, but the last frame was slow,
+ // (e.g. because of swapping or whatever), then NextFramePresentFlushTime is
+ // 33ms in the future, not 16ms. Since this function supplies the
+ // time to wait until, the app will indeed wait until 32ms, so the framerate
+ // drops to 30fps and never comes back up!
+ // So we return the *ideal* framerate, not the *actual* framerate.
+ return LastFramePresentFlushTime + (float)( CurrentPredictionValues.PresentFlushToPresentFlush - howLongBeforePresent );
+}
+
+
+bool TimewarpMachine::JustInTime_NeedDistortionTimeMeasurement() const
+{
+ if (!VsyncEnabled)
+ {
+ return false;
+ }
+ return ( DistortionTimeCount < NumDistortionTimes );
+}
+
+void TimewarpMachine::JustInTime_BeforeDistortionTimeMeasurement(double timeNow)
+{
+ DistortionTimeCurrentStart = timeNow;
+}
+
+void TimewarpMachine::JustInTime_AfterDistortionTimeMeasurement(double timeNow)
+{
+ float timeDelta = (float)( timeNow - DistortionTimeCurrentStart );
+ if ( DistortionTimeCount < NumDistortionTimes )
+ {
+ DistortionTimes[DistortionTimeCount] = timeDelta;
+ DistortionTimeCount++;
+ if ( DistortionTimeCount == NumDistortionTimes )
+ {
+ // Median.
+ float distortionTimeMedian = 0.0f;
+ for ( int i = 0; i < NumDistortionTimes/2; i++ )
+ {
+ // Find the maximum time of those remaining.
+ float maxTime = DistortionTimes[0];
+ int maxIndex = 0;
+ for ( int j = 1; j < NumDistortionTimes; j++ )
+ {
+ if ( maxTime < DistortionTimes[j] )
+ {
+ maxTime = DistortionTimes[j];
+ maxIndex = j;
+ }
+ }
+ // Zero that max time, so we'll find the next-highest time.
+ DistortionTimes[maxIndex] = 0.0f;
+ distortionTimeMedian = maxTime;
+ }
+ DistortionTimeAverage = distortionTimeMedian;
+ }
+ }
+ else
+ {
+ OVR_ASSERT ( !"Really didn't need more measurements, thanks" );
+ }
+}
+
+
+}}} // OVR::Util::Render
+
diff --git a/LibOVR/Src/Util/Util_Render_Stereo.h b/LibOVR/Src/Util/Util_Render_Stereo.h
new file mode 100644
index 0000000..326059e
--- /dev/null
+++ b/LibOVR/Src/Util/Util_Render_Stereo.h
@@ -0,0 +1,498 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : Util_Render_Stereo.h
+Content : Sample stereo rendering configuration classes.
+Created : October 22, 2012
+Authors : Michael Antonov, Tom Forsyth
+
+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.
+
+*************************************************************************************/
+
+#ifndef OVR_Util_Render_Stereo_h
+#define OVR_Util_Render_Stereo_h
+
+#include "../OVR_Stereo.h"
+
+
+namespace OVR {
+
+class SensorFusion;
+
+namespace Util { namespace Render {
+
+
+
+//-----------------------------------------------------------------------------------
+// **** Useful debug functions.
+//
+// Purely for debugging - the results are not very end-user-friendly.
+char const* GetDebugNameEyeCupType ( EyeCupType eyeCupType );
+char const* GetDebugNameHmdType ( HmdTypeEnum hmdType );
+
+
+
+//-----------------------------------------------------------------------------------
+// **** Higher-level utility functions.
+
+Sizei CalculateRecommendedTextureSize ( HmdRenderInfo const &hmd,
+ bool bRendertargetSharedByBothEyes,
+ float pixelDensityInCenter = 1.0f );
+
+FovPort CalculateRecommendedFov ( HmdRenderInfo const &hmd,
+ StereoEye eyeType,
+ bool bMakeFovSymmetrical = false);
+
+StereoEyeParams CalculateStereoEyeParams ( HmdRenderInfo const &hmd,
+ StereoEye eyeType,
+ Sizei const &actualRendertargetSurfaceSize,
+ bool bRendertargetSharedByBothEyes,
+ bool bRightHanded = true,
+ float zNear = 0.01f, float zFar = 10000.0f,
+ Sizei const *pOverrideRenderedPixelSize = NULL,
+ FovPort const *pOverrideFovport = NULL,
+ float zoomFactor = 1.0f );
+
+Vector3f CalculateEyeVirtualCameraOffset(HmdRenderInfo const &hmd,
+ StereoEye eyeType, bool bMonoRenderingMode );
+
+
+// These are two components from StereoEyeParams that can be changed
+// very easily without full recomputation of everything.
+struct ViewportScaleAndOffset
+{
+ Recti RenderedViewport;
+ ScaleAndOffset2D EyeToSourceUV;
+};
+
+// Three ways to override the size of the render view dynamically.
+// None of these require changing the distortion parameters or the regenerating the distortion mesh,
+// and can be called every frame if desired.
+ViewportScaleAndOffset ModifyRenderViewport ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ Recti const &renderViewport );
+
+ViewportScaleAndOffset ModifyRenderSize ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ Sizei const &requestedRenderSize,
+ bool bRendertargetSharedByBothEyes = false );
+
+ViewportScaleAndOffset ModifyRenderDensity ( StereoEyeParams const &params,
+ Sizei const &actualRendertargetSurfaceSize,
+ float pixelDensity = 1.0f,
+ bool bRendertargetSharedByBothEyes = false );
+
+
+//-----------------------------------------------------------------------------------
+// ***** StereoConfig
+
+// StereoConfig maintains a scene stereo state and allow switching between different
+// stereo rendering modes. To support rendering, StereoConfig keeps track of HMD
+// variables such as screen size, eye-to-screen distance and distortion, and computes
+// extra data such as FOV and distortion center offsets based on it. Rendering
+// parameters are returned though StereoEyeParams for each eye.
+//
+// Beyond regular 3D projection, this class supports rendering a 2D orthographic
+// surface for UI and text. The 2D surface will be defined by CreateOrthoSubProjection().
+// The (0,0) coordinate corresponds to eye center location.
+//
+// Applications are not required to use this class, but they should be doing very
+// similar sequences of operations, and it may be useful to start with this class
+// and modify it.
+
+struct StereoEyeParamsWithOrtho
+{
+ StereoEyeParams StereoEye;
+ Matrix4f OrthoProjection;
+};
+
+struct ViewportScaleAndOffsetBothEyes
+{
+ ViewportScaleAndOffset Left;
+ ViewportScaleAndOffset Right;
+};
+
+class StereoConfig
+{
+public:
+
+ // StereoMode describes rendering modes that can be used by StereoConfig.
+ // These modes control whether stereo rendering is used or not (Stereo_None),
+ // and how it is implemented.
+ enum StereoMode
+ {
+ Stereo_None = 0, // Single eye
+ Stereo_LeftRight_Multipass = 1, // One frustum per eye
+ };
+
+
+ StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass);
+
+ //---------------------------------------------------------------------------------------------
+ // *** Core functions - every app MUST call these functions at least once.
+
+ // Sets HMD parameters; also initializes distortion coefficients.
+ void SetHmdRenderInfo(const HmdRenderInfo& hmd);
+
+ // Set the physical size of the rendertarget surface the app created,
+ // and whether one RT is shared by both eyes, or each eye has its own RT:
+ // true: both eyes are rendered to the same RT. Left eye starts at top-left, right eye starts at top-middle.
+ // false: each eye is rendered to its own RT. Some GPU architectures prefer this arrangement.
+ // Typically, the app would call CalculateRecommendedTextureSize() to suggest the choice of RT size.
+ // This setting must be exactly the size of the actual RT created, or the UVs produced will be incorrect.
+ // If the app wants to render to a subsection of the RT, it should use SetRenderSize()
+ void SetRendertargetSize (Size<int> const rendertargetSize,
+ bool rendertargetIsSharedByBothEyes );
+
+ // Returns full set of Stereo rendering parameters for the specified eye.
+ const StereoEyeParamsWithOrtho& GetEyeRenderParams(StereoEye eye);
+
+
+
+ //---------------------------------------------------------------------------------------------
+ // *** Optional functions - an app may call these to override default behaviours.
+
+ const HmdRenderInfo& GetHmdRenderInfo() const { return Hmd; }
+
+ // Returns the recommended size of rendertargets.
+ // If rendertargetIsSharedByBothEyes is true, this is the size of the combined buffer.
+ // If rendertargetIsSharedByBothEyes is false, this is the size of each individual buffer.
+ // pixelDensityInCenter may be set to any number - by default it will match the HMD resolution in the center of the image.
+ // After creating the rendertargets, the application MUST call SetRendertargetSize() with the actual size created
+ // (which can be larger or smaller as the app wishes, but StereoConfig needs to know either way)
+ Sizei CalculateRecommendedTextureSize ( bool rendertargetSharedByBothEyes,
+ float pixelDensityInCenter = 1.0f );
+
+ // Sets a stereo rendering mode and updates internal cached
+ // state (matrices, per-eye view) based on it.
+ void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; }
+ StereoMode GetStereoMode() const { return Mode; }
+
+ // Sets the fieldOfView that the 2D coordinate area stretches to.
+ void Set2DAreaFov(float fovRadians);
+
+ // Really only for science experiments - no normal app should ever need to override
+ // the HMD's lens descriptors. Passing NULL removes the override.
+ // Supply both = set left and right.
+ // Supply just left = set both to the same.
+ // Supply neither = remove override.
+ void SetLensOverride ( LensConfig const *pLensOverrideLeft = NULL,
+ LensConfig const *pLensOverrideRight = NULL );
+
+ // Override the rendered FOV in various ways. All angles in tangent units.
+ // This is not clamped to the physical FOV of the display - you'll need to do that yourself!
+ // Supply both = set left and right.
+ // Supply just left = set both to the same.
+ // Supply neither = remove override.
+ void SetFov ( FovPort const *pfovLeft = NULL,
+ FovPort const *pfovRight = NULL );
+
+ void SetFovPortRadians ( float horizontal, float vertical )
+ {
+ FovPort fov = FovPort::CreateFromRadians(horizontal, vertical);
+ SetFov( &fov, &fov );
+ }
+
+
+ // This forces a "zero IPD" mode where there is just a single render with an FOV that
+ // is the union of the two calculated FOVs.
+ // The calculated render is for the left eye. Any size & FOV overrides for the right
+ // eye will be ignored.
+ // If you query the right eye's size, you will get the same render
+ // size & position as the left eye - you should not actually do the render of course!
+ // The distortion values will be different, because it goes to a different place on the framebuffer.
+ // Note that if you do this, the rendertarget does not need to be twice the width of
+ // the render size any more.
+ void SetZeroVirtualIpdOverride ( bool enableOverride );
+
+ // Allows the app to specify near and far clip planes and the right/left-handedness of the projection matrix.
+ void SetZClipPlanesAndHandedness ( float zNear = 0.01f, float zFar = 10000.0f,
+ bool rightHandedProjection = true );
+
+ // Allows the app to specify how much extra eye rotation to allow when determining the visible FOV.
+ void SetExtraEyeRotation ( float extraEyeRotationInRadians = 0.0f );
+
+ // The dirty flag is set by any of the above calls. Just handy for the app to know
+ // if e.g. the distortion mesh needs regeneration.
+ void SetDirty() { DirtyFlag = true; }
+ bool IsDirty() { return DirtyFlag; }
+
+ // An app never needs to call this - GetEyeRenderParams will call it internally if
+ // the state is dirty. However apps can call this explicitly to control when and where
+ // computation is performed (e.g. not inside critical loops)
+ void UpdateComputedState();
+
+ // This returns the projection matrix with a "zoom". Does not modify any internal state.
+ Matrix4f GetProjectionWithZoom ( StereoEye eye, float fovZoom ) const;
+
+
+ //---------------------------------------------------------------------------------------------
+ // The SetRender* functions are special.
+ //
+ // They do not require a full recalculation of state, and they do not change anything but the
+ // ViewportScaleAndOffset data for the eyes (which they return), and do not set the dirty flag!
+ // This means they can be called without regenerating the distortion mesh, and thus
+ // can happily be called every frame without causing performance problems. Dynamic rescaling
+ // of the rendertarget can help keep framerate up in demanding VR applications.
+ // See the documentation for more details on their use.
+
+ // Specify a pixel density - how many rendered pixels per pixel in the physical display.
+ ViewportScaleAndOffsetBothEyes SetRenderDensity ( float pixelsPerDisplayPixel );
+
+ // Supply the size directly. Will be clamped to the physical rendertarget size.
+ ViewportScaleAndOffsetBothEyes SetRenderSize ( Sizei const &renderSizeLeft, Sizei const &renderSizeRight );
+
+ // Supply the viewport directly. This is not clamped to the physical rendertarget - careful now!
+ ViewportScaleAndOffsetBothEyes SetRenderViewport ( Recti const &renderViewportLeft, Recti const &renderViewportRight );
+
+private:
+
+ // *** Modifiable State
+
+ StereoMode Mode;
+ HmdRenderInfo Hmd;
+
+ float Area2DFov; // FOV range mapping to the 2D area.
+
+ // Only one of these three overrides can be true!
+ enum SetViewportModeEnum
+ {
+ SVPM_Density,
+ SVPM_Size,
+ SVPM_Viewport,
+ } SetViewportMode;
+ // ...and depending which it is, one of the following are used.
+ float SetViewportPixelsPerDisplayPixel;
+ Sizei SetViewportSize[2];
+ Recti SetViewport[2];
+
+ // Other overrides.
+ bool OverrideLens;
+ LensConfig LensOverrideLeft;
+ LensConfig LensOverrideRight;
+ Sizei RendertargetSize;
+ bool OverrideTanHalfFov;
+ FovPort FovOverrideLeft;
+ FovPort FovOverrideRight;
+ bool OverrideZeroIpd;
+ float ZNear;
+ float ZFar;
+ float ExtraEyeRotationInRadians;
+ bool IsRendertargetSharedByBothEyes;
+ bool RightHandedProjection;
+
+ bool DirtyFlag; // Set when any if the modifiable state changed. Does NOT get set by SetRender*()
+
+ // Utility function.
+ ViewportScaleAndOffsetBothEyes setupViewportScaleAndOffsets();
+
+ // *** Computed State
+
+public: // Small hack for the config tool. Normal code should never read EyeRenderParams directly - use GetEyeRenderParams() instead.
+ // 0/1 = left/right main views.
+ StereoEyeParamsWithOrtho EyeRenderParams[2];
+};
+
+
+//-----------------------------------------------------------------------------------
+// ***** Distortion Mesh Rendering
+//
+
+// Stores both texture UV coords, or tan(angle) values.
+// Use whichever set of data the specific distortion algorithm requires.
+// This struct *must* be binary compatible with CAPI ovrDistortionVertex.
+struct DistortionMeshVertexData
+{
+ // [-1,+1],[-1,+1] over the entire framebuffer.
+ Vector2f ScreenPosNDC;
+ // [0.0-1.0] interpolation value for timewarping - see documentation for details.
+ float TimewarpLerp;
+ // [0.0-1.0] fade-to-black at the edges to reduce peripheral vision noise.
+ float Shade;
+ // The red, green, and blue vectors in tan(angle) space.
+ // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale
+ // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords.
+ Vector2f TanEyeAnglesR;
+ Vector2f TanEyeAnglesG;
+ Vector2f TanEyeAnglesB;
+};
+
+
+void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo );
+
+// Generate distortion mesh for a eye. This version requires less data then stereoParms, supporting
+// dynamic change in render target viewport.
+void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ bool rightEye,
+ const HmdRenderInfo &hmdRenderInfo,
+ const DistortionRenderDesc &distortion, const ScaleAndOffset2D &eyeToSourceNDC );
+
+void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices );
+
+
+//-----------------------------------------------------------------------------------
+// ***** Heightmap Mesh Rendering
+//
+
+// Stores both texture UV coords, or tan(angle) values.
+// This struct *must* be binary compatible with CAPI ovrHeightmapVertex.
+struct HeightmapMeshVertexData
+{
+ // [-1,+1],[-1,+1] over the entire framebuffer.
+ Vector2f ScreenPosNDC;
+ // [0.0-1.0] interpolation value for timewarping - see documentation for details.
+ float TimewarpLerp;
+ // The vectors in tan(angle) space.
+ // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale
+ // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords.
+ Vector2f TanEyeAngles;
+};
+
+
+void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles,
+ const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo );
+
+// Generate heightmap mesh for a eye. This version requires less data then stereoParms, supporting
+// dynamic change in render target viewport.
+void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices,
+ int *pNumVertices, int *pNumTriangles, bool rightEye,
+ const HmdRenderInfo &hmdRenderInfo, const ScaleAndOffset2D &eyeToSourceNDC );
+
+void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices );
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** Prediction and timewarp.
+//
+
+struct PredictionValues
+{
+ // All values in seconds.
+ // These are the times in seconds from a present+flush to the relevant display element.
+ // The time is measured to the middle of that element's visibility window,
+ // e.g. if the device is a full-persistence display, the element will be visible for
+ // an entire frame, so the time measures to the middle of that period, i.e. half the frame time.
+ float PresentFlushToRenderedScene; // To the overall rendered 3D scene being visible.
+ float PresentFlushToTimewarpStart; // To when the first timewarped scanline will be visible.
+ float PresentFlushToTimewarpEnd; // To when the last timewarped scanline will be visible.
+ float PresentFlushToPresentFlush; // To the next present+flush, i.e. the ideal framerate.
+
+ bool WithTimewarp;
+ bool WithVsync;
+};
+
+// Calculates the values from the HMD info.
+PredictionValues PredictionGetDeviceValues ( const HmdRenderInfo &hmdRenderInfo,
+ bool withTimewarp = true,
+ bool withVsync = true );
+
+// Pass in an orientation used to render the scene, and then the predicted orientation
+// (which may have been computed later on, and thus is more accurate), and this
+// will return the matrix to pass to the timewarp distortion shader.
+// TODO: deal with different handedness?
+Matrix4f TimewarpComputePoseDelta ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust );
+Matrix4f TimewarpComputePoseDeltaPosition ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust );
+
+
+
+// TimewarpMachine helps keep track of rendered frame timing and
+// handles predictions for time-warp rendering.
+class TimewarpMachine
+{
+public:
+ TimewarpMachine();
+
+ // Call this on and every time something about the setup changes.
+ void Reset ( HmdRenderInfo& renderInfo, bool vsyncEnabled, double timeNow );
+
+ // The only reliable time in most engines is directly after the frame-present and GPU flush-and-wait.
+ // This call should be done right after that to give this system the timing info it needs.
+ void AfterPresentAndFlush(double timeNow);
+
+ // The "average" time the rendered frame will show up,
+ // and the predicted pose of the HMD at that time.
+ // You usually only need to call one of these functions.
+ double GetViewRenderPredictionTime();
+ Transformf GetViewRenderPredictionPose(SensorFusion &sfusion);
+
+
+ // Timewarp prediction functions. You usually only need to call one of these three sets of functions.
+
+ // The predicted times that the first and last pixel will be visible on-screen.
+ double GetVisiblePixelTimeStart();
+ double GetVisiblePixelTimeEnd();
+ // Predicted poses of the HMD at those first and last pixels.
+ Transformf GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion);
+ Transformf GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion);
+ // The delta matrices to feed to the timewarp distortion code,
+ // given the pose that was used for rendering.
+ // (usually the one returned by GetViewRenderPredictionPose() earlier)
+ Matrix4f GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose);
+ Matrix4f GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose);
+
+
+ // Just-In-Time distortion aims to delay the second sensor reading & distortion
+ // until the very last moment to improve prediction. However, it is a little scary,
+ // since the delay might wait too long and miss the vsync completely!
+ // Use of the JustInTime_* functions is entirely optional, and we advise allowing
+ // users to turn it off in their video options to cope with odd machine configurations.
+
+ // What time should the app wait until before starting distortion?
+ double JustInTime_GetDistortionWaitUntilTime();
+
+ // Used to time the distortion rendering
+ bool JustInTime_NeedDistortionTimeMeasurement() const;
+ void JustInTime_BeforeDistortionTimeMeasurement(double timeNow);
+ void JustInTime_AfterDistortionTimeMeasurement(double timeNow);
+
+
+private:
+
+ bool VsyncEnabled;
+ HmdRenderInfo RenderInfo;
+ PredictionValues CurrentPredictionValues;
+
+ enum { NumDistortionTimes = 10 };
+ int DistortionTimeCount;
+ double DistortionTimeCurrentStart;
+ float DistortionTimes[NumDistortionTimes];
+ float DistortionTimeAverage;
+
+ // Pose at which last time the eye was rendered.
+ Transformf EyeRenderPoses[2];
+
+ // Absolute time of the last present+flush
+ double LastFramePresentFlushTime;
+ // Seconds between presentflushes
+ float PresentFlushToPresentFlushSeconds;
+ // Predicted absolute time of the next present+flush
+ double NextFramePresentFlushTime;
+
+};
+
+
+
+}}} // OVR::Util::Render
+
+#endif