aboutsummaryrefslogtreecommitdiffstats
path: root/Samples/OculusWorldDemo/OculusWorldDemo.cpp
diff options
context:
space:
mode:
authorBrad Davis <[email protected]>2013-07-03 09:16:03 -0700
committerBrad Davis <[email protected]>2013-07-03 09:16:03 -0700
commitd46694c91c2bec4eb1e282c0c0101e6dab26e082 (patch)
treeeb5fba71edf1aedc0d6af9406881004289433b20 /Samples/OculusWorldDemo/OculusWorldDemo.cpp
parent7fa8be4bc565adc9911c95c814480cc48bf2d13c (diff)
SDK 0.2.3
Diffstat (limited to 'Samples/OculusWorldDemo/OculusWorldDemo.cpp')
-rw-r--r--Samples/OculusWorldDemo/OculusWorldDemo.cpp3758
1 files changed, 1922 insertions, 1836 deletions
diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.cpp b/Samples/OculusWorldDemo/OculusWorldDemo.cpp
index 18e614c..be2e6b2 100644
--- a/Samples/OculusWorldDemo/OculusWorldDemo.cpp
+++ b/Samples/OculusWorldDemo/OculusWorldDemo.cpp
@@ -1,1836 +1,1922 @@
-/************************************************************************************
-
-Filename : OculusWorldDemo.cpp
-Content : First-person view test application for Oculus Rift
-Created : October 4, 2012
-Authors : Michael Antonov, Andrew Reisse, Steve LaValle
- Peter Hoff, Dan Goodman, Bryan Croteau
-
-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 "OVR.h"
-
-#include "Player.h"
-
-#include "../CommonSrc/Platform/Platform_Default.h"
-#include "../CommonSrc/Render/Render_Device.h"
-#include "../CommonSrc/Render/Render_XMLSceneLoader.h"
-#include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h"
-#include "../CommonSrc/Platform/Gamepad.h"
-
-#include <Kernel/OVR_SysFile.h>
-#include <Kernel/OVR_Log.h>
-#include <Kernel/OVR_Timer.h>
-
-// Filename to be loaded by default, searching specified paths.
-#define WORLDDEMO_ASSET_FILE "Tuscany.xml"
-#define WORLDDEMO_ASSET_PATH1 "Assets/Tuscany/"
-#define WORLDDEMO_ASSET_PATH2 "../Assets/Tuscany/"
-// This path allows the shortcut to work.
-#define WORLDDEMO_ASSET_PATH3 "Samples/OculusWorldDemo/Assets/Tuscany/"
-
-
-using namespace OVR;
-using namespace OVR::Platform;
-using namespace OVR::Render;
-
-//-------------------------------------------------------------------------------------
-// ***** OculusWorldDemo Description
-
-// This app renders a simple flat-shaded room allowing the user to move along the
-// floor and look around with an HMD, mouse and keyboard. The following keys work:
-//
-// 'W', 'S', 'A', 'D' and Arrow Keys - Move forward, back; strafe left/right.
-// F1 - No stereo, no distortion.
-// F2 - Stereo, no distortion.
-// F3 - Stereo and distortion.
-// F4 - Toggle MSAA.
-// F9 - Cycle through fullscreen and windowed modes. Necessary for previewing content with Rift.
-//
-// Important Oculus-specific logic can be found at following locations:
-//
-// OculusWorldDemoApp::OnStartup - This function will initialize OVR::DeviceManager and HMD,
-// creating SensorDevice and attaching it to SensorFusion.
-// This needs to be done before obtaining sensor data.
-//
-// OculusWorldDemoApp::OnIdle - Here we poll SensorFusion for orientation, apply it
-// to the scene and handle movement.
-// Stereo rendering is also done here, by delegating to
-// to Render function for each eye.
-//
-
-//-------------------------------------------------------------------------------------
-// ***** OculusWorldDemo Application class
-
-// An instance of this class is created on application startup (main/WinMain).
-// It then works as follows:
-// - Graphics and HMD setup is done OculusWorldDemoApp::OnStartup(). This function
-// also creates the room model from Slab declarations.
-// - Per-frame processing is done in OnIdle(). This function processes
-// sensor and movement input and then renders the frame.
-// - Additional input processing is done in OnMouse, OnKey.
-
-class OculusWorldDemoApp : public Application, public MessageHandler
-{
-public:
- OculusWorldDemoApp();
- ~OculusWorldDemoApp();
-
- virtual int OnStartup(int argc, const char** argv);
- virtual void OnIdle();
-
- virtual void OnMouseMove(int x, int y, int modifiers);
- virtual void OnKey(KeyCode key, int chr, bool down, int modifiers);
- virtual void OnResize(int width, int height);
-
- virtual void OnMessage(const Message& msg);
-
- void Render(const StereoEyeParams& stereo);
-
- // Sets temporarily displayed message for adjustments
- void SetAdjustMessage(const char* format, ...);
- // Overrides current timeout, in seconds (not the future default value);
- // intended to be called right after SetAdjustMessage.
- void SetAdjustMessageTimeout(float timeout);
-
- // Stereo setting adjustment functions.
- // Called with deltaTime when relevant key is held.
- void AdjustFov(float dt);
- void AdjustAspect(float dt);
- void AdjustIPD(float dt);
- void AdjustEyeHeight(float dt);
-
- void AdjustMotionPrediction(float dt);
-
- void AdjustDistortion(float dt, int kIndex, const char* label);
- void AdjustDistortionK0(float dt) { AdjustDistortion(dt, 0, "K0"); }
- void AdjustDistortionK1(float dt) { AdjustDistortion(dt, 1, "K1"); }
- void AdjustDistortionK2(float dt) { AdjustDistortion(dt, 2, "K2"); }
- void AdjustDistortionK3(float dt) { AdjustDistortion(dt, 3, "K3"); }
-
- // Adds room model to scene.
- void PopulateScene(const char* fileName);
- void PopulatePreloadScene();
- void ClearScene();
-
- // Magnetometer calibration procedure
- void UpdateManualMagCalibration();
-
-protected:
- RenderDevice* pRender;
- RendererParams RenderParams;
- int Width, Height;
- int Screen;
- int FirstScreenInCycle;
-
- // Magnetometer calibration and yaw correction
- Util::MagCalibration MagCal;
- bool MagAwaitingForwardLook;
-
- // *** Oculus HMD Variables
- Ptr<DeviceManager> pManager;
- Ptr<SensorDevice> pSensor;
- Ptr<HMDDevice> pHMD;
- SensorFusion SFusion;
- HMDInfo HMDInfo;
-
- Ptr<LatencyTestDevice> pLatencyTester;
- Util::LatencyTest LatencyUtil;
-
- double LastUpdate;
- int FPS;
- int FrameCounter;
- double NextFPSUpdate;
-
- Array<Ptr<CollisionModel> > CollisionModels;
- Array<Ptr<CollisionModel> > GroundCollisionModels;
-
- // Loading process displays screenshot in first frame
- // and then proceeds to load until finished.
- enum LoadingStateType
- {
- LoadingState_Frame0,
- LoadingState_DoLoad,
- LoadingState_Finished
- };
-
- // Player
- Player Player;
- Matrix4f View;
- Scene MainScene;
- Scene LoadingScene;
- Scene GridScene;
- Scene YawMarkGreenScene;
- Scene YawMarkRedScene;
- Scene YawLinesScene;
-
- LoadingStateType LoadingState;
-
- Ptr<ShaderFill> LitSolid, LitTextures[4];
-
- // Stereo view parameters.
- StereoConfig SConfig;
- PostProcessType PostProcess;
-
- // LOD
- String MainFilePath;
- Array<String> LODFilePaths;
- int ConsecutiveLowFPSFrames;
- int CurrentLODFileIndex;
-
- float DistortionK0;
- float DistortionK1;
- float DistortionK2;
- float DistortionK3;
-
- String AdjustMessage;
- double AdjustMessageTimeout;
-
- // Saved distortion state.
- float SavedK0, SavedK1, SavedK2, SavedK3;
- float SavedESD, SavedAspect, SavedEyeDistance;
-
- // Allows toggling color around distortion.
- Color DistortionClearColor;
-
- // Stereo settings adjustment state.
- typedef void (OculusWorldDemoApp::*AdjustFuncType)(float);
- bool ShiftDown;
- AdjustFuncType pAdjustFunc;
- float AdjustDirection;
-
- enum SceneRenderMode
- {
- Scene_World,
- Scene_Grid,
- Scene_Both,
- Scene_YawView
- };
- SceneRenderMode SceneMode;
-
-
- enum TextScreen
- {
- Text_None,
- Text_Orientation,
- Text_Config,
- Text_Help,
- Text_Count
- };
- TextScreen TextScreen;
-
- struct DeviceStatusNotificationDesc
- {
- DeviceHandle Handle;
- MessageType Action;
-
- DeviceStatusNotificationDesc():Action(Message_None) {}
- DeviceStatusNotificationDesc(MessageType mt, const DeviceHandle& dev)
- : Handle(dev), Action(mt) {}
- };
- Array<DeviceStatusNotificationDesc> DeviceStatusNotificationsQueue;
-
-
- Model* CreateModel(Vector3f pos, struct SlabModel* sm);
- Model* CreateBoundingModel(CollisionModel &cm);
- void PopulateLODFileNames();
- void DropLOD();
- void RaiseLOD();
- void CycleDisplay();
- void GamepadStateChanged(const GamepadState& pad);
-
- // Variable used by UpdateManualCalibration
- float FirstMagYaw;
-};
-
-//-------------------------------------------------------------------------------------
-
-OculusWorldDemoApp::OculusWorldDemoApp()
- : pRender(0),
- LastUpdate(0),
- LoadingState(LoadingState_Frame0),
- // Initial location
- SConfig(),
- PostProcess(PostProcess_Distortion),
- DistortionClearColor(0, 0, 0),
-
- ShiftDown(false),
- pAdjustFunc(0),
- AdjustDirection(1.0f),
- SceneMode(Scene_World),
- TextScreen(Text_None)
-{
- Width = 1280;
- Height = 800;
- Screen = 0;
- FirstScreenInCycle = 0;
-
- FPS = 0;
- FrameCounter = 0;
- NextFPSUpdate = 0;
-
- ConsecutiveLowFPSFrames = 0;
- CurrentLODFileIndex = 0;
-
- AdjustMessageTimeout = 0;
-}
-
-OculusWorldDemoApp::~OculusWorldDemoApp()
-{
- RemoveHandlerFromDevices();
-
- if(DejaVu.fill)
- {
- DejaVu.fill->Release();
- }
- pLatencyTester.Clear();
- pSensor.Clear();
- pHMD.Clear();
-
- CollisionModels.ClearAndRelease();
- GroundCollisionModels.ClearAndRelease();
-}
-
-int OculusWorldDemoApp::OnStartup(int argc, const char** argv)
-{
-
- // *** Oculus HMD & Sensor Initialization
-
- // Create DeviceManager and first available HMDDevice from it.
- // Sensor object is created from the HMD, to ensure that it is on the
- // correct device.
-
- pManager = *DeviceManager::Create();
-
- // We'll handle it's messages in this case.
- pManager->SetMessageHandler(this);
-
- pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
- if(pHMD)
- {
- pSensor = *pHMD->GetSensor();
-
- // This will initialize HMDInfo with information about configured IPD,
- // screen size and other variables needed for correct projection.
- // We pass HMD DisplayDeviceName into the renderer to select the
- // correct monitor in full-screen mode.
- if(pHMD->GetDeviceInfo(&HMDInfo))
- {
- //RenderParams.MonitorName = hmd.DisplayDeviceName;
- SConfig.SetHMDInfo(HMDInfo);
- }
- }
- else
- {
- // If we didn't detect an HMD, try to create the sensor directly.
- // This is useful for debugging sensor interaction; it is not needed in
- // a shipping app.
- pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
- }
-
- // Create the Latency Tester device and assign it to the LatencyTesterUtil object.
- pLatencyTester = *pManager->EnumerateDevices<LatencyTestDevice>().CreateDevice();
- if (pLatencyTester)
- {
- LatencyUtil.SetDevice(pLatencyTester);
- }
- // Make the user aware which devices are present.
- if(pHMD == NULL && pSensor == NULL)
- {
- SetAdjustMessage("---------------------------------\nNO HMD DETECTED\nNO SENSOR DETECTED\n---------------------------------");
- }
- else if(pHMD == NULL)
- {
- SetAdjustMessage("----------------------------\nNO HMD DETECTED\n----------------------------");
- }
- else if(pSensor == NULL)
- {
- SetAdjustMessage("---------------------------------\nNO SENSOR DETECTED\n---------------------------------");
- }
- else
- {
- SetAdjustMessage("--------------------------------------------\n"
- "Press F9 for Full-Screen on Rift\n"
- "--------------------------------------------");
- }
-
- // First message should be extra-long.
- SetAdjustMessageTimeout(10.0f);
-
-
- if(HMDInfo.HResolution > 0)
- {
- Width = HMDInfo.HResolution;
- Height = HMDInfo.VResolution;
- }
-
- if(!pPlatform->SetupWindow(Width, Height))
- {
- return 1;
- }
-
- String Title = "Oculus World Demo";
- if(HMDInfo.ProductName[0])
- {
- Title += " : ";
- Title += HMDInfo.ProductName;
- }
- pPlatform->SetWindowTitle(Title);
-
- // Report relative mouse motion in OnMouseMove
- pPlatform->SetMouseMode(Mouse_Relative);
-
- if(pSensor)
- {
- // We need to attach sensor to SensorFusion object for it to receive
- // body frame messages and update orientation. SFusion.GetOrientation()
- // is used in OnIdle() to orient the view.
- SFusion.AttachToSensor(pSensor);
-
- SFusion.SetDelegateMessageHandler(this);
-
- SFusion.SetPredictionEnabled(true);
- }
-
-
- // *** Initialize Rendering
-
- const char* graphics = "d3d11";
-
- // Select renderer based on command line arguments.
- for(int i = 1; i < argc; i++)
- {
- if(!strcmp(argv[i], "-r") && i < argc - 1)
- {
- graphics = argv[i + 1];
- }
- else if(!strcmp(argv[i], "-fs"))
- {
- RenderParams.Fullscreen = true;
- }
- }
-
- // Enable multi-sampling by default.
- RenderParams.Multisample = 4;
- pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET,
- graphics, RenderParams);
-
-
-
- // *** Configure Stereo settings.
-
- SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
- SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
-
- // Configure proper Distortion Fit.
- // For 7" screen, fit to touch left side of the view, leaving a bit of
- // invisible screen on the top (saves on rendering cost).
- // For smaller screens (5.5"), fit to the top.
- if (HMDInfo.HScreenSize > 0.0f)
- {
- if (HMDInfo.HScreenSize > 0.140f) // 7"
- SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
- else
- SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
- }
-
- pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
- //pRender->SetSceneRenderScale(0.8f);
-
- SConfig.Set2DAreaFov(DegreeToRad(85.0f));
-
-
- // *** Identify Scene File & Prepare for Loading
-
- // This creates lights and models.
- if (argc == 2)
- {
- MainFilePath = argv[1];
- PopulateLODFileNames();
- }
- else
- {
- fprintf(stderr, "Usage: OculusWorldDemo [input XML]\n");
- MainFilePath = WORLDDEMO_ASSET_FILE;
- }
-
- // Try to modify path for correctness in case specified file is not found.
- if (!SysFile(MainFilePath).IsValid())
- {
- String prefixPath1(pPlatform->GetContentDirectory() + "/" + WORLDDEMO_ASSET_PATH1),
- prefixPath2(WORLDDEMO_ASSET_PATH2),
- prefixPath3(WORLDDEMO_ASSET_PATH3);
- if (SysFile(prefixPath1 + MainFilePath).IsValid())
- MainFilePath = prefixPath1 + MainFilePath;
- else if (SysFile(prefixPath2 + MainFilePath).IsValid())
- MainFilePath = prefixPath2 + MainFilePath;
- else if (SysFile(prefixPath3 + MainFilePath).IsValid())
- MainFilePath = prefixPath3 + MainFilePath;
- }
-
- PopulatePreloadScene();
-
- LastUpdate = pPlatform->GetAppTime();
- //pPlatform->PlayMusicFile(L"Loop.wav");
-
- return 0;
-}
-
-void OculusWorldDemoApp::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);
-
- { // limit the scope of the lock
- Lock::Locker lock(pManager->GetHandlerLock());
- DeviceStatusNotificationsQueue.PushBack(
- DeviceStatusNotificationDesc(statusMsg.Type, statusMsg.Handle));
- }
-
- switch (statusMsg.Type)
- {
- case OVR::Message_DeviceAdded:
- LogText("DeviceManager reported device added.\n");
- break;
-
- case OVR::Message_DeviceRemoved:
- LogText("DeviceManager reported device removed.\n");
- break;
-
- default: OVR_ASSERT(0); // unexpected type
- }
- }
- }
-}
-
-void OculusWorldDemoApp::OnResize(int width, int height)
-{
- Width = width;
- Height = height;
- SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
-}
-
-void OculusWorldDemoApp::OnMouseMove(int x, int y, int modifiers)
-{
- if(modifiers & Mod_MouseRelative)
- {
- // Get Delta
- int dx = x, dy = y;
-
- const float maxPitch = ((3.1415f / 2) * 0.98f);
-
- // Apply to rotation. Subtract for right body frame rotation,
- // since yaw rotation is positive CCW when looking down on XZ plane.
- Player.EyeYaw -= (Sensitivity * dx) / 360.0f;
-
- if(!pSensor)
- {
- Player.EyePitch -= (Sensitivity * dy) / 360.0f;
-
- if(Player.EyePitch > maxPitch)
- {
- Player.EyePitch = maxPitch;
- }
- if(Player.EyePitch < -maxPitch)
- {
- Player.EyePitch = -maxPitch;
- }
- }
- }
-}
-
-
-void OculusWorldDemoApp::OnKey(KeyCode key, int chr, bool down, int modifiers)
-{
- OVR_UNUSED(chr);
-
- switch(key)
- {
- case Key_Q:
- if (down && (modifiers & Mod_Control))
- {
- pPlatform->Exit(0);
- }
- break;
-
- // Handle player movement keys.
- // We just update movement state here, while the actual translation is done in OnIdle()
- // based on time.
- case Key_W:
- Player.MoveForward = down ? (Player.MoveForward | 1) : (Player.MoveForward & ~1);
- break;
- case Key_S:
- Player.MoveBack = down ? (Player.MoveBack | 1) : (Player.MoveBack & ~1);
- break;
- case Key_A:
- Player.MoveLeft = down ? (Player.MoveLeft | 1) : (Player.MoveLeft & ~1);
- break;
- case Key_D:
- Player.MoveRight = down ? (Player.MoveRight | 1) : (Player.MoveRight & ~1);
- break;
- case Key_Up:
- Player.MoveForward = down ? (Player.MoveForward | 2) : (Player.MoveForward & ~2);
- break;
- case Key_Down:
- Player.MoveBack = down ? (Player.MoveBack | 2) : (Player.MoveBack & ~2);
- break;
- case Key_Left:
- Player.MoveLeft = down ? (Player.MoveLeft | 2) : (Player.MoveLeft & ~2);
- break;
- case Key_Right:
- Player.MoveRight = down ? (Player.MoveRight | 2) : (Player.MoveRight & ~2);
- break;
-
- case Key_Minus:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
- AdjustDirection = -1;
- break;
- case Key_Equal:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
- AdjustDirection = 1;
- break;
-
- case Key_B:
- if (down)
- {
- if(SConfig.GetDistortionScale() == 1.0f)
- {
- if(SConfig.GetHMDInfo().HScreenSize > 0.140f) // 7"
- {
- SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
- }
- else
- {
- SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
- }
- }
- else
- {
- // No fitting; scale == 1.0.
- SConfig.SetDistortionFitPointVP(0, 0);
- }
- }
- break;
-
- // Support toggling background color for distortion so that we can see
- // the effect on the periphery.
- case Key_V:
- if (down)
- {
- if(DistortionClearColor.B == 0)
- {
- DistortionClearColor = Color(0, 128, 255);
- }
- else
- {
- DistortionClearColor = Color(0, 0, 0);
- }
-
- pRender->SetDistortionClearColor(DistortionClearColor);
- }
- break;
-
-
- case Key_F1:
- SConfig.SetStereoMode(Stereo_None);
- PostProcess = PostProcess_None;
- SetAdjustMessage("StereoMode: None");
- break;
- case Key_F2:
- SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
- PostProcess = PostProcess_None;
- SetAdjustMessage("StereoMode: Stereo + No Distortion");
- break;
- case Key_F3:
- SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
- PostProcess = PostProcess_Distortion;
- SetAdjustMessage("StereoMode: Stereo + Distortion");
- break;
-
- case Key_R:
- SFusion.Reset();
- SetAdjustMessage("Sensor Fusion Reset");
- break;
-
- case Key_Space:
- if (!down)
- {
- TextScreen = (enum TextScreen)((TextScreen + 1) % Text_Count);
- }
- break;
-
- case Key_F4:
- if (!down)
- {
- RenderParams = pRender->GetParams();
- RenderParams.Multisample = RenderParams.Multisample > 1 ? 1 : 4;
- pRender->SetParams(RenderParams);
- if(RenderParams.Multisample > 1)
- {
- SetAdjustMessage("Multisampling On");
- }
- else
- {
- SetAdjustMessage("Multisampling Off");
- }
- }
- break;
- case Key_F9:
- if (!down)
- {
- CycleDisplay();
- }
- break;
-
-#ifdef OVR_OS_MAC
- case Key_F10: // F11 is reserved on Mac
-#else
- case Key_F11:
-#endif
- if (!down)
- {
- RenderParams = pRender->GetParams();
- RenderParams.Display = DisplayId(SConfig.GetHMDInfo().DisplayDeviceName,SConfig.GetHMDInfo().DisplayId);
- pRender->SetParams(RenderParams);
-
- pPlatform->SetMouseMode(Mouse_Normal);
- pPlatform->SetFullscreen(RenderParams, pRender->IsFullscreen() ? Display_Window : Display_FakeFullscreen);
- pPlatform->SetMouseMode(Mouse_Relative); // Avoid mode world rotation jump.
- // If using an HMD, enable post-process (for distortion) and stereo.
- if(RenderParams.IsDisplaySet() && pRender->IsFullscreen())
- {
- SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
- PostProcess = PostProcess_Distortion;
- }
- }
- break;
-
- case Key_Escape:
- if(!down)
- {
- // switch to primary screen windowed mode
- pPlatform->SetFullscreen(RenderParams, Display_Window);
- RenderParams.Display = pPlatform->GetDisplay(0);
- pRender->SetParams(RenderParams);
- Screen = 0;
- }
- break;
-
- // Stereo adjustments.
- case Key_BracketLeft:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
- AdjustDirection = 1;
- break;
- case Key_BracketRight:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
- AdjustDirection = -1;
- break;
-
- case Key_Insert:
- case Key_Num0:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
- AdjustDirection = 1;
- break;
- case Key_Delete:
- case Key_Num9:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
- AdjustDirection = -1;
- break;
-
- case Key_PageUp:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
- AdjustDirection = 1;
- break;
- case Key_PageDown:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
- AdjustDirection = -1;
- break;
-
- // Distortion correction adjustments
- case Key_H:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
- AdjustDirection = -1;
- break;
- case Key_Y:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
- AdjustDirection = 1;
- break;
- case Key_J:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
- AdjustDirection = -1;
- break;
- case Key_U:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
- AdjustDirection = 1;
- break;
- case Key_K:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
- AdjustDirection = -1;
- break;
- case Key_I:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
- AdjustDirection = 1;
- break;
- case Key_L:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
- AdjustDirection = -1;
- break;
- case Key_O:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
- AdjustDirection = 1;
- break;
-
-
- case Key_Tab:
- if (down)
- {
- float t0 = SConfig.GetDistortionK(0),
- t1 = SConfig.GetDistortionK(1),
- t2 = SConfig.GetDistortionK(2),
- t3 = SConfig.GetDistortionK(3);
- float tESD = SConfig.GetEyeToScreenDistance(),
- taspect = SConfig.GetAspectMultiplier(),
- tipd = SConfig.GetIPD();
-
- if(SavedK0 > 0.0f)
- {
- SConfig.SetDistortionK(0, SavedK0);
- SConfig.SetDistortionK(1, SavedK1);
- SConfig.SetDistortionK(2, SavedK2);
- SConfig.SetDistortionK(3, SavedK3);
- SConfig.SetEyeToScreenDistance(SavedESD);
- SConfig.SetAspectMultiplier(SavedAspect);
- SConfig.SetIPD(SavedEyeDistance);
-
- SetAdjustMessage("Restored:\n"
- "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
- "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
- "K1: \t120 %.4f\t350 K3: \t490 %.4f",
- SavedESD, SavedEyeDistance,
- SavedK0, SavedK2,
- SavedK1, SavedK3);
- }
- else
- {
- SetAdjustMessage("Setting Saved");
- }
-
- SavedK0 = t0;
- SavedK1 = t1;
- SavedK2 = t2;
- SavedK3 = t3;
- SavedESD = tESD;
- SavedAspect = taspect;
- SavedEyeDistance = tipd;
- }
- break;
-
- case Key_G:
- if (down)
- {
- if(SceneMode == Scene_World)
- {
- SceneMode = Scene_Grid;
- SetAdjustMessage("Grid Only");
- }
- else if(SceneMode == Scene_Grid)
- {
- SceneMode = Scene_Both;
- SetAdjustMessage("Grid Overlay");
- }
- else if(SceneMode == Scene_Both)
- {
- SceneMode = Scene_World;
- SetAdjustMessage("Grid Off");
- }
- }
- break;
-
- // Holding down Shift key accelerates adjustment velocity.
- case Key_Shift:
- ShiftDown = down;
- break;
-
- // Reset the camera position in case we get stuck
- case Key_T:
- Player.EyePos = Vector3f(10.0f, 1.6f, 10.0f);
- break;
-
- case Key_F5:
- if (!down)
- {
- UPInt numNodes = MainScene.Models.GetSize();
- for(UPInt i = 0; i < numNodes; i++)
- {
- Ptr<OVR::Render::Model> nodePtr = MainScene.Models[i];
- Render::Model* pNode = nodePtr.GetPtr();
- if(pNode->IsCollisionModel)
- {
- pNode->Visible = !pNode->Visible;
- }
- }
- }
- break;
-
- case Key_N:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
- AdjustDirection = -1;
- break;
-
- case Key_M:
- pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
- AdjustDirection = 1;
- break;
-
-/*
- case Key_N:
- RaiseLOD();
- break;
- case Key_M:
- DropLOD();
- break;
-*/
- // Start calibrating magnetometer
- case Key_Z:
- if (down)
- {
- if (MagCal.IsManuallyCalibrating())
- MagAwaitingForwardLook = false;
- else
- {
- MagCal.BeginManualCalibration(SFusion);
- MagAwaitingForwardLook = true;
- }
- }
- break;
-
- case Key_X:
- if (down)
- {
- MagCal.BeginAutoCalibration(SFusion);
- SetAdjustMessage("Starting Auto Mag Calibration");
- }
- break;
-
- // Set the magnetometer reference point
- case Key_Semicolon:
- if (down)
- {
- SFusion.SetMagReference();
- SetAdjustMessage("Magnetometer Reference Set");
- if (SFusion.IsMagReady())
- SFusion.SetYawCorrectionEnabled(true);
- }
- break;
-
- // Show view of yaw angles (for mag calibration/analysis)
- case Key_F6:
- if (down)
- {
- if (SceneMode != Scene_YawView)
- {
- SceneMode = Scene_YawView;
- SetAdjustMessage("Magnetometer Yaw Angle Marks");
- }
- else
- {
- SceneMode = Scene_World;
- SetAdjustMessage("Magnetometer Marks Off");
- }
- }
- break;
-
- case Key_C:
- if (down)
- {
- // Toggle chromatic aberration correction on/off.
- RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
-
- if (shader == RenderDevice::PostProcessShader_Distortion)
- {
- pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
- SetAdjustMessage("Chromatic Aberration Correction On");
- }
- else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
- {
- pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
- SetAdjustMessage("Chromatic Aberration Correction Off");
- }
- else
- OVR_ASSERT(false);
- }
- break;
-
- case Key_P:
- if (down)
- {
- // Toggle motion prediction.
- if (SFusion.IsPredictionEnabled())
- {
- SFusion.SetPredictionEnabled(false);
- SetAdjustMessage("Motion Prediction Off");
- }
- else
- {
- SFusion.SetPredictionEnabled(true);
- SetAdjustMessage("Motion Prediction On");
- }
- }
- break;
- default:
- break;
- }
-}
-
-void OculusWorldDemoApp::OnIdle()
-{
-
- double curtime = pPlatform->GetAppTime();
- float dt = float(curtime - LastUpdate);
- LastUpdate = curtime;
-
- // Update gamepad.
- GamepadState gamepadState;
- if (GetPlatformCore()->GetGamepadManager()->GetGamepadState(0, &gamepadState))
- {
- GamepadStateChanged(gamepadState);
- }
-
-
- if (LoadingState == LoadingState_DoLoad)
- {
- PopulateScene(MainFilePath.ToCStr());
- LoadingState = LoadingState_Finished;
- return;
- }
-
- // Check if any new devices were connected.
- {
- bool queueIsEmpty = false;
- while (!queueIsEmpty)
- {
- DeviceStatusNotificationDesc desc;
-
- {
- Lock::Locker lock(pManager->GetHandlerLock());
- if (DeviceStatusNotificationsQueue.GetSize() == 0)
- break;
- desc = DeviceStatusNotificationsQueue.Front();
-
- // We can't call Clear under the lock since this may introduce a dead lock:
- // this thread is locked by HandlerLock and the Clear might cause
- // call of Device->Release, which will use Manager->DeviceLock. The bkg
- // thread is most likely locked by opposite way:
- // Manager->DeviceLock ==> HandlerLock, therefore - a dead lock.
- // So, just grab the first element, save a copy of it and remove
- // the element (Device->Release won't be called since we made a copy).
-
- DeviceStatusNotificationsQueue.RemoveAt(0);
- queueIsEmpty = (DeviceStatusNotificationsQueue.GetSize() == 0);
- }
-
- bool wasAlreadyCreated = desc.Handle.IsCreated();
-
- if (desc.Action == Message_DeviceAdded)
- {
- switch(desc.Handle.GetType())
- {
- case Device_Sensor:
- if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
- {
- if (!pSensor)
- {
- pSensor = *desc.Handle.CreateDeviceTyped<SensorDevice>();
- SFusion.AttachToSensor(pSensor);
- SetAdjustMessage("---------------------------\n"
- "SENSOR connected\n"
- "---------------------------");
- }
- else if (!wasAlreadyCreated)
- {
- LogText("A new SENSOR has been detected, but it is not currently used.");
- }
- }
- break;
- case Device_LatencyTester:
- if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
- {
- if (!pLatencyTester)
- {
- pLatencyTester = *desc.Handle.CreateDeviceTyped<LatencyTestDevice>();
- LatencyUtil.SetDevice(pLatencyTester);
- if (!wasAlreadyCreated)
- SetAdjustMessage("----------------------------------------\n"
- "LATENCY TESTER connected\n"
- "----------------------------------------");
- }
- }
- break;
- case Device_HMD:
- {
- OVR::HMDInfo info;
- desc.Handle.GetDeviceInfo(&info);
- // if strlen(info.DisplayDeviceName) == 0 then
- // this HMD is 'fake' (created using sensor).
- if (strlen(info.DisplayDeviceName) > 0 && (!pHMD || !info.IsSameDisplay(HMDInfo)))
- {
- SetAdjustMessage("------------------------\n"
- "HMD connected\n"
- "------------------------");
- if (!pHMD || !desc.Handle.IsDevice(pHMD))
- pHMD = *desc.Handle.CreateDeviceTyped<HMDDevice>();
- // update stereo config with new HMDInfo
- if (pHMD && pHMD->GetDeviceInfo(&HMDInfo))
- {
- //RenderParams.MonitorName = hmd.DisplayDeviceName;
- SConfig.SetHMDInfo(HMDInfo);
- }
- LogText("HMD device added.\n");
- }
- break;
- }
- default:;
- }
- }
- else if (desc.Action == Message_DeviceRemoved)
- {
- if (desc.Handle.IsDevice(pSensor))
- {
- LogText("Sensor reported device removed.\n");
- SFusion.AttachToSensor(NULL);
- pSensor.Clear();
- SetAdjustMessage("-------------------------------\n"
- "SENSOR disconnected.\n"
- "-------------------------------");
- }
- else if (desc.Handle.IsDevice(pLatencyTester))
- {
- LogText("Latency Tester reported device removed.\n");
- LatencyUtil.SetDevice(NULL);
- pLatencyTester.Clear();
- SetAdjustMessage("---------------------------------------------\n"
- "LATENCY SENSOR disconnected.\n"
- "---------------------------------------------");
- }
- else if (desc.Handle.IsDevice(pHMD))
- {
- if (pHMD && !pHMD->IsDisconnected())
- {
- SetAdjustMessage("---------------------------\n"
- "HMD disconnected\n"
- "---------------------------");
- // Disconnect HMD. pSensor is used to restore 'fake' HMD device
- // (can be NULL).
- pHMD = pHMD->Disconnect(pSensor);
-
- // This will initialize HMDInfo with information about configured IPD,
- // screen size and other variables needed for correct projection.
- // We pass HMD DisplayDeviceName into the renderer to select the
- // correct monitor in full-screen mode.
- if (pHMD && pHMD->GetDeviceInfo(&HMDInfo))
- {
- //RenderParams.MonitorName = hmd.DisplayDeviceName;
- SConfig.SetHMDInfo(HMDInfo);
- }
- LogText("HMD device removed.\n");
- }
- }
- }
- else
- OVR_ASSERT(0); // unexpected action
- }
- }
-
- // If one of Stereo setting adjustment keys is pressed, adjust related state.
- if (pAdjustFunc)
- {
- (this->*pAdjustFunc)(dt * AdjustDirection * (ShiftDown ? 5.0f : 1.0f));
- }
-
- // Process latency tester results.
- const char* results = LatencyUtil.GetResultsString();
- if (results != NULL)
- {
- LogText("LATENCY TESTER: %s\n", results);
- }
-
- // Have to place this as close as possible to where the HMD orientation is read.
- LatencyUtil.ProcessInputs();
-
- // Magnetometer calibration procedure
- if (MagCal.IsManuallyCalibrating())
- UpdateManualMagCalibration();
-
- if (MagCal.IsAutoCalibrating())
- {
- MagCal.UpdateAutoCalibration(SFusion);
- if (MagCal.IsCalibrated())
- {
- if (SFusion.IsMagReady())
- SFusion.SetYawCorrectionEnabled(true);
- Vector3f mc = MagCal.GetMagCenter();
- SetAdjustMessage(" Magnetometer Calibration Complete \nCenter: %f %f %f",mc.x,mc.y,mc.z);
- }
- }
-
- // Handle Sensor motion.
- // We extract Yaw, Pitch, Roll instead of directly using the orientation
- // to allow "additional" yaw manipulation with mouse/controller.
- if(pSensor)
- {
- Quatf hmdOrient = SFusion.GetPredictedOrientation();
-
- float yaw = 0.0f;
- hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &Player.EyePitch, &Player.EyeRoll);
-
- Player.EyeYaw += (yaw - Player.LastSensorYaw);
- Player.LastSensorYaw = yaw;
-
- // NOTE: We can get a matrix from orientation as follows:
- // Matrix4f hmdMat(hmdOrient);
-
- // Test logic - assign quaternion result directly to view:
- // Quatf hmdOrient = SFusion.GetOrientation();
- // View = Matrix4f(hmdOrient.Inverted()) * Matrix4f::Translation(-EyePos);
- }
-
-
- if(curtime >= NextFPSUpdate)
- {
- NextFPSUpdate = curtime + 1.0;
- FPS = FrameCounter;
- FrameCounter = 0;
- }
- FrameCounter++;
-
- if(FPS < 40)
- {
- ConsecutiveLowFPSFrames++;
- }
- else
- {
- ConsecutiveLowFPSFrames = 0;
- }
-
- if(ConsecutiveLowFPSFrames > 200)
- {
- DropLOD();
- ConsecutiveLowFPSFrames = 0;
- }
-
- Player.EyeYaw -= Player.GamepadRotate.x * dt;
- Player.HandleCollision(dt, &CollisionModels, &GroundCollisionModels, ShiftDown);
-
- if(!pSensor)
- {
- Player.EyePitch -= Player.GamepadRotate.y * dt;
-
- const float maxPitch = ((3.1415f / 2) * 0.98f);
- if(Player.EyePitch > maxPitch)
- {
- Player.EyePitch = maxPitch;
- }
- if(Player.EyePitch < -maxPitch)
- {
- Player.EyePitch = -maxPitch;
- }
- }
-
- // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
- //
- Matrix4f rollPitchYaw = Matrix4f::RotationY(Player.EyeYaw) * Matrix4f::RotationX(Player.EyePitch) *
- Matrix4f::RotationZ(Player.EyeRoll);
- Vector3f up = rollPitchYaw.Transform(UpVector);
- Vector3f forward = rollPitchYaw.Transform(ForwardVector);
-
-
- // Minimal head modeling; should be moved as an option to SensorFusion.
- float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
- float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
-
- Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
- Vector3f shiftedEyePos = Player.EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
- shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
- View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
-
- // Transformation without head modeling.
- // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
-
- // This is an alternative to LookAtRH:
- // Here we transpose the rotation matrix to get its inverse.
- // View = (Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
- // Matrix4f::RotationZ(EyeRoll)).Transposed() *
- // Matrix4f::Translation(-EyePos);
-
-
- switch(SConfig.GetStereoMode())
- {
- case Stereo_None:
- Render(SConfig.GetEyeRenderParams(StereoEye_Center));
- break;
-
- case Stereo_LeftRight_Multipass:
- //case Stereo_LeftDouble_Multipass:
- Render(SConfig.GetEyeRenderParams(StereoEye_Left));
- Render(SConfig.GetEyeRenderParams(StereoEye_Right));
- break;
-
- }
-
- pRender->Present();
- // Force GPU to flush the scene, resulting in the lowest possible latency.
- pRender->ForceFlushGPU();
-}
-
-
-
-void OculusWorldDemoApp::UpdateManualMagCalibration()
-{
- float tyaw,yaw, pitch, roll;
- Quatf hmdOrient = SFusion.GetOrientation();
- // Note that yaw and pitch are used from two different Euler angle combinations. This
- // is done so that pitch (looking "up" or "down") is not dependent on yaw angle
- hmdOrient.GetEulerAngles<Axis_X, Axis_Z, Axis_Y>(&pitch, &roll, &yaw);
- hmdOrient.GetEulerAngles<Axis_Y, Axis_Z, Axis_X>(&tyaw, &roll, &pitch);
- Vector3f mag = SFusion.GetMagnetometer();
- float dtr = Math<float>::DegreeToRadFactor;
-
- switch(MagCal.NumberOfSamples())
- {
- case 0:
- if (MagAwaitingForwardLook)
- SetAdjustMessage("Magnetometer Calibration\n** Step 1: Please Look Forward **\n** and Press Z When Ready **");
- else
- if (fabs(pitch) < 10.0f*dtr)
- {
- MagCal.InsertIfAcceptable(hmdOrient, mag);
- FirstMagYaw = yaw;
- MagAwaitingForwardLook = false;
- SFusion.SetMagReference();
- }
- break;
- case 1:
- SetAdjustMessage("Magnetometer Calibration\n** Step 2: Please Look Up **");
- yaw -= FirstMagYaw;
- if (yaw < -Math<float>::Pi)
- yaw += Math<float>::TwoPi;
- if (yaw > Math<float>::Pi)
- yaw -= Math<float>::TwoPi;
- if ((pitch > 50.0f*dtr) && (fabs(yaw) < 20.0f*dtr))
- MagCal.InsertIfAcceptable(hmdOrient, mag);
- break;
- case 2:
- SetAdjustMessage("Magnetometer Calibration\n** Step 3: Please Look Left **");
- yaw -= FirstMagYaw;
- if (yaw < -Math<float>::Pi)
- yaw += Math<float>::TwoPi;
- if (yaw > Math<float>::Pi)
- yaw -= Math<float>::TwoPi;
- if (yaw > 60.0f*dtr)
- MagCal.InsertIfAcceptable(hmdOrient, mag);
- break;
- case 3:
- SetAdjustMessage("Magnetometer Calibration\n** Step 4: Please Look Right **");
- yaw -= FirstMagYaw;
- if (yaw < -Math<float>::Pi)
- yaw += Math<float>::TwoPi;
- if (yaw > Math<float>::Pi)
- yaw -= Math<float>::TwoPi;
- if (yaw < -60.0f*dtr)
- MagCal.InsertIfAcceptable(hmdOrient, mag);
- break;
- case 4:
- if (!MagCal.IsCalibrated())
- {
- MagCal.SetCalibration(SFusion);
- if (SFusion.IsMagReady())
- SFusion.SetYawCorrectionEnabled(true);
- Vector3f mc = MagCal.GetMagCenter();
- SetAdjustMessage(" Magnetometer Calibration and Activation \nCenter: %f %f %f\nReference Yaw: %f",
- mc.x,mc.y,mc.z,SFusion.GetMagRefYaw());
- }
- }
-}
-
-static const char* HelpText =
- "F1 \t100 NoStereo \t420 Z \t520 Manual Mag Calib\n"
- "F2 \t100 Stereo \t420 X \t520 Auto Mag Calib\n"
- "F3 \t100 StereoHMD \t420 ; \t520 Mag Set Ref Point\n"
- "F4 \t100 MSAA \t420 F6 \t520 Mag Info\n"
- "F9 \t100 FullScreen \t420 R \t520 Reset SensorFusion\n"
- "F11 \t100 Fast FullScreen \t500 - + \t660 Adj EyeHeight\n"
- "C \t100 Chromatic Ab \t500 [ ] \t660 Adj FOV\n"
- "P \t100 Motion Pred \t500 Shift \t660 Adj Faster\n"
- "N/M \t180 Adj Motion Pred\n"
- "( / ) \t180 Adj EyeDistance"
- ;
-
-
-enum DrawTextCenterType
-{
- DrawText_NoCenter= 0,
- DrawText_VCenter = 0x1,
- DrawText_HCenter = 0x2,
- DrawText_Center = DrawText_VCenter | DrawText_HCenter
-};
-
-static void DrawTextBox(RenderDevice* prender, float x, float y,
- float textSize, const char* text,
- DrawTextCenterType centerType = DrawText_NoCenter)
-{
- float ssize[2] = {0.0f, 0.0f};
-
- prender->MeasureText(&DejaVu, text, textSize, ssize);
-
- // Treat 0 a VCenter.
- if (centerType & DrawText_HCenter)
- {
- x = -ssize[0]/2;
- }
- if (centerType & DrawText_VCenter)
- {
- y = -ssize[1]/2;
- }
-
- prender->FillRect(x-0.02f, y-0.02f, x+ssize[0]+0.02f, y+ssize[1]+0.02f, Color(40,40,100,210));
- prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210));
-}
-
-void OculusWorldDemoApp::Render(const StereoEyeParams& stereo)
-{
- pRender->BeginScene(PostProcess);
-
- // *** 3D - Configures Viewport/Projection and Render
- pRender->ApplyStereoParams(stereo);
- pRender->Clear();
-
- pRender->SetDepthMode(true, true);
- if (SceneMode != Scene_Grid)
- {
- MainScene.Render(pRender, stereo.ViewAdjust * View);
- }
-
- if (SceneMode == Scene_YawView)
- {
- Matrix4f calView = Matrix4f();
- float viewYaw = -Player.LastSensorYaw + SFusion.GetMagRefYaw();
- calView.M[0][0] = calView.M[2][2] = cos(viewYaw);
- calView.M[0][2] = sin(viewYaw);
- calView.M[2][0] = -sin(viewYaw);
- //LogText("yaw: %f\n",SFusion.GetMagRefYaw());
-
- if (SFusion.IsYawCorrectionInProgress())
- YawMarkGreenScene.Render(pRender, stereo.ViewAdjust);
- else
- YawMarkRedScene.Render(pRender, stereo.ViewAdjust);
-
- if (fabs(Player.EyePitch) < Math<float>::Pi * 0.33)
- YawLinesScene.Render(pRender, stereo.ViewAdjust * calView);
- }
-
- // *** 2D Text & Grid - Configure Orthographic rendering.
-
- // Render UI in 2D orthographic coordinate system that maps [-1,1] range
- // to a readable FOV area centered at your eye and properly adjusted.
- pRender->ApplyStereoParams2D(stereo);
- pRender->SetDepthMode(false, false);
-
- float unitPixel = SConfig.Get2DUnitPixel();
- float textHeight= unitPixel * 22;
-
- if ((SceneMode == Scene_Grid)||(SceneMode == Scene_Both))
- { // Draw grid two pixels thick.
- GridScene.Render(pRender, Matrix4f());
- GridScene.Render(pRender, Matrix4f::Translation(unitPixel,unitPixel,0));
- }
-
- // Display Loading screen-shot in frame 0.
- if (LoadingState != LoadingState_Finished)
- {
- LoadingScene.Render(pRender, Matrix4f());
- String loadMessage = String("Loading ") + MainFilePath;
- DrawTextBox(pRender, 0.0f, 0.25f, textHeight, loadMessage.ToCStr(), DrawText_HCenter);
- LoadingState = LoadingState_DoLoad;
- }
-
- if(AdjustMessageTimeout > pPlatform->GetAppTime())
- {
- DrawTextBox(pRender,0.0f,0.4f, textHeight, AdjustMessage.ToCStr(), DrawText_HCenter);
- }
-
- switch(TextScreen)
- {
- case Text_Orientation:
- {
- char buf[256], gpustat[256];
- OVR_sprintf(buf, sizeof(buf),
- " Yaw:%4.0f Pitch:%4.0f Roll:%4.0f \n"
- " FPS: %d Frame: %d \n Pos: %3.2f, %3.2f, %3.2f \n"
- " EyeHeight: %3.2f",
- RadToDegree(Player.EyeYaw), RadToDegree(Player.EyePitch), RadToDegree(Player.EyeRoll),
- FPS, FrameCounter, Player.EyePos.x, Player.EyePos.y, Player.EyePos.z, Player.EyePos.y);
- size_t texMemInMB = pRender->GetTotalTextureMemoryUsage() / 1058576;
- if (texMemInMB)
- {
- OVR_sprintf(gpustat, sizeof(gpustat), "\n GPU Tex: %u MB", texMemInMB);
- OVR_strcat(buf, sizeof(buf), gpustat);
- }
-
- DrawTextBox(pRender, 0.0f, -0.15f, textHeight, buf, DrawText_HCenter);
- }
- break;
-
- case Text_Config:
- {
- char textBuff[2048];
-
- OVR_sprintf(textBuff, sizeof(textBuff),
- "Fov\t300 %9.4f\n"
- "EyeDistance\t300 %9.4f\n"
- "DistortionK0\t300 %9.4f\n"
- "DistortionK1\t300 %9.4f\n"
- "DistortionK2\t300 %9.4f\n"
- "DistortionK3\t300 %9.4f\n"
- "TexScale\t300 %9.4f",
- SConfig.GetYFOVDegrees(),
- SConfig.GetIPD(),
- SConfig.GetDistortionK(0),
- SConfig.GetDistortionK(1),
- SConfig.GetDistortionK(2),
- SConfig.GetDistortionK(3),
- SConfig.GetDistortionScale());
-
- DrawTextBox(pRender, 0.0f, 0.0f, textHeight, textBuff, DrawText_Center);
- }
- break;
-
- case Text_Help:
- DrawTextBox(pRender, 0.0f, -0.1f, textHeight, HelpText, DrawText_Center);
-
- default:
- break;
- }
-
-
- // Display colored quad if we're doing a latency test.
- Color colorToDisplay;
- if (LatencyUtil.DisplayScreenColor(colorToDisplay))
- {
- pRender->FillRect(-0.4f, -0.4f, 0.4f, 0.4f, colorToDisplay);
- }
-
- pRender->FinishScene();
-}
-
-
-// Sets temporarily displayed message for adjustments
-void OculusWorldDemoApp::SetAdjustMessage(const char* format, ...)
-{
- Lock::Locker lock(pManager->GetHandlerLock());
- char textBuff[2048];
- va_list argList;
- va_start(argList, format);
- OVR_vsprintf(textBuff, sizeof(textBuff), format, argList);
- va_end(argList);
-
- // Message will time out in 4 seconds.
- AdjustMessage = textBuff;
- AdjustMessageTimeout = pPlatform->GetAppTime() + 4.0f;
-}
-
-void OculusWorldDemoApp::SetAdjustMessageTimeout(float timeout)
-{
- AdjustMessageTimeout = pPlatform->GetAppTime() + timeout;
-}
-
-// ***** View Control Adjustments
-
-void OculusWorldDemoApp::AdjustFov(float dt)
-{
- float esd = SConfig.GetEyeToScreenDistance() + 0.01f * dt;
- SConfig.SetEyeToScreenDistance(esd);
- SetAdjustMessage("ESD:%6.3f FOV: %6.3f", esd, SConfig.GetYFOVDegrees());
-}
-
-void OculusWorldDemoApp::AdjustAspect(float dt)
-{
- float rawAspect = SConfig.GetAspect() / SConfig.GetAspectMultiplier();
- float newAspect = SConfig.GetAspect() + 0.01f * dt;
- SConfig.SetAspectMultiplier(newAspect / rawAspect);
- SetAdjustMessage("Aspect: %6.3f", newAspect);
-}
-
-void OculusWorldDemoApp::AdjustDistortion(float dt, int kIndex, const char* label)
-{
- SConfig.SetDistortionK(kIndex, SConfig.GetDistortionK(kIndex) + 0.03f * dt);
- SetAdjustMessage("%s: %6.4f", label, SConfig.GetDistortionK(kIndex));
-}
-
-void OculusWorldDemoApp::AdjustIPD(float dt)
-{
- SConfig.SetIPD(SConfig.GetIPD() + 0.025f * dt);
- SetAdjustMessage("EyeDistance: %6.4f", SConfig.GetIPD());
-}
-
-void OculusWorldDemoApp::AdjustEyeHeight(float dt)
-{
- float dist = 0.5f * dt;
-
- Player.EyeHeight += dist;
- Player.EyePos.y += dist;
-
- SetAdjustMessage("EyeHeight: %4.2f", Player.EyeHeight);
-}
-
-void OculusWorldDemoApp::AdjustMotionPrediction(float dt)
-{
- float motionPred = SFusion.GetPredictionDelta() + 0.01f * dt;
-
- if (motionPred < 0.0f)
- {
- motionPred = 0.0f;
- }
-
- SFusion.SetPrediction(motionPred);
-
- SetAdjustMessage("MotionPrediction: %6.3fs", motionPred);
-}
-
-
-// Loads the scene data
-void OculusWorldDemoApp::PopulateScene(const char *fileName)
-{
- XmlHandler xmlHandler;
- if(!xmlHandler.ReadFile(fileName, pRender, &MainScene, &CollisionModels, &GroundCollisionModels))
- {
- SetAdjustMessage("---------------------------------\nFILE LOAD FAILED\n---------------------------------");
- SetAdjustMessageTimeout(10.0f);
- }
-
- MainScene.SetAmbient(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
-
- // Distortion debug grid (brought up by 'G' key).
- Ptr<Model> gridModel = *Model::CreateGrid(Vector3f(0,0,0), Vector3f(1.0f/10, 0,0), Vector3f(0,1.0f/10,0),
- 10, 10, 5,
- Color(0, 255, 0, 255), Color(255, 50, 50, 255) );
- GridScene.World.Add(gridModel);
-
- // Yaw angle marker and lines (brought up by ';' key).
- float shifty = -0.5f;
- Ptr<Model> yawMarkGreenModel = *Model::CreateBox(Color(0, 255, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
- YawMarkGreenScene.World.Add(yawMarkGreenModel);
- Ptr<Model> yawMarkRedModel = *Model::CreateBox(Color(255, 0, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
- YawMarkRedScene.World.Add(yawMarkRedModel);
-
- Ptr<Model> yawLinesModel = *new Model(Prim_Lines);
- float r = 2.0f;
- float theta0 = Math<float>::PiOver2;
- float theta1 = 0.0f;
- Color c = Color(255, 200, 200, 255);
- for (int i = 0; i < 35; i++)
- {
- theta1 = theta0 + Math<float>::Pi / 18.0f;
- yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
- yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
- theta0 = theta1;
- yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
- yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty+0.1f,-r*sin(theta0)),c));
- theta0 = theta1;
- }
- theta1 = theta0 + Math<float>::Pi / 18.0f;
- yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
- yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
- yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
- yawLinesModel->AddVertex(Vector3f(r*sin(0.02f),shifty,-r*cos(0.02f)),c));
- yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
- yawLinesModel->AddVertex(Vector3f(r*sin(-0.02f),shifty,-r*cos(-0.02f)),c));
- yawLinesModel->SetPosition(Vector3f(0.0f,0.0f,0.0f));
-
- YawLinesScene.World.Add(yawLinesModel);
-}
-
-
-void OculusWorldDemoApp::PopulatePreloadScene()
-{
- // Load-screen screen shot image
- String fileName = MainFilePath;
- fileName.StripExtension();
-
- Ptr<File> imageFile = *new SysFile(fileName + "_LoadScreen.tga");
- Ptr<Texture> imageTex;
- if (imageFile->IsValid())
- imageTex = *LoadTextureTga(pRender, imageFile);
-
- // Image is rendered as a single quad.
- if (imageTex)
- {
- imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
- Ptr<Model> m = *new Model(Prim_Triangles);
- m->AddVertex(-0.5f, 0.5f, 0.0f, Color(255,255,255,255), 0.0f, 0.0f);
- m->AddVertex( 0.5f, 0.5f, 0.0f, Color(255,255,255,255), 1.0f, 0.0f);
- m->AddVertex( 0.5f, -0.5f, 0.0f, Color(255,255,255,255), 1.0f, 1.0f);
- m->AddVertex(-0.5f, -0.5f, 0.0f, Color(255,255,255,255), 0.0f, 1.0f);
- m->AddTriangle(2,1,0);
- m->AddTriangle(0,3,2);
-
- Ptr<ShaderFill> fill = *new ShaderFill(*pRender->CreateShaderSet());
- fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
- fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture));
- fill->SetTexture(0, imageTex);
- m->Fill = fill;
-
- LoadingScene.World.Add(m);
- }
-}
-
-void OculusWorldDemoApp::ClearScene()
-{
- MainScene.Clear();
- GridScene.Clear();
- YawMarkGreenScene.Clear();
- YawMarkRedScene.Clear();
- YawLinesScene.Clear();
-}
-
-void OculusWorldDemoApp::PopulateLODFileNames()
-{
- //OVR::String mainFilePath = MainFilePath;
- LODFilePaths.PushBack(MainFilePath);
- int LODIndex = 1;
- SPInt pos = strcspn(MainFilePath.ToCStr(), ".");
- SPInt len = strlen(MainFilePath.ToCStr());
- SPInt diff = len - pos;
-
- if (diff == 0)
- return;
-
- while(true)
- {
- char pathWithoutExt[250];
- char buffer[250];
- for(SPInt i = 0; i < pos; ++i)
- {
- pathWithoutExt[i] = MainFilePath[(int)i];
- }
- pathWithoutExt[pos] = '\0';
- OVR_sprintf(buffer, sizeof(buffer), "%s%i.xml", pathWithoutExt, LODIndex);
- FILE* fp = 0;
-#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
- errno_t err = fopen_s(&fp, buffer, "rb");
- if(!fp || err)
- {
-#else
- fp = fopen(buffer, "rb");
- if(!fp)
- {
-#endif
- break;
- }
- fclose(fp);
- OVR::String result = buffer;
- LODFilePaths.PushBack(result);
- LODIndex++;
- }
-}
-
-void OculusWorldDemoApp::DropLOD()
-{
- if(CurrentLODFileIndex < (int)(LODFilePaths.GetSize() - 1))
- {
- ClearScene();
- CurrentLODFileIndex++;
- PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
- }
-}
-
-void OculusWorldDemoApp::RaiseLOD()
-{
- if(CurrentLODFileIndex > 0)
- {
- ClearScene();
- CurrentLODFileIndex--;
- PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
- }
-}
-
-//-----------------------------------------------------------------------------
-void OculusWorldDemoApp::CycleDisplay()
-{
- int screenCount = pPlatform->GetDisplayCount();
-
- // If Windowed, switch to the HMD screen first in Full-Screen Mode.
- // If already Full-Screen, cycle to next screen until we reach FirstScreenInCycle.
-
- if (pRender->IsFullscreen())
- {
- // Right now, we always need to restore window before going to next screen.
- pPlatform->SetFullscreen(RenderParams, Display_Window);
-
- Screen++;
- if (Screen == screenCount)
- Screen = 0;
-
- RenderParams.Display = pPlatform->GetDisplay(Screen);
-
- if (Screen != FirstScreenInCycle)
- {
- pRender->SetParams(RenderParams);
- pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
- }
- }
- else
- {
- // Try to find HMD Screen, making it the first screen in full-screen Cycle.
- FirstScreenInCycle = 0;
-
- if (pHMD)
- {
- DisplayId HMD (SConfig.GetHMDInfo().DisplayDeviceName, SConfig.GetHMDInfo().DisplayId);
- for (int i = 0; i< screenCount; i++)
- {
- if (pPlatform->GetDisplay(i) == HMD)
- {
- FirstScreenInCycle = i;
- break;
- }
- }
- }
-
- // Switch full-screen on the HMD.
- Screen = FirstScreenInCycle;
- RenderParams.Display = pPlatform->GetDisplay(Screen);
- pRender->SetParams(RenderParams);
- pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
- }
-}
-
-void OculusWorldDemoApp::GamepadStateChanged(const GamepadState& pad)
-{
- Player.GamepadMove = Vector3f(pad.LX * pad.LX * (pad.LX > 0 ? 1 : -1),
- 0,
- pad.LY * pad.LY * (pad.LY > 0 ? -1 : 1));
- Player.GamepadRotate = Vector3f(2 * pad.RX, -2 * pad.RY, 0);
-}
-
-
-//-------------------------------------------------------------------------------------
-
-OVR_PLATFORM_APP(OculusWorldDemoApp);
+/************************************************************************************
+
+Filename : OculusWorldDemo.cpp
+Content : First-person view test application for Oculus Rift
+Created : October 4, 2012
+Authors : Michael Antonov, Andrew Reisse, Steve LaValle
+ Peter Hoff, Dan Goodman, Bryan Croteau
+
+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 "OVR.h"
+
+#include "../CommonSrc/Platform/Platform_Default.h"
+#include "../CommonSrc/Render/Render_Device.h"
+#include "../CommonSrc/Render/Render_XmlSceneLoader.h"
+#include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h"
+#include "../CommonSrc/Platform/Gamepad.h"
+
+#include <Kernel/OVR_SysFile.h>
+#include <Kernel/OVR_Log.h>
+#include <Kernel/OVR_Timer.h>
+
+#include "Player.h"
+
+// Filename to be loaded by default, searching specified paths.
+#define WORLDDEMO_ASSET_FILE "Tuscany.xml"
+#define WORLDDEMO_ASSET_PATH1 "Assets/Tuscany/"
+#define WORLDDEMO_ASSET_PATH2 "../Assets/Tuscany/"
+// This path allows the shortcut to work.
+#define WORLDDEMO_ASSET_PATH3 "Samples/OculusWorldDemo/Assets/Tuscany/"
+
+
+using namespace OVR;
+using namespace OVR::Platform;
+using namespace OVR::Render;
+
+//-------------------------------------------------------------------------------------
+// ***** OculusWorldDemo Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse and keyboard. The following keys work:
+//
+// 'W', 'S', 'A', 'D' and Arrow Keys - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+// F4 - Toggle MSAA.
+// F9 - Cycle through fullscreen and windowed modes. Necessary for previewing content with Rift.
+//
+// Important Oculus-specific logic can be found at following locations:
+//
+// OculusWorldDemoApp::OnStartup - This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OculusWorldDemoApp::OnIdle - Here we poll SensorFusion for orientation, apply it
+// to the scene and handle movement.
+// Stereo rendering is also done here, by delegating to
+// to Render function for each eye.
+//
+
+//-------------------------------------------------------------------------------------
+// ***** OculusWorldDemo Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+// It then works as follows:
+// - Graphics and HMD setup is done OculusWorldDemoApp::OnStartup(). This function
+// also creates the room model from Slab declarations.
+// - Per-frame processing is done in OnIdle(). This function processes
+// sensor and movement input and then renders the frame.
+// - Additional input processing is done in OnMouse, OnKey.
+
+class OculusWorldDemoApp : public Application, public MessageHandler
+{
+public:
+ OculusWorldDemoApp();
+ ~OculusWorldDemoApp();
+
+ virtual int OnStartup(int argc, const char** argv);
+ virtual void OnIdle();
+
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(OVR::KeyCode key, int chr, bool down, int modifiers);
+ virtual void OnResize(int width, int height);
+
+ virtual void OnMessage(const Message& msg);
+
+ void Render(const StereoEyeParams& stereo);
+
+ // Sets temporarily displayed message for adjustments
+ void SetAdjustMessage(const char* format, ...);
+ // Overrides current timeout, in seconds (not the future default value);
+ // intended to be called right after SetAdjustMessage.
+ void SetAdjustMessageTimeout(float timeout);
+
+ // Stereo setting adjustment functions.
+ // Called with deltaTime when relevant key is held.
+ void AdjustFov(float dt);
+ void AdjustAspect(float dt);
+ void AdjustIPD(float dt);
+ void AdjustEyeHeight(float dt);
+
+ void AdjustMotionPrediction(float dt);
+
+ void AdjustDistortion(float dt, int kIndex, const char* label);
+ void AdjustDistortionK0(float dt) { AdjustDistortion(dt, 0, "K0"); }
+ void AdjustDistortionK1(float dt) { AdjustDistortion(dt, 1, "K1"); }
+ void AdjustDistortionK2(float dt) { AdjustDistortion(dt, 2, "K2"); }
+ void AdjustDistortionK3(float dt) { AdjustDistortion(dt, 3, "K3"); }
+
+ // Adds room model to scene.
+ void PopulateScene(const char* fileName);
+ void PopulatePreloadScene();
+ void ClearScene();
+
+ // Magnetometer calibration procedure
+ void UpdateManualMagCalibration();
+
+protected:
+ RenderDevice* pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+ int Screen;
+ int FirstScreenInCycle;
+
+ // Magnetometer calibration and yaw correction
+ Util::MagCalibration MagCal;
+ bool MagAwaitingForwardLook;
+
+ // *** Oculus HMD Variables
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ Ptr<Profile> pUserProfile;
+ SensorFusion SFusion;
+ HMDInfo TheHMDInfo;
+
+ Ptr<LatencyTestDevice> pLatencyTester;
+ Util::LatencyTest LatencyUtil;
+
+ double LastUpdate;
+ int FPS;
+ int FrameCounter;
+ double NextFPSUpdate;
+
+ Array<Ptr<CollisionModel> > CollisionModels;
+ Array<Ptr<CollisionModel> > GroundCollisionModels;
+
+ // Loading process displays screenshot in first frame
+ // and then proceeds to load until finished.
+ enum LoadingStateType
+ {
+ LoadingState_Frame0,
+ LoadingState_DoLoad,
+ LoadingState_Finished
+ };
+
+ // Player
+ Player ThePlayer;
+ Matrix4f View;
+ Scene MainScene;
+ Scene LoadingScene;
+ Scene GridScene;
+ Scene YawMarkGreenScene;
+ Scene YawMarkRedScene;
+ Scene YawLinesScene;
+
+ LoadingStateType LoadingState;
+
+ Ptr<ShaderFill> LitSolid, LitTextures[4];
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // LOD
+ String MainFilePath;
+ Array<String> LODFilePaths;
+ int ConsecutiveLowFPSFrames;
+ int CurrentLODFileIndex;
+
+ float DistortionK0;
+ float DistortionK1;
+ float DistortionK2;
+ float DistortionK3;
+
+ String AdjustMessage;
+ double AdjustMessageTimeout;
+
+ // Saved distortion state.
+ float SavedK0, SavedK1, SavedK2, SavedK3;
+ float SavedESD, SavedAspect, SavedEyeDistance;
+
+ // Allows toggling color around distortion.
+ Color DistortionClearColor;
+
+ // Stereo settings adjustment state.
+ typedef void (OculusWorldDemoApp::*AdjustFuncType)(float);
+ bool ShiftDown;
+ AdjustFuncType pAdjustFunc;
+ float AdjustDirection;
+
+ enum SceneRenderMode
+ {
+ Scene_World,
+ Scene_Grid,
+ Scene_Both,
+ Scene_YawView
+ };
+ SceneRenderMode SceneMode;
+
+
+ enum TextScreen
+ {
+ Text_None,
+ Text_Orientation,
+ Text_Config,
+ Text_Help,
+ Text_Count
+ };
+ TextScreen TextScreen;
+
+ struct DeviceStatusNotificationDesc
+ {
+ DeviceHandle Handle;
+ MessageType Action;
+
+ DeviceStatusNotificationDesc():Action(Message_None) {}
+ DeviceStatusNotificationDesc(MessageType mt, const DeviceHandle& dev)
+ : Handle(dev), Action(mt) {}
+ };
+ Array<DeviceStatusNotificationDesc> DeviceStatusNotificationsQueue;
+
+
+ Model* CreateModel(Vector3f pos, struct SlabModel* sm);
+ Model* CreateBoundingModel(CollisionModel &cm);
+ void PopulateLODFileNames();
+ void DropLOD();
+ void RaiseLOD();
+ void CycleDisplay();
+ void GamepadStateChanged(const GamepadState& pad);
+
+ // Variable used by UpdateManualCalibration
+ Anglef FirstMagYaw;
+ int ManualMagCalStage;
+ int ManualMagFailures;
+};
+
+//-------------------------------------------------------------------------------------
+
+OculusWorldDemoApp::OculusWorldDemoApp()
+ : pRender(0),
+ LastUpdate(0),
+ LoadingState(LoadingState_Frame0),
+ // Initial location
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ DistortionClearColor(0, 0, 0),
+
+ ShiftDown(false),
+ pAdjustFunc(0),
+ AdjustDirection(1.0f),
+ SceneMode(Scene_World),
+ TextScreen(Text_None)
+{
+ Width = 1280;
+ Height = 800;
+ Screen = 0;
+ FirstScreenInCycle = 0;
+
+ FPS = 0;
+ FrameCounter = 0;
+ NextFPSUpdate = 0;
+
+ ConsecutiveLowFPSFrames = 0;
+ CurrentLODFileIndex = 0;
+
+ AdjustMessageTimeout = 0;
+}
+
+OculusWorldDemoApp::~OculusWorldDemoApp()
+{
+ RemoveHandlerFromDevices();
+
+ if(DejaVu.fill)
+ {
+ DejaVu.fill->Release();
+ }
+ pLatencyTester.Clear();
+ pSensor.Clear();
+ pHMD.Clear();
+
+ CollisionModels.ClearAndRelease();
+ GroundCollisionModels.ClearAndRelease();
+}
+
+int OculusWorldDemoApp::OnStartup(int argc, const char** argv)
+{
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if(pHMD->GetDeviceInfo(&TheHMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(TheHMDInfo);
+ }
+
+ // Retrieve relevant profile settings.
+ pUserProfile = pHMD->GetProfile();
+ if (pUserProfile)
+ {
+ ThePlayer.EyeHeight = pUserProfile->GetEyeHeight();
+ ThePlayer.EyePos.y = ThePlayer.EyeHeight;
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+ // Create the Latency Tester device and assign it to the LatencyTesterUtil object.
+ pLatencyTester = *pManager->EnumerateDevices<LatencyTestDevice>().CreateDevice();
+ if (pLatencyTester)
+ {
+ LatencyUtil.SetDevice(pLatencyTester);
+ }
+ // Make the user aware which devices are present.
+ if(pHMD == NULL && pSensor == NULL)
+ {
+ SetAdjustMessage("---------------------------------\nNO HMD DETECTED\nNO SENSOR DETECTED\n---------------------------------");
+ }
+ else if(pHMD == NULL)
+ {
+ SetAdjustMessage("----------------------------\nNO HMD DETECTED\n----------------------------");
+ }
+ else if(pSensor == NULL)
+ {
+ SetAdjustMessage("---------------------------------\nNO SENSOR DETECTED\n---------------------------------");
+ }
+ else
+ {
+ SetAdjustMessage("--------------------------------------------\n"
+ "Press F9 for Full-Screen on Rift\n"
+ "--------------------------------------------");
+ }
+
+ // First message should be extra-long.
+ SetAdjustMessageTimeout(10.0f);
+
+
+ if(TheHMDInfo.HResolution > 0)
+ {
+ Width = TheHMDInfo.HResolution;
+ Height = TheHMDInfo.VResolution;
+ }
+
+ if(!pPlatform->SetupWindow(Width, Height))
+ {
+ return 1;
+ }
+
+ String Title = "Oculus World Demo";
+ if(TheHMDInfo.ProductName[0])
+ {
+ Title += " : ";
+ Title += TheHMDInfo.ProductName;
+ }
+ pPlatform->SetWindowTitle(Title);
+
+ // Report relative mouse motion in OnMouseMove
+ pPlatform->SetMouseMode(Mouse_Relative);
+
+ if(pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+
+ SFusion.SetDelegateMessageHandler(this);
+
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ const char* graphics = "d3d11";
+
+ // Select renderer based on command line arguments.
+ for(int i = 1; i < argc; i++)
+ {
+ if(!strcmp(argv[i], "-r") && i < argc - 1)
+ {
+ graphics = argv[i + 1];
+ }
+ else if(!strcmp(argv[i], "-fs"))
+ {
+ RenderParams.Fullscreen = true;
+ }
+ }
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET,
+ graphics, RenderParams);
+
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of
+ // invisible screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (TheHMDInfo.HScreenSize > 0.0f)
+ {
+ if (TheHMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+ //pRender->SetSceneRenderScale(1.0f);
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Identify Scene File & Prepare for Loading
+
+ // This creates lights and models.
+ if (argc == 2)
+ {
+ MainFilePath = argv[1];
+ PopulateLODFileNames();
+ }
+ else
+ {
+ fprintf(stderr, "Usage: OculusWorldDemo [input XML]\n");
+ MainFilePath = WORLDDEMO_ASSET_FILE;
+ }
+
+ // Try to modify path for correctness in case specified file is not found.
+ if (!SysFile(MainFilePath).IsValid())
+ {
+ String prefixPath1(pPlatform->GetContentDirectory() + "/" + WORLDDEMO_ASSET_PATH1),
+ prefixPath2(WORLDDEMO_ASSET_PATH2),
+ prefixPath3(WORLDDEMO_ASSET_PATH3);
+ if (SysFile(prefixPath1 + MainFilePath).IsValid())
+ MainFilePath = prefixPath1 + MainFilePath;
+ else if (SysFile(prefixPath2 + MainFilePath).IsValid())
+ MainFilePath = prefixPath2 + MainFilePath;
+ else if (SysFile(prefixPath3 + MainFilePath).IsValid())
+ MainFilePath = prefixPath3 + MainFilePath;
+ }
+
+ PopulatePreloadScene();
+
+ LastUpdate = pPlatform->GetAppTime();
+ //pPlatform->PlayMusicFile(L"Loop.wav");
+
+ return 0;
+}
+
+void OculusWorldDemoApp::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);
+
+ { // limit the scope of the lock
+ Lock::Locker lock(pManager->GetHandlerLock());
+ DeviceStatusNotificationsQueue.PushBack(
+ DeviceStatusNotificationDesc(statusMsg.Type, statusMsg.Handle));
+ }
+
+ switch (statusMsg.Type)
+ {
+ case OVR::Message_DeviceAdded:
+ LogText("DeviceManager reported device added.\n");
+ break;
+
+ case OVR::Message_DeviceRemoved:
+ LogText("DeviceManager reported device removed.\n");
+ break;
+
+ default: OVR_ASSERT(0); // unexpected type
+ }
+ }
+ }
+}
+
+void OculusWorldDemoApp::OnResize(int width, int height)
+{
+ Width = width;
+ Height = height;
+ SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
+}
+
+void OculusWorldDemoApp::OnMouseMove(int x, int y, int modifiers)
+{
+ if(modifiers & Mod_MouseRelative)
+ {
+ // Get Delta
+ int dx = x, dy = y;
+
+ const float maxPitch = ((3.1415f / 2) * 0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ ThePlayer.EyeYaw -= (Sensitivity * dx) / 360.0f;
+
+ if(!pSensor)
+ {
+ ThePlayer.EyePitch -= (Sensitivity * dy) / 360.0f;
+
+ if(ThePlayer.EyePitch > maxPitch)
+ {
+ ThePlayer.EyePitch = maxPitch;
+ }
+ if(ThePlayer.EyePitch < -maxPitch)
+ {
+ ThePlayer.EyePitch = -maxPitch;
+ }
+ }
+ }
+}
+
+
+void OculusWorldDemoApp::OnKey(OVR::KeyCode key, int chr, bool down, int modifiers)
+{
+ OVR_UNUSED(chr);
+
+ switch(key)
+ {
+ case Key_Q:
+ if (down && (modifiers & Mod_Control))
+ {
+ pPlatform->Exit(0);
+ }
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case Key_W:
+ ThePlayer.MoveForward = down ? (ThePlayer.MoveForward | 1) : (ThePlayer.MoveForward & ~1);
+ break;
+ case Key_S:
+ ThePlayer.MoveBack = down ? (ThePlayer.MoveBack | 1) : (ThePlayer.MoveBack & ~1);
+ break;
+ case Key_A:
+ ThePlayer.MoveLeft = down ? (ThePlayer.MoveLeft | 1) : (ThePlayer.MoveLeft & ~1);
+ break;
+ case Key_D:
+ ThePlayer.MoveRight = down ? (ThePlayer.MoveRight | 1) : (ThePlayer.MoveRight & ~1);
+ break;
+ case Key_Up:
+ ThePlayer.MoveForward = down ? (ThePlayer.MoveForward | 2) : (ThePlayer.MoveForward & ~2);
+ break;
+ case Key_Down:
+ ThePlayer.MoveBack = down ? (ThePlayer.MoveBack | 2) : (ThePlayer.MoveBack & ~2);
+ break;
+ case Key_Left:
+ ThePlayer.MoveLeft = down ? (ThePlayer.MoveLeft | 2) : (ThePlayer.MoveLeft & ~2);
+ break;
+ case Key_Right:
+ ThePlayer.MoveRight = down ? (ThePlayer.MoveRight | 2) : (ThePlayer.MoveRight & ~2);
+ break;
+
+ case Key_Minus:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
+ AdjustDirection = -1;
+ break;
+ case Key_Equal:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight : 0;
+ AdjustDirection = 1;
+ break;
+
+ case Key_B:
+ if (down)
+ {
+ if(SConfig.GetDistortionScale() == 1.0f)
+ {
+ if(SConfig.GetHMDInfo().HScreenSize > 0.140f) // 7"
+ {
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ }
+ else
+ {
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+ }
+ else
+ {
+ // No fitting; scale == 1.0.
+ SConfig.SetDistortionFitPointVP(0, 0);
+ }
+ }
+ break;
+
+ // Support toggling background color for distortion so that we can see
+ // the effect on the periphery.
+ case Key_V:
+ if (down)
+ {
+ if(DistortionClearColor.B == 0)
+ {
+ DistortionClearColor = Color(0, 128, 255);
+ }
+ else
+ {
+ DistortionClearColor = Color(0, 0, 0);
+ }
+
+ pRender->SetDistortionClearColor(DistortionClearColor);
+ }
+ break;
+
+
+ case Key_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ SetAdjustMessage("StereoMode: None");
+ break;
+ case Key_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ SetAdjustMessage("StereoMode: Stereo + No Distortion");
+ break;
+ case Key_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ SetAdjustMessage("StereoMode: Stereo + Distortion");
+ break;
+
+ case Key_R:
+ SFusion.Reset();
+ if (MagCal.IsAutoCalibrating() || MagCal.IsManuallyCalibrating())
+ MagCal.AbortCalibration();
+
+ SetAdjustMessage("Sensor Fusion Reset");
+ break;
+
+ case Key_Space:
+ if (!down)
+ {
+ TextScreen = (enum TextScreen)((TextScreen + 1) % Text_Count);
+ }
+ break;
+
+ case Key_F4:
+ if (!down)
+ {
+ RenderParams = pRender->GetParams();
+ RenderParams.Multisample = RenderParams.Multisample > 1 ? 1 : 4;
+ pRender->SetParams(RenderParams);
+ if(RenderParams.Multisample > 1)
+ {
+ SetAdjustMessage("Multisampling On");
+ }
+ else
+ {
+ SetAdjustMessage("Multisampling Off");
+ }
+ }
+ break;
+ case Key_F9:
+#ifndef OVR_OS_LINUX // On Linux F9 does the same as F11.
+ if (!down)
+ {
+ CycleDisplay();
+ }
+ break;
+#endif
+#ifdef OVR_OS_MAC
+ case Key_F10: // F11 is reserved on Mac
+#else
+ case Key_F11:
+#endif
+ if (!down)
+ {
+ RenderParams = pRender->GetParams();
+ RenderParams.Display = DisplayId(SConfig.GetHMDInfo().DisplayDeviceName,SConfig.GetHMDInfo().DisplayId);
+ pRender->SetParams(RenderParams);
+
+ pPlatform->SetMouseMode(Mouse_Normal);
+ pPlatform->SetFullscreen(RenderParams, pRender->IsFullscreen() ? Display_Window : Display_FakeFullscreen);
+ pPlatform->SetMouseMode(Mouse_Relative); // Avoid mode world rotation jump.
+ // If using an HMD, enable post-process (for distortion) and stereo.
+ if(RenderParams.IsDisplaySet() && pRender->IsFullscreen())
+ {
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ }
+ }
+ break;
+
+ case Key_Escape:
+ if(!down)
+ {
+ if (MagCal.IsAutoCalibrating() || MagCal.IsManuallyCalibrating())
+ {
+ MagCal.AbortCalibration();
+ SetAdjustMessage("Aborting Magnetometer Calibration");
+ }
+ else
+ {
+ // switch to primary screen windowed mode
+ pPlatform->SetFullscreen(RenderParams, Display_Window);
+ RenderParams.Display = pPlatform->GetDisplay(0);
+ pRender->SetParams(RenderParams);
+ Screen = 0;
+ }
+ }
+ break;
+
+ // Stereo adjustments.
+ case Key_BracketLeft:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_BracketRight:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov : 0;
+ AdjustDirection = -1;
+ break;
+
+ case Key_Insert:
+ case Key_Num0:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_Delete:
+ case Key_Num9:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD : 0;
+ AdjustDirection = -1;
+ break;
+
+ case Key_PageUp:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
+ AdjustDirection = 1;
+ break;
+ case Key_PageDown:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
+ AdjustDirection = -1;
+ break;
+
+ // Distortion correction adjustments
+ case Key_H:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_Y:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_J:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_U:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_K:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_I:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
+ AdjustDirection = 1;
+ break;
+ case Key_L:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
+ AdjustDirection = -1;
+ break;
+ case Key_O:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
+ AdjustDirection = 1;
+ break;
+
+ case Key_Tab:
+ if (down)
+ {
+ float t0 = SConfig.GetDistortionK(0),
+ t1 = SConfig.GetDistortionK(1),
+ t2 = SConfig.GetDistortionK(2),
+ t3 = SConfig.GetDistortionK(3);
+ float tESD = SConfig.GetEyeToScreenDistance(),
+ taspect = SConfig.GetAspectMultiplier(),
+ tipd = SConfig.GetIPD();
+
+ if(SavedK0 > 0.0f)
+ {
+ SConfig.SetDistortionK(0, SavedK0);
+ SConfig.SetDistortionK(1, SavedK1);
+ SConfig.SetDistortionK(2, SavedK2);
+ SConfig.SetDistortionK(3, SavedK3);
+ SConfig.SetEyeToScreenDistance(SavedESD);
+ SConfig.SetAspectMultiplier(SavedAspect);
+ SConfig.SetIPD(SavedEyeDistance);
+
+ if ( ShiftDown )
+ {
+ // Swap saved and current values. Good for doing direct comparisons.
+ SetAdjustMessage("Swapped current and saved. New settings:\n"
+ "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
+ "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
+ "K1: \t120 %.4f\t350 K3: \t490 %.4f\n",
+ SavedESD, SavedEyeDistance,
+ SavedK0, SavedK2,
+ SavedK1, SavedK3);
+ SavedK0 = t0;
+ SavedK1 = t1;
+ SavedK2 = t2;
+ SavedK3 = t3;
+ SavedESD = tESD;
+ SavedAspect = taspect;
+ SavedEyeDistance = tipd;
+ }
+ else
+ {
+ SetAdjustMessage("Restored:\n"
+ "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
+ "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
+ "K1: \t120 %.4f\t350 K3: \t490 %.4f\n",
+ SavedESD, SavedEyeDistance,
+ SavedK0, SavedK2,
+ SavedK1, SavedK3);
+ }
+ }
+ else
+ {
+ SetAdjustMessage("Setting Saved");
+ SavedK0 = t0;
+ SavedK1 = t1;
+ SavedK2 = t2;
+ SavedK3 = t3;
+ SavedESD = tESD;
+ SavedAspect = taspect;
+ SavedEyeDistance = tipd;
+ }
+
+ }
+ break;
+
+ case Key_G:
+ if (down)
+ {
+ if(SceneMode == Scene_World)
+ {
+ SceneMode = Scene_Grid;
+ SetAdjustMessage("Grid Only");
+ }
+ else if(SceneMode == Scene_Grid)
+ {
+ SceneMode = Scene_Both;
+ SetAdjustMessage("Grid Overlay");
+ }
+ else if(SceneMode == Scene_Both)
+ {
+ SceneMode = Scene_World;
+ SetAdjustMessage("Grid Off");
+ }
+ }
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case Key_Shift:
+ ShiftDown = down;
+ break;
+
+ // Reset the camera position in case we get stuck
+ case Key_T:
+ ThePlayer.EyePos = Vector3f(10.0f, 1.6f, 10.0f);
+ break;
+
+ case Key_F5:
+ if (!down)
+ {
+ UPInt numNodes = MainScene.Models.GetSize();
+ for(UPInt i = 0; i < numNodes; i++)
+ {
+ Ptr<OVR::Render::Model> nodePtr = MainScene.Models[i];
+ Render::Model* pNode = nodePtr.GetPtr();
+ if(pNode->IsCollisionModel)
+ {
+ pNode->Visible = !pNode->Visible;
+ }
+ }
+ }
+ break;
+
+ case Key_N:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
+ AdjustDirection = -1;
+ break;
+
+ case Key_M:
+ pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
+ AdjustDirection = 1;
+ break;
+
+/*
+ case Key_N:
+ RaiseLOD();
+ break;
+ case Key_M:
+ DropLOD();
+ break;
+*/
+ // Start calibrating magnetometer
+ case Key_Z:
+ if (down)
+ {
+ ManualMagCalStage = 0;
+ if (MagCal.IsManuallyCalibrating())
+ MagAwaitingForwardLook = false;
+ else
+ {
+ MagCal.BeginManualCalibration(SFusion);
+ MagAwaitingForwardLook = true;
+ }
+ }
+ break;
+
+ case Key_X:
+ if (down)
+ {
+ MagCal.BeginAutoCalibration(SFusion);
+ SetAdjustMessage("Starting Auto Mag Calibration");
+ }
+ break;
+
+ // Show view of yaw angles (for mag calibration/analysis)
+ case Key_F6:
+ if (down)
+ {
+ if (SceneMode != Scene_YawView)
+ {
+ SceneMode = Scene_YawView;
+ SetAdjustMessage("Magnetometer Yaw Angle Marks");
+ }
+ else
+ {
+ SceneMode = Scene_World;
+ SetAdjustMessage("Magnetometer Marks Off");
+ }
+ }
+ break;
+
+ case Key_C:
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ SetAdjustMessage("Chromatic Aberration Correction On");
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ SetAdjustMessage("Chromatic Aberration Correction Off");
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ case Key_P:
+ if (down)
+ {
+ // Toggle motion prediction.
+ if (SFusion.IsPredictionEnabled())
+ {
+ SFusion.SetPredictionEnabled(false);
+ SetAdjustMessage("Motion Prediction Off");
+ }
+ else
+ {
+ SFusion.SetPredictionEnabled(true);
+ SetAdjustMessage("Motion Prediction On");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void OculusWorldDemoApp::OnIdle()
+{
+
+ double curtime = pPlatform->GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+ // Update gamepad.
+ GamepadState gamepadState;
+ if (GetPlatformCore()->GetGamepadManager()->GetGamepadState(0, &gamepadState))
+ {
+ GamepadStateChanged(gamepadState);
+ }
+
+
+ if (LoadingState == LoadingState_DoLoad)
+ {
+ PopulateScene(MainFilePath.ToCStr());
+ LoadingState = LoadingState_Finished;
+ return;
+ }
+
+ // Check if any new devices were connected.
+ {
+ bool queueIsEmpty = false;
+ while (!queueIsEmpty)
+ {
+ DeviceStatusNotificationDesc desc;
+
+ {
+ Lock::Locker lock(pManager->GetHandlerLock());
+ if (DeviceStatusNotificationsQueue.GetSize() == 0)
+ break;
+ desc = DeviceStatusNotificationsQueue.Front();
+
+ // We can't call Clear under the lock since this may introduce a dead lock:
+ // this thread is locked by HandlerLock and the Clear might cause
+ // call of Device->Release, which will use Manager->DeviceLock. The bkg
+ // thread is most likely locked by opposite way:
+ // Manager->DeviceLock ==> HandlerLock, therefore - a dead lock.
+ // So, just grab the first element, save a copy of it and remove
+ // the element (Device->Release won't be called since we made a copy).
+
+ DeviceStatusNotificationsQueue.RemoveAt(0);
+ queueIsEmpty = (DeviceStatusNotificationsQueue.GetSize() == 0);
+ }
+
+ bool wasAlreadyCreated = desc.Handle.IsCreated();
+
+ if (desc.Action == Message_DeviceAdded)
+ {
+ switch(desc.Handle.GetType())
+ {
+ case Device_Sensor:
+ if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
+ {
+ if (!pSensor)
+ {
+ pSensor = *desc.Handle.CreateDeviceTyped<SensorDevice>();
+ SFusion.AttachToSensor(pSensor);
+ SetAdjustMessage("---------------------------\n"
+ "SENSOR connected\n"
+ "---------------------------");
+ }
+ else if (!wasAlreadyCreated)
+ {
+ LogText("A new SENSOR has been detected, but it is not currently used.");
+ }
+ }
+ break;
+ case Device_LatencyTester:
+ if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
+ {
+ if (!pLatencyTester)
+ {
+ pLatencyTester = *desc.Handle.CreateDeviceTyped<LatencyTestDevice>();
+ LatencyUtil.SetDevice(pLatencyTester);
+ if (!wasAlreadyCreated)
+ SetAdjustMessage("----------------------------------------\n"
+ "LATENCY TESTER connected\n"
+ "----------------------------------------");
+ }
+ }
+ break;
+ case Device_HMD:
+ {
+ OVR::HMDInfo info;
+ desc.Handle.GetDeviceInfo(&info);
+ // if strlen(info.DisplayDeviceName) == 0 then
+ // this HMD is 'fake' (created using sensor).
+ if (strlen(info.DisplayDeviceName) > 0 && (!pHMD || !info.IsSameDisplay(TheHMDInfo)))
+ {
+ SetAdjustMessage("------------------------\n"
+ "HMD connected\n"
+ "------------------------");
+ if (!pHMD || !desc.Handle.IsDevice(pHMD))
+ pHMD = *desc.Handle.CreateDeviceTyped<HMDDevice>();
+ // update stereo config with new HMDInfo
+ if (pHMD && pHMD->GetDeviceInfo(&TheHMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(TheHMDInfo);
+ }
+ LogText("HMD device added.\n");
+ }
+ break;
+ }
+ default:;
+ }
+ }
+ else if (desc.Action == Message_DeviceRemoved)
+ {
+ if (desc.Handle.IsDevice(pSensor))
+ {
+ LogText("Sensor reported device removed.\n");
+ SFusion.AttachToSensor(NULL);
+ pSensor.Clear();
+ SetAdjustMessage("-------------------------------\n"
+ "SENSOR disconnected.\n"
+ "-------------------------------");
+ }
+ else if (desc.Handle.IsDevice(pLatencyTester))
+ {
+ LogText("Latency Tester reported device removed.\n");
+ LatencyUtil.SetDevice(NULL);
+ pLatencyTester.Clear();
+ SetAdjustMessage("---------------------------------------------\n"
+ "LATENCY SENSOR disconnected.\n"
+ "---------------------------------------------");
+ }
+ else if (desc.Handle.IsDevice(pHMD))
+ {
+ if (pHMD && !pHMD->IsDisconnected())
+ {
+ SetAdjustMessage("---------------------------\n"
+ "HMD disconnected\n"
+ "---------------------------");
+ // Disconnect HMD. pSensor is used to restore 'fake' HMD device
+ // (can be NULL).
+ pHMD = pHMD->Disconnect(pSensor);
+
+ // This will initialize TheHMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD && pHMD->GetDeviceInfo(&TheHMDInfo))
+ {
+ //RenderParams.MonitorName = hmd.DisplayDeviceName;
+ SConfig.SetHMDInfo(TheHMDInfo);
+ }
+ LogText("HMD device removed.\n");
+ }
+ }
+ }
+ else
+ OVR_ASSERT(0); // unexpected action
+ }
+ }
+
+ // If one of Stereo setting adjustment keys is pressed, adjust related state.
+ if (pAdjustFunc)
+ {
+ (this->*pAdjustFunc)(dt * AdjustDirection * (ShiftDown ? 5.0f : 1.0f));
+ }
+
+ // Process latency tester results.
+ const char* results = LatencyUtil.GetResultsString();
+ if (results != NULL)
+ {
+ LogText("LATENCY TESTER: %s\n", results);
+ }
+
+ // Have to place this as close as possible to where the HMD orientation is read.
+ LatencyUtil.ProcessInputs();
+
+ // Magnetometer calibration procedure
+ if (MagCal.IsManuallyCalibrating())
+ UpdateManualMagCalibration();
+
+ if (MagCal.IsAutoCalibrating())
+ {
+ MagCal.UpdateAutoCalibration(SFusion);
+ int n = MagCal.NumberOfSamples();
+ if (n == 1)
+ SetAdjustMessage(" Magnetometer Calibration Has 1 Sample \n %d Remaining - Please Keep Looking Around ",4-n);
+ else if (n < 4)
+ SetAdjustMessage(" Magnetometer Calibration Has %d Samples \n %d Remaining - Please Keep Looking Around ",n,4-n);
+ if (MagCal.IsCalibrated())
+ {
+ SFusion.SetYawCorrectionEnabled(true);
+ Vector3f mc = MagCal.GetMagCenter();
+ SetAdjustMessage(" Magnetometer Calibration Complete \nCenter: %f %f %f",mc.x,mc.y,mc.z);
+ }
+ }
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if(pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetPredictedOrientation();
+
+ float yaw = 0.0f;
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &ThePlayer.EyePitch, &ThePlayer.EyeRoll);
+
+ ThePlayer.EyeYaw += (yaw - ThePlayer.LastSensorYaw);
+ ThePlayer.LastSensorYaw = yaw;
+
+ // NOTE: We can get a matrix from orientation as follows:
+ // Matrix4f hmdMat(hmdOrient);
+
+ // Test logic - assign quaternion result directly to view:
+ // Quatf hmdOrient = SFusion.GetOrientation();
+ // View = Matrix4f(hmdOrient.Inverted()) * Matrix4f::Translation(-EyePos);
+ }
+
+
+ if(curtime >= NextFPSUpdate)
+ {
+ NextFPSUpdate = curtime + 1.0;
+ FPS = FrameCounter;
+ FrameCounter = 0;
+ }
+ FrameCounter++;
+
+ if(FPS < 40)
+ {
+ ConsecutiveLowFPSFrames++;
+ }
+ else
+ {
+ ConsecutiveLowFPSFrames = 0;
+ }
+
+ if(ConsecutiveLowFPSFrames > 200)
+ {
+ DropLOD();
+ ConsecutiveLowFPSFrames = 0;
+ }
+
+ ThePlayer.EyeYaw -= ThePlayer.GamepadRotate.x * dt;
+ ThePlayer.HandleCollision(dt, &CollisionModels, &GroundCollisionModels, ShiftDown);
+
+ if(!pSensor)
+ {
+ ThePlayer.EyePitch -= ThePlayer.GamepadRotate.y * dt;
+
+ const float maxPitch = ((3.1415f / 2) * 0.98f);
+ if(ThePlayer.EyePitch > maxPitch)
+ {
+ ThePlayer.EyePitch = maxPitch;
+ }
+ if(ThePlayer.EyePitch < -maxPitch)
+ {
+ ThePlayer.EyePitch = -maxPitch;
+ }
+ }
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(ThePlayer.EyeYaw) * Matrix4f::RotationX(ThePlayer.EyePitch) *
+ Matrix4f::RotationZ(ThePlayer.EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modeling; should be moved as an option to SensorFusion.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = ThePlayer.EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+ View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // Transformation without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ // This is an alternative to LookAtRH:
+ // Here we transpose the rotation matrix to get its inverse.
+ // View = (Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ // Matrix4f::RotationZ(EyeRoll)).Transposed() *
+ // Matrix4f::Translation(-EyePos);
+
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ //case Stereo_LeftDouble_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+void OculusWorldDemoApp::UpdateManualMagCalibration()
+{
+ float tyaw, pitch, roll;
+ Anglef yaw;
+ Quatf hmdOrient = SFusion.GetOrientation();
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&tyaw, &pitch, &roll);
+ Vector3f mag = SFusion.GetMagnetometer();
+ float dtr = Math<float>::DegreeToRadFactor;
+ yaw.Set(tyaw); // Using Angle class to handle angle wraparound arithmetic
+
+ const int timeout = 100;
+
+ switch(ManualMagCalStage)
+ {
+ case 0:
+ if (MagAwaitingForwardLook)
+ SetAdjustMessage("Magnetometer Calibration\n** Step 1: Please Look Forward **\n** and Press Z When Ready **");
+ else
+ if (fabs(pitch) < 10.0f*dtr)
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ FirstMagYaw = yaw;
+ MagAwaitingForwardLook = false;
+ if (MagCal.NumberOfSamples() == 1)
+ {
+ ManualMagCalStage = 1;
+ ManualMagFailures = 0;
+ }
+ }
+ else
+ MagAwaitingForwardLook = true;
+ break;
+ case 1:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 2: Please Look Up **");
+ yaw -= FirstMagYaw;
+ if ((pitch > 50.0f*dtr) && (yaw.Abs() < 20.0f*dtr))
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ ManualMagFailures++;
+ if ((MagCal.NumberOfSamples() == 2)||(ManualMagFailures > timeout))
+ {
+ ManualMagCalStage = 2;
+ ManualMagFailures = 0;
+ }
+ }
+ break;
+ case 2:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 3: Please Look Left **");
+ yaw -= FirstMagYaw;
+ if (yaw.Get() > 60.0f*dtr)
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ ManualMagFailures++;
+ if ((MagCal.NumberOfSamples() == 3)||(ManualMagFailures > timeout))
+ {
+ ManualMagCalStage = 3;
+ ManualMagFailures = 0;
+ }
+ }
+ break;
+ case 3:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 4: Please Look Right **");
+ yaw -= FirstMagYaw;
+ if (yaw.Get() < -60.0f*dtr)
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ ManualMagFailures++;
+ if (MagCal.NumberOfSamples() == 4)
+ ManualMagCalStage = 6;
+ else
+ {
+ if (ManualMagFailures > timeout)
+ {
+ ManualMagCalStage = 4;
+ ManualMagFailures = 0;
+ }
+ }
+ }
+ break;
+ case 4:
+ SetAdjustMessage("Magnetometer Calibration\n** Step 5: Please Look Upper Right **");
+ yaw -= FirstMagYaw;
+ if ((yaw.Get() < -50.0f*dtr) && (pitch > 40.0f*dtr))
+ {
+ MagCal.InsertIfAcceptable(hmdOrient, mag);
+ if (MagCal.NumberOfSamples() == 4)
+ ManualMagCalStage = 6;
+ else
+ {
+ if (ManualMagFailures > timeout)
+ {
+ ManualMagCalStage = 5;
+ ManualMagFailures = 0;
+ }
+ else
+ ManualMagFailures++;
+ }
+ }
+ break;
+ case 5:
+ SetAdjustMessage("Calibration Failed\n** Try Again From Another Location **");
+ MagCal.AbortCalibration();
+ break;
+ case 6:
+ if (!MagCal.IsCalibrated())
+ {
+ MagCal.SetCalibration(SFusion);
+ SFusion.SetYawCorrectionEnabled(true);
+ Vector3f mc = MagCal.GetMagCenter();
+ SetAdjustMessage(" Magnetometer Calibration and Activation \nCenter: %f %f %f",
+ mc.x,mc.y,mc.z);
+ }
+ }
+}
+
+
+
+static const char* HelpText =
+ "F1 \t100 NoStereo \t420 Z \t520 Manual Mag Calib\n"
+ "F2 \t100 Stereo \t420 X \t520 Auto Mag Calib\n"
+ "F3 \t100 StereoHMD \t420 ; \t520 Mag Set Ref Point\n"
+ "F4 \t100 MSAA \t420 F6 \t520 Mag Info\n"
+ "F9 \t100 FullScreen \t420 R \t520 Reset SensorFusion\n"
+ "F11 \t100 Fast FullScreen \t500 - + \t660 Adj EyeHeight\n"
+ "C \t100 Chromatic Ab \t500 [ ] \t660 Adj FOV\n"
+ "P \t100 Motion Pred \t500 Shift \t660 Adj Faster\n"
+ "N/M \t180 Adj Motion Pred\n"
+ "( / ) \t180 Adj EyeDistance"
+ ;
+
+
+enum DrawTextCenterType
+{
+ DrawText_NoCenter= 0,
+ DrawText_VCenter = 0x1,
+ DrawText_HCenter = 0x2,
+ DrawText_Center = DrawText_VCenter | DrawText_HCenter
+};
+
+static void DrawTextBox(RenderDevice* prender, float x, float y,
+ float textSize, const char* text,
+ DrawTextCenterType centerType = DrawText_NoCenter)
+{
+ float ssize[2] = {0.0f, 0.0f};
+
+ prender->MeasureText(&DejaVu, text, textSize, ssize);
+
+ // Treat 0 a VCenter.
+ if (centerType & DrawText_HCenter)
+ {
+ x = -ssize[0]/2;
+ }
+ if (centerType & DrawText_VCenter)
+ {
+ y = -ssize[1]/2;
+ }
+
+ prender->FillRect(x-0.02f, y-0.02f, x+ssize[0]+0.02f, y+ssize[1]+0.02f, Color(40,40,100,210));
+ prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210));
+}
+
+void OculusWorldDemoApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // *** 3D - Configures Viewport/Projection and Render
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+
+ pRender->SetDepthMode(true, true);
+ if (SceneMode != Scene_Grid)
+ {
+ MainScene.Render(pRender, stereo.ViewAdjust * View);
+ }
+
+ if (SceneMode == Scene_YawView)
+ {
+ Matrix4f calView = Matrix4f();
+ float viewYaw = -ThePlayer.LastSensorYaw + SFusion.GetMagRefYaw();
+ calView.M[0][0] = calView.M[2][2] = cos(viewYaw);
+ calView.M[0][2] = sin(viewYaw);
+ calView.M[2][0] = -sin(viewYaw);
+ //LogText("yaw: %f\n",SFusion.GetMagRefYaw());
+
+ if (SFusion.IsYawCorrectionInProgress())
+ YawMarkGreenScene.Render(pRender, stereo.ViewAdjust);
+ else
+ YawMarkRedScene.Render(pRender, stereo.ViewAdjust);
+
+ if (fabs(ThePlayer.EyePitch) < Math<float>::Pi * 0.33)
+ YawLinesScene.Render(pRender, stereo.ViewAdjust * calView);
+ }
+
+ // *** 2D Text & Grid - Configure Orthographic rendering.
+
+ // Render UI in 2D orthographic coordinate system that maps [-1,1] range
+ // to a readable FOV area centered at your eye and properly adjusted.
+ pRender->ApplyStereoParams2D(stereo);
+ pRender->SetDepthMode(false, false);
+
+ float unitPixel = SConfig.Get2DUnitPixel();
+ float textHeight= unitPixel * 22;
+
+ if ((SceneMode == Scene_Grid)||(SceneMode == Scene_Both))
+ { // Draw grid two pixels thick.
+ GridScene.Render(pRender, Matrix4f());
+ GridScene.Render(pRender, Matrix4f::Translation(unitPixel,unitPixel,0));
+ }
+
+ // Display Loading screen-shot in frame 0.
+ if (LoadingState != LoadingState_Finished)
+ {
+ LoadingScene.Render(pRender, Matrix4f());
+ String loadMessage = String("Loading ") + MainFilePath;
+ DrawTextBox(pRender, 0.0f, 0.25f, textHeight, loadMessage.ToCStr(), DrawText_HCenter);
+ LoadingState = LoadingState_DoLoad;
+ }
+
+ if(!AdjustMessage.IsEmpty() && AdjustMessageTimeout > pPlatform->GetAppTime())
+ {
+ DrawTextBox(pRender,0.0f,0.4f, textHeight, AdjustMessage.ToCStr(), DrawText_HCenter);
+ }
+
+ switch(TextScreen)
+ {
+ case Text_Orientation:
+ {
+ char buf[256], gpustat[256];
+ OVR_sprintf(buf, sizeof(buf),
+ " Yaw:%4.0f Pitch:%4.0f Roll:%4.0f \n"
+ " FPS: %d Frame: %d \n Pos: %3.2f, %3.2f, %3.2f \n"
+ " EyeHeight: %3.2f",
+ RadToDegree(ThePlayer.EyeYaw), RadToDegree(ThePlayer.EyePitch), RadToDegree(ThePlayer.EyeRoll),
+ FPS, FrameCounter, ThePlayer.EyePos.x, ThePlayer.EyePos.y, ThePlayer.EyePos.z, ThePlayer.EyePos.y);
+ size_t texMemInMB = pRender->GetTotalTextureMemoryUsage() / 1058576;
+ if (texMemInMB)
+ {
+ OVR_sprintf(gpustat, sizeof(gpustat), "\n GPU Tex: %u MB", texMemInMB);
+ OVR_strcat(buf, sizeof(buf), gpustat);
+ }
+
+ DrawTextBox(pRender, 0.0f, -0.15f, textHeight, buf, DrawText_HCenter);
+ }
+ break;
+
+ case Text_Config:
+ {
+ char textBuff[2048];
+
+ OVR_sprintf(textBuff, sizeof(textBuff),
+ "Fov\t300 %9.4f\n"
+ "EyeDistance\t300 %9.4f\n"
+ "DistortionK0\t300 %9.4f\n"
+ "DistortionK1\t300 %9.4f\n"
+ "DistortionK2\t300 %9.4f\n"
+ "DistortionK3\t300 %9.4f\n"
+ "TexScale\t300 %9.4f",
+ SConfig.GetYFOVDegrees(),
+ SConfig.GetIPD(),
+ SConfig.GetDistortionK(0),
+ SConfig.GetDistortionK(1),
+ SConfig.GetDistortionK(2),
+ SConfig.GetDistortionK(3),
+ SConfig.GetDistortionScale());
+
+ DrawTextBox(pRender, 0.0f, 0.0f, textHeight, textBuff, DrawText_Center);
+ }
+ break;
+
+ case Text_Help:
+ DrawTextBox(pRender, 0.0f, -0.1f, textHeight, HelpText, DrawText_Center);
+
+ default:
+ break;
+ }
+
+
+ // Display colored quad if we're doing a latency test.
+ Color colorToDisplay;
+ if (LatencyUtil.DisplayScreenColor(colorToDisplay))
+ {
+ pRender->FillRect(-0.4f, -0.4f, 0.4f, 0.4f, colorToDisplay);
+ }
+
+ pRender->FinishScene();
+}
+
+
+// Sets temporarily displayed message for adjustments
+void OculusWorldDemoApp::SetAdjustMessage(const char* format, ...)
+{
+ Lock::Locker lock(pManager->GetHandlerLock());
+ char textBuff[2048];
+ va_list argList;
+ va_start(argList, format);
+ OVR_vsprintf(textBuff, sizeof(textBuff), format, argList);
+ va_end(argList);
+
+ // Message will time out in 4 seconds.
+ AdjustMessage = textBuff;
+ AdjustMessageTimeout = pPlatform->GetAppTime() + 4.0f;
+}
+
+void OculusWorldDemoApp::SetAdjustMessageTimeout(float timeout)
+{
+ AdjustMessageTimeout = pPlatform->GetAppTime() + timeout;
+}
+
+// ***** View Control Adjustments
+
+void OculusWorldDemoApp::AdjustFov(float dt)
+{
+ float esd = SConfig.GetEyeToScreenDistance() + 0.01f * dt;
+ SConfig.SetEyeToScreenDistance(esd);
+ SetAdjustMessage("ESD:%6.3f FOV: %6.3f", esd, SConfig.GetYFOVDegrees());
+}
+
+void OculusWorldDemoApp::AdjustAspect(float dt)
+{
+ float rawAspect = SConfig.GetAspect() / SConfig.GetAspectMultiplier();
+ float newAspect = SConfig.GetAspect() + 0.01f * dt;
+ SConfig.SetAspectMultiplier(newAspect / rawAspect);
+ SetAdjustMessage("Aspect: %6.3f", newAspect);
+}
+
+void OculusWorldDemoApp::AdjustDistortion(float dt, int kIndex, const char* label)
+{
+ SConfig.SetDistortionK(kIndex, SConfig.GetDistortionK(kIndex) + 0.03f * dt);
+ SetAdjustMessage("%s: %6.4f", label, SConfig.GetDistortionK(kIndex));
+}
+
+void OculusWorldDemoApp::AdjustIPD(float dt)
+{
+ SConfig.SetIPD(SConfig.GetIPD() + 0.025f * dt);
+ SetAdjustMessage("EyeDistance: %6.4f", SConfig.GetIPD());
+}
+
+void OculusWorldDemoApp::AdjustEyeHeight(float dt)
+{
+ float dist = 0.5f * dt;
+
+ ThePlayer.EyeHeight += dist;
+ ThePlayer.EyePos.y += dist;
+
+ SetAdjustMessage("EyeHeight: %4.2f", ThePlayer.EyeHeight);
+}
+
+void OculusWorldDemoApp::AdjustMotionPrediction(float dt)
+{
+ float motionPred = SFusion.GetPredictionDelta() + 0.01f * dt;
+
+ if (motionPred < 0.0f)
+ {
+ motionPred = 0.0f;
+ }
+
+ SFusion.SetPrediction(motionPred);
+
+ SetAdjustMessage("MotionPrediction: %6.3fs", motionPred);
+}
+
+
+// Loads the scene data
+void OculusWorldDemoApp::PopulateScene(const char *fileName)
+{
+ XmlHandler xmlHandler;
+ if(!xmlHandler.ReadFile(fileName, pRender, &MainScene, &CollisionModels, &GroundCollisionModels))
+ {
+ SetAdjustMessage("---------------------------------\nFILE LOAD FAILED\n---------------------------------");
+ SetAdjustMessageTimeout(10.0f);
+ }
+
+ MainScene.SetAmbient(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
+
+ // Distortion debug grid (brought up by 'G' key).
+ Ptr<Model> gridModel = *Model::CreateGrid(Vector3f(0,0,0), Vector3f(1.0f/10, 0,0), Vector3f(0,1.0f/10,0),
+ 10, 10, 5,
+ Color(0, 255, 0, 255), Color(255, 50, 50, 255) );
+ GridScene.World.Add(gridModel);
+
+ // Yaw angle marker and lines (brought up by ';' key).
+ float shifty = -0.5f;
+ Ptr<Model> yawMarkGreenModel = *Model::CreateBox(Color(0, 255, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
+ YawMarkGreenScene.World.Add(yawMarkGreenModel);
+ Ptr<Model> yawMarkRedModel = *Model::CreateBox(Color(255, 0, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
+ YawMarkRedScene.World.Add(yawMarkRedModel);
+
+ Ptr<Model> yawLinesModel = *new Model(Prim_Lines);
+ float r = 2.0f;
+ float theta0 = Math<float>::PiOver2;
+ float theta1 = 0.0f;
+ Color c = Color(255, 200, 200, 255);
+ for (int i = 0; i < 35; i++)
+ {
+ theta1 = theta0 + Math<float>::Pi / 18.0f;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
+ theta0 = theta1;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty+0.1f,-r*sin(theta0)),c));
+ theta0 = theta1;
+ }
+ theta1 = theta0 + Math<float>::Pi / 18.0f;
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
+ yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
+ yawLinesModel->AddVertex(Vector3f(r*sin(0.02f),shifty,-r*cos(0.02f)),c));
+ yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
+ yawLinesModel->AddVertex(Vector3f(r*sin(-0.02f),shifty,-r*cos(-0.02f)),c));
+ yawLinesModel->SetPosition(Vector3f(0.0f,0.0f,0.0f));
+
+ YawLinesScene.World.Add(yawLinesModel);
+}
+
+
+void OculusWorldDemoApp::PopulatePreloadScene()
+{
+ // Load-screen screen shot image
+ String fileName = MainFilePath;
+ fileName.StripExtension();
+
+ Ptr<File> imageFile = *new SysFile(fileName + "_LoadScreen.tga");
+ Ptr<Texture> imageTex;
+ if (imageFile->IsValid())
+ imageTex = *LoadTextureTga(pRender, imageFile);
+
+ // Image is rendered as a single quad.
+ if (imageTex)
+ {
+ imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ Ptr<Model> m = *new Model(Prim_Triangles);
+ m->AddVertex(-0.5f, 0.5f, 0.0f, Color(255,255,255,255), 0.0f, 0.0f);
+ m->AddVertex( 0.5f, 0.5f, 0.0f, Color(255,255,255,255), 1.0f, 0.0f);
+ m->AddVertex( 0.5f, -0.5f, 0.0f, Color(255,255,255,255), 1.0f, 1.0f);
+ m->AddVertex(-0.5f, -0.5f, 0.0f, Color(255,255,255,255), 0.0f, 1.0f);
+ m->AddTriangle(2,1,0);
+ m->AddTriangle(0,3,2);
+
+ Ptr<ShaderFill> fill = *new ShaderFill(*pRender->CreateShaderSet());
+ fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture));
+ fill->SetTexture(0, imageTex);
+ m->Fill = fill;
+
+ LoadingScene.World.Add(m);
+ }
+}
+
+void OculusWorldDemoApp::ClearScene()
+{
+ MainScene.Clear();
+ GridScene.Clear();
+ YawMarkGreenScene.Clear();
+ YawMarkRedScene.Clear();
+ YawLinesScene.Clear();
+}
+
+void OculusWorldDemoApp::PopulateLODFileNames()
+{
+ //OVR::String mainFilePath = MainFilePath;
+ LODFilePaths.PushBack(MainFilePath);
+ int LODIndex = 1;
+ SPInt pos = strcspn(MainFilePath.ToCStr(), ".");
+ SPInt len = strlen(MainFilePath.ToCStr());
+ SPInt diff = len - pos;
+
+ if (diff == 0)
+ return;
+
+ while(true)
+ {
+ char pathWithoutExt[250];
+ char buffer[250];
+ for(SPInt i = 0; i < pos; ++i)
+ {
+ pathWithoutExt[i] = MainFilePath[(int)i];
+ }
+ pathWithoutExt[pos] = '\0';
+ OVR_sprintf(buffer, sizeof(buffer), "%s%i.xml", pathWithoutExt, LODIndex);
+ FILE* fp = 0;
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ errno_t err = fopen_s(&fp, buffer, "rb");
+ if(!fp || err)
+ {
+#else
+ fp = fopen(buffer, "rb");
+ if(!fp)
+ {
+#endif
+ break;
+ }
+ fclose(fp);
+ OVR::String result = buffer;
+ LODFilePaths.PushBack(result);
+ LODIndex++;
+ }
+}
+
+void OculusWorldDemoApp::DropLOD()
+{
+ if(CurrentLODFileIndex < (int)(LODFilePaths.GetSize() - 1))
+ {
+ ClearScene();
+ CurrentLODFileIndex++;
+ PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
+ }
+}
+
+void OculusWorldDemoApp::RaiseLOD()
+{
+ if(CurrentLODFileIndex > 0)
+ {
+ ClearScene();
+ CurrentLODFileIndex--;
+ PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
+ }
+}
+
+//-----------------------------------------------------------------------------
+void OculusWorldDemoApp::CycleDisplay()
+{
+ int screenCount = pPlatform->GetDisplayCount();
+
+ // If Windowed, switch to the HMD screen first in Full-Screen Mode.
+ // If already Full-Screen, cycle to next screen until we reach FirstScreenInCycle.
+
+ if (pRender->IsFullscreen())
+ {
+ // Right now, we always need to restore window before going to next screen.
+ pPlatform->SetFullscreen(RenderParams, Display_Window);
+
+ Screen++;
+ if (Screen == screenCount)
+ Screen = 0;
+
+ RenderParams.Display = pPlatform->GetDisplay(Screen);
+
+ if (Screen != FirstScreenInCycle)
+ {
+ pRender->SetParams(RenderParams);
+ pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
+ }
+ }
+ else
+ {
+ // Try to find HMD Screen, making it the first screen in full-screen Cycle.
+ FirstScreenInCycle = 0;
+
+ if (pHMD)
+ {
+ DisplayId HMD (SConfig.GetHMDInfo().DisplayDeviceName, SConfig.GetHMDInfo().DisplayId);
+ for (int i = 0; i< screenCount; i++)
+ {
+ if (pPlatform->GetDisplay(i) == HMD)
+ {
+ FirstScreenInCycle = i;
+ break;
+ }
+ }
+ }
+
+ // Switch full-screen on the HMD.
+ Screen = FirstScreenInCycle;
+ RenderParams.Display = pPlatform->GetDisplay(Screen);
+ pRender->SetParams(RenderParams);
+ pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
+ }
+}
+
+void OculusWorldDemoApp::GamepadStateChanged(const GamepadState& pad)
+{
+ ThePlayer.GamepadMove = Vector3f(pad.LX * pad.LX * (pad.LX > 0 ? 1 : -1),
+ 0,
+ pad.LY * pad.LY * (pad.LY > 0 ? -1 : 1));
+ ThePlayer.GamepadRotate = Vector3f(2 * pad.RX, -2 * pad.RY, 0);
+}
+
+
+//-------------------------------------------------------------------------------------
+
+OVR_PLATFORM_APP(OculusWorldDemoApp);