From ebefcc885f74461cd0e3f19b5ae3622dc6cf6dbc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Jun 2013 11:25:32 -0800 Subject: SDK 0.2.2 --- Samples/OculusWorldDemo/OculusWorldDemo.cpp | 1836 +++++++++++++++++++++++++++ 1 file changed, 1836 insertions(+) create mode 100644 Samples/OculusWorldDemo/OculusWorldDemo.cpp (limited to 'Samples/OculusWorldDemo/OculusWorldDemo.cpp') diff --git a/Samples/OculusWorldDemo/OculusWorldDemo.cpp b/Samples/OculusWorldDemo/OculusWorldDemo.cpp new file mode 100644 index 0000000..18e614c --- /dev/null +++ b/Samples/OculusWorldDemo/OculusWorldDemo.cpp @@ -0,0 +1,1836 @@ +/************************************************************************************ + +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 +#include +#include + +// 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 pManager; + Ptr pSensor; + Ptr pHMD; + SensorFusion SFusion; + HMDInfo HMDInfo; + + Ptr pLatencyTester; + Util::LatencyTest LatencyUtil; + + double LastUpdate; + int FPS; + int FrameCounter; + double NextFPSUpdate; + + Array > CollisionModels; + Array > 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 LitSolid, LitTextures[4]; + + // Stereo view parameters. + StereoConfig SConfig; + PostProcessType PostProcess; + + // LOD + String MainFilePath; + Array 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 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().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().CreateDevice(); + } + + // Create the Latency Tester device and assign it to the LatencyTesterUtil object. + pLatencyTester = *pManager->EnumerateDevices().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(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 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(); + 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(); + 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(); + // 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(&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(&pitch, &roll, &yaw); + hmdOrient.GetEulerAngles(&tyaw, &roll, &pitch); + Vector3f mag = SFusion.GetMagnetometer(); + float dtr = Math::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::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::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::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::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::Pi) + yaw += Math::TwoPi; + if (yaw > Math::Pi) + yaw -= Math::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::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 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 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 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 yawLinesModel = *new Model(Prim_Lines); + float r = 2.0f; + float theta0 = Math::PiOver2; + float theta1 = 0.0f; + Color c = Color(255, 200, 200, 255); + for (int i = 0; i < 35; i++) + { + theta1 = theta0 + Math::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::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 imageFile = *new SysFile(fileName + "_LoadScreen.tga"); + Ptr imageTex; + if (imageFile->IsValid()) + imageTex = *LoadTextureTga(pRender, imageFile); + + // Image is rendered as a single quad. + if (imageTex) + { + imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat); + Ptr 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 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); -- cgit v1.2.3