OculusWorldDemo.cpp
Go to the documentation of this file.
00001 /************************************************************************************
00002 
00003 Filename    :   OculusWorldDemo.cpp
00004 Content     :   First-person view test application for Oculus Rift
00005 Created     :   October 4, 2012
00006 Authors     :   Michael Antonov, Andrew Reisse, Steve LaValle
00007                                 Peter Hoff, Dan Goodman, Bryan Croteau
00008 
00009 Copyright   :   Copyright 2012 Oculus VR, Inc. All Rights reserved.
00010 
00011 Licensed under the Apache License, Version 2.0 (the "License");
00012 you may not use this file except in compliance with the License.
00013 You may obtain a copy of the License at
00014 
00015 http://www.apache.org/licenses/LICENSE-2.0
00016 
00017 Unless required by applicable law or agreed to in writing, software
00018 distributed under the License is distributed on an "AS IS" BASIS,
00019 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020 See the License for the specific language governing permissions and
00021 limitations under the License.
00022 
00023 *************************************************************************************/
00024 
00025 #include "OVR.h"
00026 
00027 #include "../CommonSrc/Platform/Platform_Default.h"
00028 #include "../CommonSrc/Render/Render_Device.h"
00029 #include "../CommonSrc/Render/Render_XmlSceneLoader.h"
00030 #include "../CommonSrc/Render/Render_FontEmbed_DejaVu48.h"
00031 #include "../CommonSrc/Platform/Gamepad.h"
00032 
00033 #include <Kernel/OVR_SysFile.h>
00034 #include <Kernel/OVR_Log.h>
00035 #include <Kernel/OVR_Timer.h>
00036 
00037 #include "Player.h"
00038 
00039 // Filename to be loaded by default, searching specified paths.
00040 #define WORLDDEMO_ASSET_FILE  "Tuscany.xml"
00041 #define WORLDDEMO_ASSET_PATH1 "Assets/Tuscany/"
00042 #define WORLDDEMO_ASSET_PATH2 "../Assets/Tuscany/"
00043 // This path allows the shortcut to work.
00044 #define WORLDDEMO_ASSET_PATH3 "Samples/OculusWorldDemo/Assets/Tuscany/"
00045 
00046 
00047 using namespace OVR;
00048 using namespace OVR::Platform;
00049 using namespace OVR::Render;
00050 
00051 //-------------------------------------------------------------------------------------
00052 // ***** OculusWorldDemo Description
00053 
00054 // This app renders a simple flat-shaded room allowing the user to move along the
00055 // floor and look around with an HMD, mouse and keyboard. The following keys work:
00056 //
00057 //  'W', 'S', 'A', 'D' and Arrow Keys - Move forward, back; strafe left/right.
00058 //  F1 - No stereo, no distortion.
00059 //  F2 - Stereo, no distortion.
00060 //  F3 - Stereo and distortion.
00061 //  F4 - Toggle MSAA.
00062 //  F9 - Cycle through fullscreen and windowed modes. Necessary for previewing content with Rift.
00063 //
00064 // Important Oculus-specific logic can be found at following locations:
00065 //
00066 //  OculusWorldDemoApp::OnStartup - This function will initialize OVR::DeviceManager and HMD,
00067 //                                                                      creating SensorDevice and attaching it to SensorFusion.
00068 //                                                                      This needs to be done before obtaining sensor data.
00069 //
00070 //  OculusWorldDemoApp::OnIdle    - Here we poll SensorFusion for orientation, apply it
00071 //                                                                      to the scene and handle movement.
00072 //                                                                      Stereo rendering is also done here, by delegating to
00073 //                                                                      to Render function for each eye.
00074 //
00075 
00076 //-------------------------------------------------------------------------------------
00077 // ***** OculusWorldDemo Application class
00078 
00079 // An instance of this class is created on application startup (main/WinMain).
00080 // It then works as follows:
00081 //  - Graphics and HMD setup is done OculusWorldDemoApp::OnStartup(). This function
00082 //    also creates the room model from Slab declarations.
00083 //  - Per-frame processing is done in OnIdle(). This function processes
00084 //    sensor and movement input and then renders the frame.
00085 //  - Additional input processing is done in OnMouse, OnKey.
00086 
00087 class OculusWorldDemoApp : public Application, public MessageHandler
00088 {
00089 public:
00090     OculusWorldDemoApp();
00091     ~OculusWorldDemoApp();
00092 
00093     virtual int  OnStartup(int argc, const char** argv);
00094     virtual void OnIdle();
00095 
00096     virtual void OnMouseMove(int x, int y, int modifiers);
00097     virtual void OnKey(OVR::KeyCode key, int chr, bool down, int modifiers);
00098     virtual void OnResize(int width, int height);
00099 
00100     virtual void OnMessage(const Message& msg);
00101 
00102     void         Render(const StereoEyeParams& stereo);
00103 
00104     // Sets temporarily displayed message for adjustments
00105     void         SetAdjustMessage(const char* format, ...);
00106     // Overrides current timeout, in seconds (not the future default value);
00107     // intended to be called right after SetAdjustMessage.
00108     void         SetAdjustMessageTimeout(float timeout);
00109 
00110     // Stereo setting adjustment functions.
00111     // Called with deltaTime when relevant key is held.
00112     void         AdjustFov(float dt);
00113     void         AdjustAspect(float dt);
00114     void         AdjustIPD(float dt);
00115     void         AdjustEyeHeight(float dt);
00116 
00117     void         AdjustMotionPrediction(float dt);
00118 
00119     void         AdjustDistortion(float dt, int kIndex, const char* label);
00120     void         AdjustDistortionK0(float dt)  { AdjustDistortion(dt, 0, "K0"); }
00121     void         AdjustDistortionK1(float dt)  { AdjustDistortion(dt, 1, "K1"); }
00122     void         AdjustDistortionK2(float dt)  { AdjustDistortion(dt, 2, "K2"); }
00123     void         AdjustDistortionK3(float dt)  { AdjustDistortion(dt, 3, "K3"); }
00124 
00125     // Adds room model to scene.
00126     void         PopulateScene(const char* fileName);
00127     void         PopulatePreloadScene();
00128     void                 ClearScene();
00129 
00130     // Magnetometer calibration procedure
00131     void         UpdateManualMagCalibration();
00132 
00133 protected:
00134     RenderDevice*       pRender;
00135     RendererParams      RenderParams;
00136     int                 Width, Height;
00137     int                 Screen;
00138     int                 FirstScreenInCycle;
00139 
00140     // Magnetometer calibration and yaw correction
00141     Util::MagCalibration      MagCal;
00142         bool                              MagAwaitingForwardLook;
00143 
00144     // *** Oculus HMD Variables
00145     Ptr<DeviceManager>  pManager;
00146     Ptr<SensorDevice>   pSensor;
00147     Ptr<HMDDevice>      pHMD;
00148     Ptr<Profile>        pUserProfile;
00149     SensorFusion        SFusion;
00150     HMDInfo             TheHMDInfo;
00151 
00152     Ptr<LatencyTestDevice>  pLatencyTester;
00153     Util::LatencyTest   LatencyUtil;
00154 
00155     double              LastUpdate;
00156     int                 FPS;
00157     int                 FrameCounter;
00158     double              NextFPSUpdate;
00159 
00160     Array<Ptr<CollisionModel> > CollisionModels;
00161     Array<Ptr<CollisionModel> > GroundCollisionModels;
00162 
00163     // Loading process displays screenshot in first frame
00164     // and then proceeds to load until finished.
00165     enum LoadingStateType
00166     {
00167         LoadingState_Frame0,
00168         LoadingState_DoLoad,
00169         LoadingState_Finished
00170     };
00171 
00172         // Player
00173     Player                              ThePlayer;
00174     Matrix4f            View;
00175     Scene               MainScene;
00176     Scene               LoadingScene;
00177     Scene               GridScene;
00178     Scene               YawMarkGreenScene;
00179     Scene               YawMarkRedScene;
00180     Scene               YawLinesScene;
00181 
00182     LoadingStateType    LoadingState;
00183 
00184     Ptr<ShaderFill>     LitSolid, LitTextures[4];
00185 
00186     // Stereo view parameters.
00187     StereoConfig        SConfig;
00188     PostProcessType     PostProcess;
00189 
00190     // LOD
00191     String                  MainFilePath;
00192     Array<String>       LODFilePaths;
00193     int                                 ConsecutiveLowFPSFrames;
00194     int                                 CurrentLODFileIndex;
00195 
00196     float               DistortionK0;
00197     float               DistortionK1;
00198     float               DistortionK2;
00199     float               DistortionK3;
00200 
00201     String              AdjustMessage;
00202     double              AdjustMessageTimeout;
00203 
00204     // Saved distortion state.
00205     float               SavedK0, SavedK1, SavedK2, SavedK3;
00206     float               SavedESD, SavedAspect, SavedEyeDistance;
00207 
00208     // Allows toggling color around distortion.
00209     Color               DistortionClearColor;
00210 
00211     // Stereo settings adjustment state.
00212     typedef void (OculusWorldDemoApp::*AdjustFuncType)(float);
00213     bool                ShiftDown;
00214     AdjustFuncType      pAdjustFunc;
00215     float               AdjustDirection;
00216 
00217     enum SceneRenderMode
00218     {
00219         Scene_World,
00220         Scene_Grid,
00221         Scene_Both,
00222         Scene_YawView
00223     };
00224     SceneRenderMode    SceneMode;
00225 
00226 
00227     enum TextScreen
00228     {
00229         Text_None,
00230         Text_Orientation,
00231         Text_Config,
00232         Text_Help,
00233         Text_Count
00234     };
00235     TextScreen          TextScreen;
00236 
00237     struct DeviceStatusNotificationDesc
00238     {
00239         DeviceHandle    Handle;
00240         MessageType     Action;
00241 
00242         DeviceStatusNotificationDesc():Action(Message_None) {}
00243         DeviceStatusNotificationDesc(MessageType mt, const DeviceHandle& dev) 
00244             : Handle(dev), Action(mt) {}
00245     };
00246     Array<DeviceStatusNotificationDesc> DeviceStatusNotificationsQueue; 
00247 
00248 
00249     Model* CreateModel(Vector3f pos, struct SlabModel* sm);
00250     Model* CreateBoundingModel(CollisionModel &cm);
00251     void PopulateLODFileNames();
00252     void DropLOD();
00253     void RaiseLOD();
00254     void CycleDisplay();
00255     void GamepadStateChanged(const GamepadState& pad);
00256 
00257         // Variable used by UpdateManualCalibration
00258     Anglef FirstMagYaw;
00259         int ManualMagCalStage;
00260         int ManualMagFailures;
00261 };
00262 
00263 //-------------------------------------------------------------------------------------
00264 
00265 OculusWorldDemoApp::OculusWorldDemoApp()
00266     : pRender(0),
00267       LastUpdate(0),
00268       LoadingState(LoadingState_Frame0),
00269       // Initial location
00270       SConfig(),
00271       PostProcess(PostProcess_Distortion),
00272       DistortionClearColor(0, 0, 0),
00273 
00274       ShiftDown(false),
00275       pAdjustFunc(0),
00276       AdjustDirection(1.0f),
00277       SceneMode(Scene_World),
00278       TextScreen(Text_None)
00279 {
00280     Width  = 1280;
00281     Height = 800;
00282     Screen = 0;
00283     FirstScreenInCycle = 0;
00284 
00285     FPS = 0;
00286     FrameCounter = 0;
00287     NextFPSUpdate = 0;
00288 
00289     ConsecutiveLowFPSFrames = 0;
00290     CurrentLODFileIndex = 0;
00291 
00292     AdjustMessageTimeout = 0;
00293 }
00294 
00295 OculusWorldDemoApp::~OculusWorldDemoApp()
00296 {
00297     RemoveHandlerFromDevices();
00298 
00299     if(DejaVu.fill)
00300     {
00301         DejaVu.fill->Release();
00302     }
00303     pLatencyTester.Clear();
00304     pSensor.Clear();
00305     pHMD.Clear();
00306     
00307         CollisionModels.ClearAndRelease();
00308         GroundCollisionModels.ClearAndRelease();
00309 }
00310 
00311 int OculusWorldDemoApp::OnStartup(int argc, const char** argv)
00312 {
00313 
00314     // *** Oculus HMD & Sensor Initialization
00315 
00316     // Create DeviceManager and first available HMDDevice from it.
00317     // Sensor object is created from the HMD, to ensure that it is on the
00318     // correct device.
00319 
00320     pManager = *DeviceManager::Create();
00321 
00322     // We'll handle it's messages in this case.
00323     pManager->SetMessageHandler(this);
00324     
00325 
00326     pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
00327     if (pHMD)
00328     {
00329         pSensor = *pHMD->GetSensor();
00330 
00331         // This will initialize HMDInfo with information about configured IPD,
00332         // screen size and other variables needed for correct projection.
00333         // We pass HMD DisplayDeviceName into the renderer to select the
00334         // correct monitor in full-screen mode.
00335         if(pHMD->GetDeviceInfo(&TheHMDInfo))
00336         {
00337             //RenderParams.MonitorName = hmd.DisplayDeviceName;
00338             SConfig.SetHMDInfo(TheHMDInfo);
00339         }
00340 
00341         // Retrieve relevant profile settings. 
00342         pUserProfile = pHMD->GetProfile();
00343         if (pUserProfile)
00344         {
00345             ThePlayer.EyeHeight = pUserProfile->GetEyeHeight();
00346             ThePlayer.EyePos.y = ThePlayer.EyeHeight;
00347         }
00348     }
00349     else
00350     {
00351         // If we didn't detect an HMD, try to create the sensor directly.
00352         // This is useful for debugging sensor interaction; it is not needed in
00353         // a shipping app.
00354         pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
00355     }
00356         
00357     // Create the Latency Tester device and assign it to the LatencyTesterUtil object.
00358     pLatencyTester = *pManager->EnumerateDevices<LatencyTestDevice>().CreateDevice();
00359     if (pLatencyTester)
00360     {
00361         LatencyUtil.SetDevice(pLatencyTester);
00362     }
00363     // Make the user aware which devices are present.
00364     if(pHMD == NULL && pSensor == NULL)
00365     {
00366         SetAdjustMessage("---------------------------------\nNO HMD DETECTED\nNO SENSOR DETECTED\n---------------------------------");
00367     }
00368     else if(pHMD == NULL)
00369     {
00370         SetAdjustMessage("----------------------------\nNO HMD DETECTED\n----------------------------");
00371     }
00372     else if(pSensor == NULL)
00373     {
00374         SetAdjustMessage("---------------------------------\nNO SENSOR DETECTED\n---------------------------------");
00375     }
00376     else
00377     {
00378         SetAdjustMessage("--------------------------------------------\n"
00379                          "Press F9 for Full-Screen on Rift\n"
00380                          "--------------------------------------------");
00381     }
00382 
00383     // First message should be extra-long.
00384     SetAdjustMessageTimeout(10.0f);
00385 
00386 
00387     if(TheHMDInfo.HResolution > 0)
00388     {
00389         Width  = TheHMDInfo.HResolution;
00390         Height = TheHMDInfo.VResolution;
00391     }
00392 
00393     if(!pPlatform->SetupWindow(Width, Height))
00394     {
00395         return 1;
00396     }
00397 
00398     String Title = "Oculus World Demo";
00399     if(TheHMDInfo.ProductName[0])
00400     {
00401         Title += " : ";
00402         Title += TheHMDInfo.ProductName;
00403     }
00404     pPlatform->SetWindowTitle(Title);
00405 
00406     // Report relative mouse motion in OnMouseMove
00407     pPlatform->SetMouseMode(Mouse_Relative);
00408     
00409     if(pSensor)
00410     {
00411         // We need to attach sensor to SensorFusion object for it to receive
00412         // body frame messages and update orientation. SFusion.GetOrientation()
00413         // is used in OnIdle() to orient the view.
00414         SFusion.AttachToSensor(pSensor);
00415 
00416         SFusion.SetDelegateMessageHandler(this);
00417 
00418         SFusion.SetPredictionEnabled(true);
00419     }
00420 
00421 
00422     // *** Initialize Rendering
00423 
00424     const char* graphics = "d3d11";
00425 
00426     // Select renderer based on command line arguments.
00427     for(int i = 1; i < argc; i++)
00428     {
00429         if(!strcmp(argv[i], "-r") && i < argc - 1)
00430         {
00431             graphics = argv[i + 1];
00432         }
00433         else if(!strcmp(argv[i], "-fs"))
00434         {
00435             RenderParams.Fullscreen = true;
00436         }
00437     }
00438 
00439     // Enable multi-sampling by default.
00440     RenderParams.Multisample = 4;
00441     pRender = pPlatform->SetupGraphics(OVR_DEFAULT_RENDER_DEVICE_SET,
00442                                        graphics, RenderParams);
00443 
00444 
00445 
00446     // *** Configure Stereo settings.
00447 
00448     SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
00449     SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
00450 
00451     // Configure proper Distortion Fit.
00452     // For 7" screen, fit to touch left side of the view, leaving a bit of
00453     // invisible screen on the top (saves on rendering cost).
00454     // For smaller screens (5.5"), fit to the top.
00455     if (TheHMDInfo.HScreenSize > 0.0f)
00456     {
00457         if (TheHMDInfo.HScreenSize > 0.140f)  // 7"
00458             SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);        
00459         else        
00460             SConfig.SetDistortionFitPointVP(0.0f, 1.0f);        
00461     }
00462 
00463     pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
00464     //pRender->SetSceneRenderScale(1.0f);
00465 
00466     SConfig.Set2DAreaFov(DegreeToRad(85.0f));
00467 
00468 
00469     // *** Identify Scene File & Prepare for Loading
00470    
00471     // This creates lights and models.
00472     if (argc == 2)
00473     {        
00474         MainFilePath = argv[1];
00475         PopulateLODFileNames();
00476     }
00477     else
00478     {
00479         fprintf(stderr, "Usage: OculusWorldDemo [input XML]\n");
00480         exit(1);
00481     }
00482 
00483     // Try to modify path for correctness in case specified file is not found.
00484     if (!SysFile(MainFilePath).IsValid())
00485     {
00486         String prefixPath1(pPlatform->GetContentDirectory() + "/" + WORLDDEMO_ASSET_PATH1),
00487                prefixPath2(WORLDDEMO_ASSET_PATH2),
00488                prefixPath3(WORLDDEMO_ASSET_PATH3);
00489         if (SysFile(prefixPath1 + MainFilePath).IsValid())
00490             MainFilePath = prefixPath1 + MainFilePath;
00491         else if (SysFile(prefixPath2 + MainFilePath).IsValid())
00492             MainFilePath = prefixPath2 + MainFilePath;
00493         else if (SysFile(prefixPath3 + MainFilePath).IsValid())
00494             MainFilePath = prefixPath3 + MainFilePath;
00495     }
00496 
00497     PopulatePreloadScene();
00498 
00499     LastUpdate = pPlatform->GetAppTime();
00500         //pPlatform->PlayMusicFile(L"Loop.wav");
00501     
00502     return 0;
00503 }
00504 
00505 void OculusWorldDemoApp::OnMessage(const Message& msg)
00506 {
00507     if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved)
00508     {
00509         if (msg.pDevice == pManager)
00510         {
00511             const MessageDeviceStatus& statusMsg =
00512                 static_cast<const MessageDeviceStatus&>(msg);
00513 
00514             { // limit the scope of the lock
00515                 Lock::Locker lock(pManager->GetHandlerLock());
00516                 DeviceStatusNotificationsQueue.PushBack(
00517                     DeviceStatusNotificationDesc(statusMsg.Type, statusMsg.Handle));
00518             }
00519 
00520             switch (statusMsg.Type)
00521             {
00522                 case OVR::Message_DeviceAdded:
00523                     LogText("DeviceManager reported device added.\n");
00524                     break;
00525                 
00526                 case OVR::Message_DeviceRemoved:
00527                     LogText("DeviceManager reported device removed.\n");
00528                     break;
00529                 
00530                 default: OVR_ASSERT(0); // unexpected type
00531             }
00532         }
00533     }
00534 }
00535 
00536 void OculusWorldDemoApp::OnResize(int width, int height)
00537 {
00538     Width  = width;
00539     Height = height;
00540     SConfig.SetFullViewport(Viewport(0, 0, Width, Height));
00541 }
00542 
00543 void OculusWorldDemoApp::OnMouseMove(int x, int y, int modifiers)
00544 {
00545     if(modifiers & Mod_MouseRelative)
00546     {
00547         // Get Delta
00548         int dx = x, dy = y;
00549 
00550         const float maxPitch = ((3.1415f / 2) * 0.98f);
00551 
00552         // Apply to rotation. Subtract for right body frame rotation,
00553         // since yaw rotation is positive CCW when looking down on XZ plane.
00554         ThePlayer.EyeYaw   -= (Sensitivity * dx) / 360.0f;
00555 
00556         if(!pSensor)
00557         {
00558             ThePlayer.EyePitch -= (Sensitivity * dy) / 360.0f;
00559 
00560             if(ThePlayer.EyePitch > maxPitch)
00561             {
00562                 ThePlayer.EyePitch = maxPitch;
00563             }
00564             if(ThePlayer.EyePitch < -maxPitch)
00565             {
00566                 ThePlayer.EyePitch = -maxPitch;
00567             }
00568         }
00569     }
00570 }
00571 
00572 
00573 void OculusWorldDemoApp::OnKey(OVR::KeyCode key, int chr, bool down, int modifiers)
00574 {
00575     OVR_UNUSED(chr);
00576 
00577     switch(key)
00578     {
00579     case Key_Q:
00580         if (down && (modifiers & Mod_Control))
00581         {
00582             pPlatform->Exit(0);
00583         }
00584         break;
00585 
00586         // Handle player movement keys.
00587         // We just update movement state here, while the actual translation is done in OnIdle()
00588         // based on time.
00589     case Key_W:
00590         ThePlayer.MoveForward = down ? (ThePlayer.MoveForward | 1) : (ThePlayer.MoveForward & ~1);
00591         break;
00592     case Key_S:
00593         ThePlayer.MoveBack    = down ? (ThePlayer.MoveBack    | 1) : (ThePlayer.MoveBack    & ~1);
00594         break;
00595     case Key_A:
00596         ThePlayer.MoveLeft    = down ? (ThePlayer.MoveLeft    | 1) : (ThePlayer.MoveLeft    & ~1);
00597         break;
00598     case Key_D:
00599         ThePlayer.MoveRight   = down ? (ThePlayer.MoveRight   | 1) : (ThePlayer.MoveRight   & ~1);
00600         break;
00601     case Key_Up:
00602         ThePlayer.MoveForward = down ? (ThePlayer.MoveForward | 2) : (ThePlayer.MoveForward & ~2);
00603         break;
00604     case Key_Down:
00605         ThePlayer.MoveBack    = down ? (ThePlayer.MoveBack    | 2) : (ThePlayer.MoveBack    & ~2);
00606         break;
00607     case Key_Left:
00608         ThePlayer.MoveLeft    = down ? (ThePlayer.MoveLeft    | 2) : (ThePlayer.MoveLeft    & ~2);
00609         break;
00610     case Key_Right:
00611         ThePlayer.MoveRight   = down ? (ThePlayer.MoveRight   | 2) : (ThePlayer.MoveRight   & ~2);
00612         break;
00613 
00614     case Key_Minus:
00615         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight  : 0;
00616         AdjustDirection = -1;
00617         break;
00618     case Key_Equal:
00619         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustEyeHeight  : 0;
00620         AdjustDirection = 1;
00621         break;
00622 
00623     case Key_B:
00624         if (down)
00625         {
00626             if(SConfig.GetDistortionScale() == 1.0f)
00627             {
00628                 if(SConfig.GetHMDInfo().HScreenSize > 0.140f)  // 7"
00629                 {
00630                     SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
00631                 }
00632                 else
00633                 {
00634                  SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
00635                 }
00636             }
00637             else
00638             {
00639                 // No fitting; scale == 1.0.
00640                 SConfig.SetDistortionFitPointVP(0, 0);
00641             }
00642         }
00643         break;
00644 
00645     // Support toggling background color for distortion so that we can see
00646     // the effect on the periphery.
00647     case Key_V:
00648         if (down)
00649         {
00650             if(DistortionClearColor.B == 0)
00651             {
00652                 DistortionClearColor = Color(0, 128, 255);
00653             }
00654             else
00655             {
00656                 DistortionClearColor = Color(0, 0, 0);
00657             }
00658 
00659             pRender->SetDistortionClearColor(DistortionClearColor);
00660         }
00661         break;
00662 
00663 
00664     case Key_F1:
00665         SConfig.SetStereoMode(Stereo_None);
00666         PostProcess = PostProcess_None;
00667         SetAdjustMessage("StereoMode: None");
00668         break;
00669     case Key_F2:
00670         SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
00671         PostProcess = PostProcess_None;
00672         SetAdjustMessage("StereoMode: Stereo + No Distortion");
00673         break;
00674     case Key_F3:
00675         SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
00676         PostProcess = PostProcess_Distortion;
00677         SetAdjustMessage("StereoMode: Stereo + Distortion");
00678         break;
00679 
00680     case Key_R:
00681         SFusion.Reset();
00682                 if (MagCal.IsAutoCalibrating() || MagCal.IsManuallyCalibrating())
00683                 MagCal.AbortCalibration();
00684 
00685         SetAdjustMessage("Sensor Fusion Reset");
00686         break;
00687 
00688     case Key_Space:
00689         if (!down)
00690         {
00691             TextScreen = (enum TextScreen)((TextScreen + 1) % Text_Count);
00692         }
00693         break;
00694 
00695     case Key_F4:
00696         if (!down)
00697         {
00698                         RenderParams = pRender->GetParams();
00699             RenderParams.Multisample = RenderParams.Multisample > 1 ? 1 : 4;
00700             pRender->SetParams(RenderParams);
00701             if(RenderParams.Multisample > 1)
00702             {
00703                 SetAdjustMessage("Multisampling On");
00704             }
00705             else
00706             {
00707                 SetAdjustMessage("Multisampling Off");
00708             }
00709         }
00710         break;
00711     case Key_F9:
00712 #ifndef OVR_OS_LINUX    // On Linux F9 does the same as F11.
00713         if (!down)
00714         {
00715             CycleDisplay();
00716         }
00717         break;
00718 #endif
00719 #ifdef OVR_OS_MAC
00720     case Key_F10:  // F11 is reserved on Mac
00721 #else
00722     case Key_F11:
00723 #endif
00724         if (!down)
00725         {
00726             RenderParams = pRender->GetParams();
00727             RenderParams.Display = DisplayId(SConfig.GetHMDInfo().DisplayDeviceName,SConfig.GetHMDInfo().DisplayId);
00728             pRender->SetParams(RenderParams);
00729 
00730             pPlatform->SetMouseMode(Mouse_Normal);            
00731             pPlatform->SetFullscreen(RenderParams, pRender->IsFullscreen() ? Display_Window : Display_FakeFullscreen);
00732             pPlatform->SetMouseMode(Mouse_Relative); // Avoid mode world rotation jump.
00733             // If using an HMD, enable post-process (for distortion) and stereo.
00734             if(RenderParams.IsDisplaySet() && pRender->IsFullscreen())
00735             {
00736                 SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
00737                 PostProcess = PostProcess_Distortion;
00738             }
00739         }
00740         break;
00741 
00742     case Key_Escape:
00743         if(!down)
00744         {
00745                         if (MagCal.IsAutoCalibrating() || MagCal.IsManuallyCalibrating())
00746                         {
00747                                 MagCal.AbortCalibration();
00748                                 SetAdjustMessage("Aborting Magnetometer Calibration");
00749                         }
00750                         else 
00751                         {
00752                 // switch to primary screen windowed mode
00753                 pPlatform->SetFullscreen(RenderParams, Display_Window);
00754                 RenderParams.Display = pPlatform->GetDisplay(0);
00755                 pRender->SetParams(RenderParams);
00756                 Screen = 0;
00757                         }
00758         }
00759         break;
00760 
00761         // Stereo adjustments.
00762     case Key_BracketLeft:
00763         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov    : 0;
00764         AdjustDirection = 1;
00765         break;
00766     case Key_BracketRight:
00767         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustFov    : 0;
00768         AdjustDirection = -1;
00769         break;
00770 
00771     case Key_Insert:
00772     case Key_Num0:
00773         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD    : 0;
00774         AdjustDirection = 1;
00775         break;
00776     case Key_Delete:
00777     case Key_Num9:
00778         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustIPD    : 0;
00779         AdjustDirection = -1;
00780         break;
00781 
00782     case Key_PageUp:
00783         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
00784         AdjustDirection = 1;
00785         break;
00786     case Key_PageDown:
00787         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustAspect : 0;
00788         AdjustDirection = -1;
00789         break;
00790 
00791         // Distortion correction adjustments
00792     case Key_H:
00793         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
00794         AdjustDirection = -1;
00795         break;
00796     case Key_Y:
00797         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK0 : NULL;
00798         AdjustDirection = 1;
00799         break;
00800     case Key_J:
00801         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
00802         AdjustDirection = -1;
00803         break;
00804     case Key_U:
00805         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK1 : NULL;
00806         AdjustDirection = 1;
00807         break;
00808     case Key_K:
00809         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
00810         AdjustDirection = -1;
00811         break;
00812     case Key_I:
00813         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK2 : NULL;
00814         AdjustDirection = 1;
00815         break;
00816     case Key_L:
00817         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
00818         AdjustDirection = -1;
00819         break;
00820     case Key_O:
00821         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustDistortionK3 : NULL;
00822         AdjustDirection = 1;
00823         break;
00824 
00825     case Key_Tab:
00826         if (down)
00827         {
00828             float t0      = SConfig.GetDistortionK(0),
00829                   t1      = SConfig.GetDistortionK(1),
00830                   t2      = SConfig.GetDistortionK(2),
00831                   t3      = SConfig.GetDistortionK(3);
00832             float tESD    = SConfig.GetEyeToScreenDistance(),
00833                   taspect = SConfig.GetAspectMultiplier(),
00834                   tipd    = SConfig.GetIPD();
00835 
00836             if(SavedK0 > 0.0f)
00837             {
00838                                 SConfig.SetDistortionK(0, SavedK0);
00839                                 SConfig.SetDistortionK(1, SavedK1);
00840                                 SConfig.SetDistortionK(2, SavedK2);
00841                                 SConfig.SetDistortionK(3, SavedK3);
00842                                 SConfig.SetEyeToScreenDistance(SavedESD);
00843                                 SConfig.SetAspectMultiplier(SavedAspect);
00844                                 SConfig.SetIPD(SavedEyeDistance);
00845 
00846                                 if ( ShiftDown )
00847                                 {
00848                                         // Swap saved and current values. Good for doing direct comparisons.
00849                                         SetAdjustMessage("Swapped current and saved. New settings:\n"
00850                                                                          "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
00851                                                                          "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
00852                                                                          "K1: \t120 %.4f\t350 K3: \t490 %.4f\n",
00853                                                                          SavedESD, SavedEyeDistance,
00854                                                                          SavedK0, SavedK2,
00855                                                                          SavedK1, SavedK3);
00856                                         SavedK0 = t0;
00857                                         SavedK1 = t1;
00858                                         SavedK2 = t2;
00859                                         SavedK3 = t3;
00860                                         SavedESD = tESD;
00861                                         SavedAspect = taspect;
00862                                         SavedEyeDistance = tipd;
00863                                 }
00864                                 else
00865                                 {
00866                                         SetAdjustMessage("Restored:\n"
00867                                                                          "ESD:\t120 %.3f\t350 Eye:\t490 %.3f\n"
00868                                                                          "K0: \t120 %.4f\t350 K2: \t490 %.4f\n"
00869                                                                          "K1: \t120 %.4f\t350 K3: \t490 %.4f\n",
00870                                                                          SavedESD, SavedEyeDistance,
00871                                                                          SavedK0, SavedK2,
00872                                                                          SavedK1, SavedK3);
00873                                 }
00874             }
00875             else
00876             {
00877                 SetAdjustMessage("Setting Saved");
00878                                 SavedK0 = t0;
00879                                 SavedK1 = t1;
00880                                 SavedK2 = t2;
00881                                 SavedK3 = t3;
00882                                 SavedESD = tESD;
00883                                 SavedAspect = taspect;
00884                                 SavedEyeDistance = tipd;
00885             }
00886 
00887         }
00888         break; 
00889  
00890     case Key_G:
00891         if (down)
00892         {
00893             if(SceneMode == Scene_World)
00894             {
00895                 SceneMode = Scene_Grid;
00896                 SetAdjustMessage("Grid Only");
00897             }
00898             else if(SceneMode == Scene_Grid)
00899             {
00900                 SceneMode = Scene_Both;
00901                 SetAdjustMessage("Grid Overlay");
00902             }
00903             else if(SceneMode == Scene_Both)
00904             {
00905                 SceneMode = Scene_World;
00906                 SetAdjustMessage("Grid Off");
00907             }
00908         }
00909         break;
00910 
00911         // Holding down Shift key accelerates adjustment velocity.
00912     case Key_Shift:
00913         ShiftDown = down;
00914         break;
00915 
00916         // Reset the camera position in case we get stuck
00917     case Key_T:
00918         ThePlayer.EyePos = Vector3f(10.0f, 1.6f, 10.0f);
00919         break;
00920 
00921     case Key_F5:
00922         if (!down)
00923         {
00924             UPInt numNodes = MainScene.Models.GetSize();
00925             for(UPInt i = 0; i < numNodes; i++)
00926             {
00927                 Ptr<OVR::Render::Model> nodePtr = MainScene.Models[i];
00928                 Render::Model*          pNode = nodePtr.GetPtr();
00929                 if(pNode->IsCollisionModel)
00930                 {
00931                     pNode->Visible = !pNode->Visible;
00932                 }
00933             }
00934         }
00935         break;
00936 
00937     case Key_N:
00938         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
00939         AdjustDirection = -1;
00940         break;
00941 
00942     case Key_M:
00943         pAdjustFunc = down ? &OculusWorldDemoApp::AdjustMotionPrediction : NULL;
00944         AdjustDirection = 1;
00945         break;
00946 
00947 /*
00948     case Key_N:
00949         RaiseLOD();
00950         break;
00951     case Key_M:
00952         DropLOD();
00953         break;
00954 */
00955         // Start calibrating magnetometer
00956         case Key_Z:
00957                 if (down) 
00958                 {
00959                         ManualMagCalStage = 0;
00960                         if (MagCal.IsManuallyCalibrating())
00961                                 MagAwaitingForwardLook = false;
00962                         else
00963                         {
00964                 MagCal.BeginManualCalibration(SFusion);
00965                                 MagAwaitingForwardLook = true;
00966                         }
00967                 }
00968         break;
00969 
00970     case Key_X:
00971                 if (down) 
00972                 {
00973             MagCal.BeginAutoCalibration(SFusion);
00974             SetAdjustMessage("Starting Auto Mag Calibration");
00975                 }
00976         break;
00977 
00978         // Show view of yaw angles (for mag calibration/analysis)
00979     case Key_F6:
00980         if (down)
00981         {
00982             if (SceneMode != Scene_YawView)
00983             {
00984                 SceneMode = Scene_YawView;  
00985                 SetAdjustMessage("Magnetometer Yaw Angle Marks");
00986             }
00987             else
00988             {
00989                 SceneMode = Scene_World;               
00990                 SetAdjustMessage("Magnetometer Marks Off");
00991             }
00992         }
00993         break;
00994 
00995     case Key_C:
00996         if (down)
00997         {
00998             // Toggle chromatic aberration correction on/off.
00999             RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
01000 
01001             if (shader == RenderDevice::PostProcessShader_Distortion)
01002             {
01003                 pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);                
01004                 SetAdjustMessage("Chromatic Aberration Correction On");
01005             }
01006             else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
01007             {
01008                 pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);                
01009                 SetAdjustMessage("Chromatic Aberration Correction Off");
01010             }
01011             else
01012                 OVR_ASSERT(false);
01013         }
01014         break;
01015 
01016     case Key_P:
01017         if (down)
01018         {
01019             // Toggle motion prediction.
01020             if (SFusion.IsPredictionEnabled())
01021             {
01022                 SFusion.SetPredictionEnabled(false);
01023                 SetAdjustMessage("Motion Prediction Off");
01024             }
01025             else
01026             {
01027                 SFusion.SetPredictionEnabled(true);
01028                 SetAdjustMessage("Motion Prediction On");
01029             }      
01030         }
01031         break;
01032      default:
01033         break;
01034     }
01035 }
01036 
01037 void OculusWorldDemoApp::OnIdle()
01038 {
01039 
01040     double curtime = pPlatform->GetAppTime();
01041     float  dt      = float(curtime - LastUpdate);
01042     LastUpdate     = curtime;
01043 
01044     // Update gamepad.
01045     GamepadState gamepadState;
01046     if (GetPlatformCore()->GetGamepadManager()->GetGamepadState(0, &gamepadState))
01047     {
01048         GamepadStateChanged(gamepadState);
01049     }
01050 
01051 
01052     if (LoadingState == LoadingState_DoLoad)
01053     {
01054         PopulateScene(MainFilePath.ToCStr());
01055         LoadingState = LoadingState_Finished;
01056         return;
01057     }
01058     
01059     // Check if any new devices were connected.
01060     {
01061         bool queueIsEmpty = false;
01062         while (!queueIsEmpty)
01063         {
01064             DeviceStatusNotificationDesc desc;
01065 
01066             {
01067                 Lock::Locker lock(pManager->GetHandlerLock());
01068                 if (DeviceStatusNotificationsQueue.GetSize() == 0)
01069                     break;
01070                 desc = DeviceStatusNotificationsQueue.Front();
01071              
01072                 // We can't call Clear under the lock since this may introduce a dead lock:
01073                 // this thread is locked by HandlerLock and the Clear might cause 
01074                 // call of Device->Release, which will use Manager->DeviceLock. The bkg
01075                 // thread is most likely locked by opposite way: 
01076                 // Manager->DeviceLock ==> HandlerLock, therefore - a dead lock.
01077                 // So, just grab the first element, save a copy of it and remove
01078                 // the element (Device->Release won't be called since we made a copy).
01079                 
01080                 DeviceStatusNotificationsQueue.RemoveAt(0);
01081                 queueIsEmpty = (DeviceStatusNotificationsQueue.GetSize() == 0);
01082             }
01083 
01084             bool wasAlreadyCreated = desc.Handle.IsCreated();
01085 
01086             if (desc.Action == Message_DeviceAdded)
01087             {
01088                 switch(desc.Handle.GetType())
01089                 {
01090                 case Device_Sensor:
01091                     if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
01092                     {
01093                         if (!pSensor)
01094                         {
01095                             pSensor = *desc.Handle.CreateDeviceTyped<SensorDevice>();
01096                             SFusion.AttachToSensor(pSensor);
01097                             SetAdjustMessage("---------------------------\n"
01098                                              "SENSOR connected\n"
01099                                              "---------------------------");
01100                         }
01101                         else if (!wasAlreadyCreated)
01102                         {
01103                             LogText("A new SENSOR has been detected, but it is not currently used.");
01104                         }
01105                     }
01106                     break;
01107                 case Device_LatencyTester:
01108                     if (desc.Handle.IsAvailable() && !desc.Handle.IsCreated())
01109                     {
01110                         if (!pLatencyTester)
01111                         {
01112                             pLatencyTester = *desc.Handle.CreateDeviceTyped<LatencyTestDevice>();
01113                             LatencyUtil.SetDevice(pLatencyTester);
01114                             if (!wasAlreadyCreated)
01115                                 SetAdjustMessage("----------------------------------------\n"
01116                                                  "LATENCY TESTER connected\n"
01117                                                  "----------------------------------------");
01118                         }
01119                     }
01120                     break;
01121                 case Device_HMD:
01122                     {
01123                         OVR::HMDInfo info;
01124                         desc.Handle.GetDeviceInfo(&info);
01125                         // if strlen(info.DisplayDeviceName) == 0 then
01126                         // this HMD is 'fake' (created using sensor).
01127                         if (strlen(info.DisplayDeviceName) > 0 && (!pHMD || !info.IsSameDisplay(TheHMDInfo)))
01128                         {
01129                             SetAdjustMessage("------------------------\n"
01130                                              "HMD connected\n"
01131                                              "------------------------");
01132                             if (!pHMD || !desc.Handle.IsDevice(pHMD))
01133                                 pHMD = *desc.Handle.CreateDeviceTyped<HMDDevice>();
01134                             // update stereo config with new HMDInfo
01135                             if (pHMD && pHMD->GetDeviceInfo(&TheHMDInfo))
01136                             {
01137                                 //RenderParams.MonitorName = hmd.DisplayDeviceName;
01138                                 SConfig.SetHMDInfo(TheHMDInfo);
01139                             }
01140                             LogText("HMD device added.\n");
01141                         }
01142                         break;
01143                     }
01144                 default:;
01145                 }
01146             }
01147             else if (desc.Action == Message_DeviceRemoved)
01148             {
01149                 if (desc.Handle.IsDevice(pSensor))
01150                 {
01151                     LogText("Sensor reported device removed.\n");
01152                     SFusion.AttachToSensor(NULL);
01153                     pSensor.Clear();
01154                     SetAdjustMessage("-------------------------------\n"
01155                                      "SENSOR disconnected.\n"
01156                                      "-------------------------------");
01157                 }
01158                 else if (desc.Handle.IsDevice(pLatencyTester))
01159                 {
01160                     LogText("Latency Tester reported device removed.\n");
01161                     LatencyUtil.SetDevice(NULL);
01162                     pLatencyTester.Clear();
01163                     SetAdjustMessage("---------------------------------------------\n"
01164                                      "LATENCY SENSOR disconnected.\n"
01165                                      "---------------------------------------------");
01166                 }
01167                 else if (desc.Handle.IsDevice(pHMD))
01168                 {
01169                     if (pHMD && !pHMD->IsDisconnected())
01170                     {
01171                         SetAdjustMessage("---------------------------\n"
01172                                          "HMD disconnected\n"
01173                                          "---------------------------");
01174                         // Disconnect HMD. pSensor is used to restore 'fake' HMD device
01175                         // (can be NULL).
01176                         pHMD = pHMD->Disconnect(pSensor);
01177 
01178                         // This will initialize TheHMDInfo with information about configured IPD,
01179                         // screen size and other variables needed for correct projection.
01180                         // We pass HMD DisplayDeviceName into the renderer to select the
01181                         // correct monitor in full-screen mode.
01182                         if (pHMD && pHMD->GetDeviceInfo(&TheHMDInfo))
01183                         {
01184                             //RenderParams.MonitorName = hmd.DisplayDeviceName;
01185                             SConfig.SetHMDInfo(TheHMDInfo);
01186                         }
01187                         LogText("HMD device removed.\n");
01188                     }
01189                 }
01190             }
01191             else 
01192                 OVR_ASSERT(0); // unexpected action
01193         }
01194     }
01195 
01196     // If one of Stereo setting adjustment keys is pressed, adjust related state.
01197     if (pAdjustFunc)
01198     {
01199         (this->*pAdjustFunc)(dt * AdjustDirection * (ShiftDown ? 5.0f : 1.0f));
01200     }
01201 
01202     // Process latency tester results.
01203     const char* results = LatencyUtil.GetResultsString();
01204     if (results != NULL)
01205     {
01206         LogText("LATENCY TESTER: %s\n", results); 
01207     }
01208 
01209     // Have to place this as close as possible to where the HMD orientation is read.
01210     LatencyUtil.ProcessInputs();
01211 
01212     // Magnetometer calibration procedure
01213     if (MagCal.IsManuallyCalibrating())
01214         UpdateManualMagCalibration();
01215 
01216     if (MagCal.IsAutoCalibrating()) 
01217     {
01218         MagCal.UpdateAutoCalibration(SFusion);
01219                 int n = MagCal.NumberOfSamples();
01220             if (n == 1)
01221                 SetAdjustMessage("   Magnetometer Calibration Has 1 Sample   \n   %d Remaining - Please Keep Looking Around   ",4-n);
01222                 else if (n < 4)
01223                         SetAdjustMessage("   Magnetometer Calibration Has %d Samples   \n   %d Remaining - Please Keep Looking Around   ",n,4-n);
01224         if (MagCal.IsCalibrated())
01225         {
01226             SFusion.SetYawCorrectionEnabled(true);
01227             Vector3f mc = MagCal.GetMagCenter();
01228             SetAdjustMessage("   Magnetometer Calibration Complete   \nCenter: %f %f %f",mc.x,mc.y,mc.z);
01229         }
01230     }
01231 
01232     // Handle Sensor motion.
01233     // We extract Yaw, Pitch, Roll instead of directly using the orientation
01234     // to allow "additional" yaw manipulation with mouse/controller.
01235     if(pSensor)
01236     {
01237         Quatf    hmdOrient = SFusion.GetPredictedOrientation();
01238 
01239         float    yaw = 0.0f;
01240         hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &ThePlayer.EyePitch, &ThePlayer.EyeRoll);
01241 
01242         ThePlayer.EyeYaw += (yaw - ThePlayer.LastSensorYaw);
01243         ThePlayer.LastSensorYaw = yaw;
01244 
01245         // NOTE: We can get a matrix from orientation as follows:
01246         // Matrix4f hmdMat(hmdOrient);
01247 
01248         // Test logic - assign quaternion result directly to view:
01249         // Quatf hmdOrient = SFusion.GetOrientation();
01250         // View = Matrix4f(hmdOrient.Inverted()) * Matrix4f::Translation(-EyePos);
01251     }
01252 
01253 
01254     if(curtime >= NextFPSUpdate)
01255     {
01256         NextFPSUpdate = curtime + 1.0;
01257         FPS = FrameCounter;
01258         FrameCounter = 0;
01259     }
01260     FrameCounter++;
01261 
01262     if(FPS < 40)
01263     {
01264         ConsecutiveLowFPSFrames++;
01265     }
01266     else
01267     {
01268         ConsecutiveLowFPSFrames = 0;
01269     }
01270 
01271     if(ConsecutiveLowFPSFrames > 200)
01272     {
01273         DropLOD();
01274         ConsecutiveLowFPSFrames = 0;
01275     }
01276 
01277     ThePlayer.EyeYaw -= ThePlayer.GamepadRotate.x * dt;
01278     ThePlayer.HandleCollision(dt, &CollisionModels, &GroundCollisionModels, ShiftDown);
01279 
01280     if(!pSensor)
01281     {
01282         ThePlayer.EyePitch -= ThePlayer.GamepadRotate.y * dt;
01283 
01284         const float maxPitch = ((3.1415f / 2) * 0.98f);
01285         if(ThePlayer.EyePitch > maxPitch)
01286         {
01287             ThePlayer.EyePitch = maxPitch;
01288         }
01289         if(ThePlayer.EyePitch < -maxPitch)
01290         {
01291             ThePlayer.EyePitch = -maxPitch;
01292         }
01293     }
01294 
01295     // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
01296     //
01297     Matrix4f rollPitchYaw = Matrix4f::RotationY(ThePlayer.EyeYaw) * Matrix4f::RotationX(ThePlayer.EyePitch) *
01298                             Matrix4f::RotationZ(ThePlayer.EyeRoll);
01299     Vector3f up      = rollPitchYaw.Transform(UpVector);
01300     Vector3f forward = rollPitchYaw.Transform(ForwardVector);
01301 
01302 
01303     // Minimal head modeling; should be moved as an option to SensorFusion.
01304     float headBaseToEyeHeight     = 0.15f;  // Vertical height of eye from base of head
01305     float headBaseToEyeProtrusion = 0.09f;  // Distance forward of eye from base of head
01306 
01307     Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
01308     Vector3f shiftedEyePos = ThePlayer.EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
01309     shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
01310     View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
01311 
01312     //  Transformation without head modeling.
01313     // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
01314 
01315     // This is an alternative to LookAtRH:
01316     // Here we transpose the rotation matrix to get its inverse.
01317     //  View = (Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
01318     //                                        Matrix4f::RotationZ(EyeRoll)).Transposed() *
01319     //         Matrix4f::Translation(-EyePos);
01320 
01321 
01322     switch(SConfig.GetStereoMode())
01323     {
01324     case Stereo_None:
01325         Render(SConfig.GetEyeRenderParams(StereoEye_Center));
01326         break;
01327 
01328     case Stereo_LeftRight_Multipass:
01329         //case Stereo_LeftDouble_Multipass:
01330         Render(SConfig.GetEyeRenderParams(StereoEye_Left));
01331         Render(SConfig.GetEyeRenderParams(StereoEye_Right));
01332         break;
01333 
01334     }
01335 
01336     pRender->Present();
01337     // Force GPU to flush the scene, resulting in the lowest possible latency.
01338     pRender->ForceFlushGPU();
01339 }
01340 
01341 
01342 void OculusWorldDemoApp::UpdateManualMagCalibration() 
01343 {
01344     float tyaw, pitch, roll;
01345         Anglef yaw;
01346     Quatf hmdOrient = SFusion.GetOrientation();
01347     hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&tyaw, &pitch, &roll);
01348     Vector3f mag = SFusion.GetMagnetometer();
01349     float dtr = Math<float>::DegreeToRadFactor;
01350         yaw.Set(tyaw); // Using Angle class to handle angle wraparound arithmetic
01351 
01352         const int timeout = 100;
01353 
01354         switch(ManualMagCalStage)
01355     {
01356         case 0:
01357                         if (MagAwaitingForwardLook)
01358                 SetAdjustMessage("Magnetometer Calibration\n** Step 1: Please Look Forward **\n** and Press Z When Ready **");
01359                         else
01360                 if (fabs(pitch) < 10.0f*dtr)
01361                             {
01362                     MagCal.InsertIfAcceptable(hmdOrient, mag);
01363                                     FirstMagYaw = yaw;
01364                                         MagAwaitingForwardLook = false;
01365                                         if (MagCal.NumberOfSamples() == 1)
01366                                         {
01367                                                 ManualMagCalStage = 1;
01368                                                 ManualMagFailures = 0;
01369                                         }   
01370                             }
01371                                 else
01372                                         MagAwaitingForwardLook = true;
01373             break;
01374         case 1:
01375             SetAdjustMessage("Magnetometer Calibration\n** Step 2: Please Look Up **");
01376                         yaw -= FirstMagYaw;
01377             if ((pitch > 50.0f*dtr) && (yaw.Abs() < 20.0f*dtr))
01378                         {
01379                             MagCal.InsertIfAcceptable(hmdOrient, mag);
01380                             ManualMagFailures++;
01381                     if ((MagCal.NumberOfSamples() == 2)||(ManualMagFailures > timeout))
01382                                 {
01383                                 ManualMagCalStage = 2;
01384                                         ManualMagFailures = 0;
01385                                 }
01386                         }
01387             break;
01388         case 2:
01389             SetAdjustMessage("Magnetometer Calibration\n** Step 3: Please Look Left **");
01390                         yaw -= FirstMagYaw;
01391             if (yaw.Get() > 60.0f*dtr) 
01392                         {
01393                 MagCal.InsertIfAcceptable(hmdOrient, mag);
01394                             ManualMagFailures++;
01395                     if ((MagCal.NumberOfSamples() == 3)||(ManualMagFailures > timeout))
01396                                 {
01397                                 ManualMagCalStage = 3;
01398                                         ManualMagFailures = 0;
01399                                 }
01400                         }
01401             break;
01402         case 3:
01403             SetAdjustMessage("Magnetometer Calibration\n** Step 4: Please Look Right **");
01404                         yaw -= FirstMagYaw;
01405                         if (yaw.Get() < -60.0f*dtr)
01406                         {
01407                 MagCal.InsertIfAcceptable(hmdOrient, mag);
01408                             ManualMagFailures++;
01409                     if (MagCal.NumberOfSamples() == 4)
01410                                 ManualMagCalStage = 6;
01411                                 else
01412                                 {
01413                                         if (ManualMagFailures > timeout)
01414                                     {
01415                                             ManualMagCalStage = 4;
01416                                             ManualMagFailures = 0;
01417                                     }
01418                                 }
01419                         }
01420             break;
01421         case 4:
01422             SetAdjustMessage("Magnetometer Calibration\n** Step 5: Please Look Upper Right **");
01423                         yaw -= FirstMagYaw;
01424                         if ((yaw.Get() < -50.0f*dtr) && (pitch > 40.0f*dtr)) 
01425                         {
01426                 MagCal.InsertIfAcceptable(hmdOrient, mag);
01427                     if (MagCal.NumberOfSamples() == 4)
01428                                 ManualMagCalStage = 6;
01429                                 else 
01430                                 {
01431                                         if (ManualMagFailures > timeout)
01432                                     {
01433                                             ManualMagCalStage = 5;
01434                                             ManualMagFailures = 0;
01435                                     }
01436                                     else
01437                                             ManualMagFailures++;
01438                                 }
01439                         }
01440             break;
01441         case 5:
01442             SetAdjustMessage("Calibration Failed\n** Try Again From Another Location **");
01443                         MagCal.AbortCalibration();
01444                         break;
01445                 case 6:
01446             if (!MagCal.IsCalibrated()) 
01447             {
01448                 MagCal.SetCalibration(SFusion);
01449                 SFusion.SetYawCorrectionEnabled(true);
01450                 Vector3f mc = MagCal.GetMagCenter();
01451                 SetAdjustMessage("   Magnetometer Calibration and Activation   \nCenter: %f %f %f",
01452                                         mc.x,mc.y,mc.z);
01453             }
01454     }
01455 }
01456 
01457 
01458 
01459 static const char* HelpText =
01460     "F1         \t100 NoStereo                   \t420 Z    \t520 Manual Mag Calib\n"
01461     "F2         \t100 Stereo                     \t420 X    \t520 Auto Mag Calib\n"
01462     "F3         \t100 StereoHMD                  \t420 ;    \t520 Mag Set Ref Point\n" 
01463     "F4         \t100 MSAA                       \t420 F6   \t520 Mag Info\n"
01464     "F9         \t100 FullScreen                 \t420 R    \t520 Reset SensorFusion\n"
01465     "F11        \t100 Fast FullScreen                   \t500 - +       \t660 Adj EyeHeight\n"                                           
01466     "C          \t100 Chromatic Ab                      \t500 [ ]       \t660 Adj FOV\n"
01467     "P          \t100 Motion Pred                       \t500 Shift     \t660 Adj Faster\n"
01468     "N/M        \t180 Adj Motion Pred\n"
01469     "( / )      \t180 Adj EyeDistance"
01470     ;
01471 
01472 
01473 enum DrawTextCenterType
01474 {
01475     DrawText_NoCenter= 0,
01476     DrawText_VCenter = 0x1,
01477     DrawText_HCenter = 0x2,
01478     DrawText_Center  = DrawText_VCenter | DrawText_HCenter
01479 };
01480 
01481 static void DrawTextBox(RenderDevice* prender, float x, float y,
01482                         float textSize, const char* text,
01483                         DrawTextCenterType centerType = DrawText_NoCenter)
01484 {
01485     float ssize[2] = {0.0f, 0.0f};
01486 
01487     prender->MeasureText(&DejaVu, text, textSize, ssize);
01488 
01489     // Treat 0 a VCenter.
01490     if (centerType & DrawText_HCenter)
01491     {
01492         x = -ssize[0]/2;
01493     }
01494     if (centerType & DrawText_VCenter)
01495     {
01496         y = -ssize[1]/2;
01497     }
01498 
01499     prender->FillRect(x-0.02f, y-0.02f, x+ssize[0]+0.02f, y+ssize[1]+0.02f, Color(40,40,100,210));
01500     prender->RenderText(&DejaVu, text, x, y, textSize, Color(255,255,0,210));
01501 }
01502 
01503 void OculusWorldDemoApp::Render(const StereoEyeParams& stereo)
01504 {
01505     pRender->BeginScene(PostProcess);
01506 
01507     // *** 3D - Configures Viewport/Projection and Render
01508     pRender->ApplyStereoParams(stereo);    
01509     pRender->Clear();
01510 
01511     pRender->SetDepthMode(true, true);
01512     if (SceneMode != Scene_Grid)
01513     {
01514         MainScene.Render(pRender, stereo.ViewAdjust * View);
01515     }
01516 
01517     if (SceneMode == Scene_YawView)
01518     {
01519         Matrix4f calView = Matrix4f();
01520         float viewYaw = -ThePlayer.LastSensorYaw + SFusion.GetMagRefYaw();
01521         calView.M[0][0] = calView.M[2][2] = cos(viewYaw);
01522         calView.M[0][2] = sin(viewYaw);
01523         calView.M[2][0] = -sin(viewYaw);
01524         //LogText("yaw: %f\n",SFusion.GetMagRefYaw());
01525 
01526         if (SFusion.IsYawCorrectionInProgress())
01527             YawMarkGreenScene.Render(pRender, stereo.ViewAdjust);
01528         else
01529             YawMarkRedScene.Render(pRender, stereo.ViewAdjust);
01530 
01531         if (fabs(ThePlayer.EyePitch) < Math<float>::Pi * 0.33)
01532         YawLinesScene.Render(pRender, stereo.ViewAdjust * calView);
01533     }
01534 
01535     // *** 2D Text & Grid - Configure Orthographic rendering.
01536 
01537     // Render UI in 2D orthographic coordinate system that maps [-1,1] range
01538     // to a readable FOV area centered at your eye and properly adjusted.
01539     pRender->ApplyStereoParams2D(stereo);    
01540     pRender->SetDepthMode(false, false);
01541 
01542     float unitPixel = SConfig.Get2DUnitPixel();
01543     float textHeight= unitPixel * 22; 
01544 
01545     if ((SceneMode == Scene_Grid)||(SceneMode == Scene_Both))
01546     {   // Draw grid two pixels thick.
01547         GridScene.Render(pRender, Matrix4f());
01548         GridScene.Render(pRender, Matrix4f::Translation(unitPixel,unitPixel,0));
01549     }
01550 
01551     // Display Loading screen-shot in frame 0.
01552     if (LoadingState != LoadingState_Finished)
01553     {
01554         LoadingScene.Render(pRender, Matrix4f());
01555         String loadMessage = String("Loading ") + MainFilePath;
01556         DrawTextBox(pRender, 0.0f, 0.25f, textHeight, loadMessage.ToCStr(), DrawText_HCenter);
01557         LoadingState = LoadingState_DoLoad;
01558     }
01559 
01560     if(!AdjustMessage.IsEmpty() && AdjustMessageTimeout > pPlatform->GetAppTime())
01561     {
01562         DrawTextBox(pRender,0.0f,0.4f, textHeight, AdjustMessage.ToCStr(), DrawText_HCenter);
01563     }
01564 
01565     switch(TextScreen)
01566     {
01567     case Text_Orientation:
01568     {
01569         char buf[256], gpustat[256];
01570         OVR_sprintf(buf, sizeof(buf),
01571                     " Yaw:%4.0f  Pitch:%4.0f  Roll:%4.0f \n"
01572                     " FPS: %d  Frame: %d \n Pos: %3.2f, %3.2f, %3.2f \n"
01573                     " EyeHeight: %3.2f",
01574                     RadToDegree(ThePlayer.EyeYaw), RadToDegree(ThePlayer.EyePitch), RadToDegree(ThePlayer.EyeRoll),
01575                     FPS, FrameCounter, ThePlayer.EyePos.x, ThePlayer.EyePos.y, ThePlayer.EyePos.z, ThePlayer.EyePos.y);
01576         size_t texMemInMB = pRender->GetTotalTextureMemoryUsage() / 1058576;
01577         if (texMemInMB)
01578         {
01579             OVR_sprintf(gpustat, sizeof(gpustat), "\n GPU Tex: %u MB", texMemInMB);
01580             OVR_strcat(buf, sizeof(buf), gpustat);
01581         }
01582         
01583         DrawTextBox(pRender, 0.0f, -0.15f, textHeight, buf, DrawText_HCenter);
01584     }
01585     break;
01586 
01587     case Text_Config:
01588     {
01589         char   textBuff[2048];
01590          
01591         OVR_sprintf(textBuff, sizeof(textBuff),
01592                     "Fov\t300 %9.4f\n"
01593                     "EyeDistance\t300 %9.4f\n"
01594                     "DistortionK0\t300 %9.4f\n"
01595                     "DistortionK1\t300 %9.4f\n"
01596                     "DistortionK2\t300 %9.4f\n"
01597                     "DistortionK3\t300 %9.4f\n"
01598                     "TexScale\t300 %9.4f",
01599                     SConfig.GetYFOVDegrees(),
01600                         SConfig.GetIPD(),
01601                     SConfig.GetDistortionK(0),
01602                     SConfig.GetDistortionK(1),
01603                     SConfig.GetDistortionK(2),
01604                     SConfig.GetDistortionK(3),
01605                     SConfig.GetDistortionScale());
01606 
01607             DrawTextBox(pRender, 0.0f, 0.0f, textHeight, textBuff, DrawText_Center);
01608     }
01609     break;
01610 
01611     case Text_Help:
01612         DrawTextBox(pRender, 0.0f, -0.1f, textHeight, HelpText, DrawText_Center);
01613             
01614     default:
01615         break;
01616     }
01617 
01618 
01619     // Display colored quad if we're doing a latency test.
01620     Color colorToDisplay;
01621     if (LatencyUtil.DisplayScreenColor(colorToDisplay))
01622     {
01623         pRender->FillRect(-0.4f, -0.4f, 0.4f, 0.4f, colorToDisplay);
01624     }
01625 
01626     pRender->FinishScene();
01627 }
01628 
01629 
01630 // Sets temporarily displayed message for adjustments
01631 void OculusWorldDemoApp::SetAdjustMessage(const char* format, ...)
01632 {
01633     Lock::Locker lock(pManager->GetHandlerLock());
01634     char textBuff[2048];
01635     va_list argList;
01636     va_start(argList, format);
01637     OVR_vsprintf(textBuff, sizeof(textBuff), format, argList);
01638     va_end(argList);
01639 
01640     // Message will time out in 4 seconds.
01641     AdjustMessage = textBuff;
01642     AdjustMessageTimeout = pPlatform->GetAppTime() + 4.0f;
01643 }
01644 
01645 void OculusWorldDemoApp::SetAdjustMessageTimeout(float timeout)
01646 {
01647     AdjustMessageTimeout = pPlatform->GetAppTime() + timeout;
01648 }
01649 
01650 // ***** View Control Adjustments
01651 
01652 void OculusWorldDemoApp::AdjustFov(float dt)
01653 {
01654     float esd = SConfig.GetEyeToScreenDistance() + 0.01f * dt;
01655     SConfig.SetEyeToScreenDistance(esd);
01656     SetAdjustMessage("ESD:%6.3f  FOV: %6.3f", esd, SConfig.GetYFOVDegrees());
01657 }
01658 
01659 void OculusWorldDemoApp::AdjustAspect(float dt)
01660 {
01661     float rawAspect = SConfig.GetAspect() / SConfig.GetAspectMultiplier();
01662     float newAspect = SConfig.GetAspect() + 0.01f * dt;
01663     SConfig.SetAspectMultiplier(newAspect / rawAspect);
01664     SetAdjustMessage("Aspect: %6.3f", newAspect);
01665 }
01666 
01667 void OculusWorldDemoApp::AdjustDistortion(float dt, int kIndex, const char* label)
01668 {
01669     SConfig.SetDistortionK(kIndex, SConfig.GetDistortionK(kIndex) + 0.03f * dt);
01670     SetAdjustMessage("%s: %6.4f", label, SConfig.GetDistortionK(kIndex));
01671 }
01672 
01673 void OculusWorldDemoApp::AdjustIPD(float dt)
01674 {
01675     SConfig.SetIPD(SConfig.GetIPD() + 0.025f * dt);
01676     SetAdjustMessage("EyeDistance: %6.4f", SConfig.GetIPD());
01677 }
01678 
01679 void OculusWorldDemoApp::AdjustEyeHeight(float dt)
01680 {
01681     float dist = 0.5f * dt;
01682 
01683     ThePlayer.EyeHeight += dist;
01684     ThePlayer.EyePos.y += dist;
01685 
01686     SetAdjustMessage("EyeHeight: %4.2f", ThePlayer.EyeHeight);
01687 }
01688 
01689 void OculusWorldDemoApp::AdjustMotionPrediction(float dt)
01690 {
01691     float motionPred = SFusion.GetPredictionDelta() + 0.01f * dt;
01692 
01693     if (motionPred < 0.0f)
01694     {
01695         motionPred = 0.0f;
01696     }
01697     
01698     SFusion.SetPrediction(motionPred);
01699 
01700     SetAdjustMessage("MotionPrediction: %6.3fs", motionPred);
01701 }
01702 
01703 
01704 // Loads the scene data
01705 void OculusWorldDemoApp::PopulateScene(const char *fileName)
01706 {    
01707     XmlHandler xmlHandler;     
01708     if(!xmlHandler.ReadFile(fileName, pRender, &MainScene, &CollisionModels, &GroundCollisionModels))
01709     {
01710         SetAdjustMessage("---------------------------------\nFILE LOAD FAILED\n---------------------------------");
01711         SetAdjustMessageTimeout(10.0f);
01712     }    
01713 
01714     MainScene.SetAmbient(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
01715     
01716     // Distortion debug grid (brought up by 'G' key).
01717     Ptr<Model> gridModel = *Model::CreateGrid(Vector3f(0,0,0), Vector3f(1.0f/10, 0,0), Vector3f(0,1.0f/10,0),
01718                                               10, 10, 5, 
01719                                               Color(0, 255, 0, 255), Color(255, 50, 50, 255) );
01720     GridScene.World.Add(gridModel);
01721 
01722     // Yaw angle marker and lines (brought up by ';' key).
01723     float shifty = -0.5f;
01724     Ptr<Model> yawMarkGreenModel = *Model::CreateBox(Color(0, 255, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
01725     YawMarkGreenScene.World.Add(yawMarkGreenModel);
01726     Ptr<Model> yawMarkRedModel = *Model::CreateBox(Color(255, 0, 0, 255), Vector3f(0.0f, shifty, -2.0f), Vector3f(0.05f, 0.05f, 0.05f));
01727     YawMarkRedScene.World.Add(yawMarkRedModel);
01728 
01729     Ptr<Model> yawLinesModel = *new Model(Prim_Lines);
01730     float r = 2.0f;
01731     float theta0 = Math<float>::PiOver2;
01732     float theta1 = 0.0f;
01733     Color c = Color(255, 200, 200, 255);
01734     for (int i = 0; i < 35; i++) 
01735     {
01736         theta1 = theta0 + Math<float>::Pi / 18.0f;
01737         yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
01738                                yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
01739         theta0 = theta1;
01740         yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
01741                                yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty+0.1f,-r*sin(theta0)),c));
01742         theta0 = theta1;
01743     }
01744     theta1 = theta0 + Math<float>::Pi / 18.0f;
01745     yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(r*cos(theta0),shifty,-r*sin(theta0)),c),
01746                            yawLinesModel->AddVertex(Vector3f(r*cos(theta1),shifty,-r*sin(theta1)),c));
01747     yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
01748                            yawLinesModel->AddVertex(Vector3f(r*sin(0.02f),shifty,-r*cos(0.02f)),c));
01749     yawLinesModel->AddLine(yawLinesModel->AddVertex(Vector3f(0.0f,shifty+0.1f,-r),c),
01750                            yawLinesModel->AddVertex(Vector3f(r*sin(-0.02f),shifty,-r*cos(-0.02f)),c));
01751     yawLinesModel->SetPosition(Vector3f(0.0f,0.0f,0.0f));
01752 
01753     YawLinesScene.World.Add(yawLinesModel);
01754 }
01755 
01756 
01757 void OculusWorldDemoApp::PopulatePreloadScene()
01758 {
01759     // Load-screen screen shot image
01760     String fileName = MainFilePath;
01761     fileName.StripExtension();
01762 
01763     Ptr<File>    imageFile = *new SysFile(fileName + "_LoadScreen.tga");
01764     Ptr<Texture> imageTex;
01765     if (imageFile->IsValid())
01766         imageTex = *LoadTextureTga(pRender, imageFile);
01767 
01768     // Image is rendered as a single quad.
01769     if (imageTex)
01770     {
01771         imageTex->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
01772         Ptr<Model> m = *new Model(Prim_Triangles);        
01773         m->AddVertex(-0.5f,  0.5f,  0.0f, Color(255,255,255,255), 0.0f, 0.0f);
01774         m->AddVertex( 0.5f,  0.5f,  0.0f, Color(255,255,255,255), 1.0f, 0.0f);
01775         m->AddVertex( 0.5f, -0.5f,  0.0f, Color(255,255,255,255), 1.0f, 1.0f);
01776         m->AddVertex(-0.5f, -0.5f,  0.0f, Color(255,255,255,255), 0.0f, 1.0f);
01777         m->AddTriangle(2,1,0);
01778         m->AddTriangle(0,3,2);
01779 
01780         Ptr<ShaderFill> fill = *new ShaderFill(*pRender->CreateShaderSet());
01781         fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Vertex, VShader_MVP)); 
01782         fill->GetShaders()->SetShader(pRender->LoadBuiltinShader(Shader_Fragment, FShader_Texture)); 
01783         fill->SetTexture(0, imageTex);
01784         m->Fill = fill;
01785 
01786         LoadingScene.World.Add(m);
01787     }
01788 }
01789 
01790 void OculusWorldDemoApp::ClearScene()
01791 {
01792     MainScene.Clear();
01793     GridScene.Clear();
01794     YawMarkGreenScene.Clear();
01795     YawMarkRedScene.Clear();
01796     YawLinesScene.Clear();
01797 }
01798 
01799 void OculusWorldDemoApp::PopulateLODFileNames()
01800 {
01801     //OVR::String mainFilePath = MainFilePath;
01802     LODFilePaths.PushBack(MainFilePath);
01803     int   LODIndex = 1;
01804     SPInt pos = strcspn(MainFilePath.ToCStr(), ".");
01805     SPInt len = strlen(MainFilePath.ToCStr());
01806     SPInt diff = len - pos;
01807 
01808     if (diff == 0)
01809         return;    
01810 
01811     while(true)
01812     {
01813         char pathWithoutExt[250];
01814         char buffer[250];
01815         for(SPInt i = 0; i < pos; ++i)
01816         {
01817             pathWithoutExt[i] = MainFilePath[(int)i];
01818         }
01819         pathWithoutExt[pos] = '\0';
01820         OVR_sprintf(buffer, sizeof(buffer), "%s%i.xml", pathWithoutExt, LODIndex);
01821         FILE* fp = 0;
01822 #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
01823         errno_t err = fopen_s(&fp, buffer, "rb");
01824         if(!fp || err)
01825         {
01826 #else
01827         fp = fopen(buffer, "rb");
01828         if(!fp)
01829         {
01830 #endif
01831             break;
01832         }
01833         fclose(fp);
01834         OVR::String result = buffer;
01835         LODFilePaths.PushBack(result);
01836         LODIndex++;
01837     }
01838 }
01839 
01840 void OculusWorldDemoApp::DropLOD()
01841 {
01842     if(CurrentLODFileIndex < (int)(LODFilePaths.GetSize() - 1))
01843     {
01844         ClearScene();
01845         CurrentLODFileIndex++;
01846         PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
01847     }
01848 }
01849 
01850 void OculusWorldDemoApp::RaiseLOD()
01851 {
01852     if(CurrentLODFileIndex > 0)
01853     {
01854         ClearScene();
01855         CurrentLODFileIndex--;
01856         PopulateScene(LODFilePaths[CurrentLODFileIndex].ToCStr());
01857     }
01858 }
01859 
01860 //-----------------------------------------------------------------------------
01861 void OculusWorldDemoApp::CycleDisplay()
01862 {
01863     int screenCount = pPlatform->GetDisplayCount();
01864 
01865     // If Windowed, switch to the HMD screen first in Full-Screen Mode.
01866     // If already Full-Screen, cycle to next screen until we reach FirstScreenInCycle.
01867 
01868     if (pRender->IsFullscreen())
01869     {
01870         // Right now, we always need to restore window before going to next screen.
01871         pPlatform->SetFullscreen(RenderParams, Display_Window);
01872 
01873         Screen++;
01874         if (Screen == screenCount)
01875             Screen = 0;
01876 
01877         RenderParams.Display = pPlatform->GetDisplay(Screen);
01878 
01879         if (Screen != FirstScreenInCycle)
01880         {
01881             pRender->SetParams(RenderParams);
01882             pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
01883         }
01884     }
01885     else
01886     {
01887         // Try to find HMD Screen, making it the first screen in full-screen Cycle.        
01888         FirstScreenInCycle = 0;
01889 
01890         if (pHMD)
01891         {
01892             DisplayId HMD (SConfig.GetHMDInfo().DisplayDeviceName, SConfig.GetHMDInfo().DisplayId);
01893             for (int i = 0; i< screenCount; i++)
01894             {   
01895                 if (pPlatform->GetDisplay(i) == HMD)
01896                 {
01897                     FirstScreenInCycle = i;
01898                     break;
01899                 }
01900             }            
01901         }
01902 
01903         // Switch full-screen on the HMD.
01904         Screen = FirstScreenInCycle;
01905         RenderParams.Display = pPlatform->GetDisplay(Screen);
01906         pRender->SetParams(RenderParams);
01907         pPlatform->SetFullscreen(RenderParams, Display_Fullscreen);
01908     }
01909 }
01910 
01911 void OculusWorldDemoApp::GamepadStateChanged(const GamepadState& pad)
01912 {
01913     ThePlayer.GamepadMove   = Vector3f(pad.LX * pad.LX * (pad.LX > 0 ? 1 : -1),
01914                              0,
01915                              pad.LY * pad.LY * (pad.LY > 0 ? -1 : 1));
01916     ThePlayer.GamepadRotate = Vector3f(2 * pad.RX, -2 * pad.RY, 0);
01917 }
01918 
01919 
01920 //-------------------------------------------------------------------------------------
01921 
01922 OVR_PLATFORM_APP(OculusWorldDemoApp);


oculus_sdk
Author(s): Tully Foote
autogenerated on Thu Jun 6 2019 20:13:48