diff options
author | Brad Davis <[email protected]> | 2013-10-13 20:28:58 -0700 |
---|---|---|
committer | Brad Davis <[email protected]> | 2013-10-13 20:28:58 -0700 |
commit | 672cdd0ef5455cd62a0d7f7eb6b9889f3ea35f21 (patch) | |
tree | f3454dc864242744aef1ea5474b5011556d0f388 /Samples/OculusRoomTiny/OculusRoomTiny.cpp | |
parent | 1408ed7b208c7a1cff1a2448fc890e9b8bd6dc4e (diff) |
Updating to cmake, glew, xrandr
Diffstat (limited to 'Samples/OculusRoomTiny/OculusRoomTiny.cpp')
-rw-r--r-- | Samples/OculusRoomTiny/OculusRoomTiny.cpp | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny.cpp b/Samples/OculusRoomTiny/OculusRoomTiny.cpp new file mode 100644 index 0000000..6b5ea2d --- /dev/null +++ b/Samples/OculusRoomTiny/OculusRoomTiny.cpp @@ -0,0 +1,538 @@ +/************************************************************************************ + +Filename : Win32_OculusRoomTiny.cpp +Content : First-person view test application for Oculus Rift +Created : October 4, 2012 +Authors : Michael Antonov, Andrew Reisse + +Copyright : Copyright 2012 Oculus, 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 "OculusRoomTiny.h" +#include "RenderTiny_Device.h" +#include "MessageBox.h" + +void glfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + OculusRoomTinyApp * instance = (OculusRoomTinyApp *)glfwGetWindowUserPointer(window); + instance->OnKey(key, action == GLFW_PRESS || action == GLFW_REPEAT); +} + +void glfwErrorCallback(int error, const char* description) { + LogText("Error code %d, message: %s", error, description); + exit(1); +} + +//------------------------------------------------------------------------------------- +// ***** OculusRoomTiny Class + +OculusRoomTinyApp::OculusRoomTinyApp() : + window(0), Width(1280), Height(800), Quit(false), LastUpdate(0), StartupTicks(OVR::Timer::GetTicks()), EyePos(0.0f, 1.6f, -5.0f), EyeYaw(YawInitial), + EyePitch(0), EyeRoll(0), LastSensorYaw(0), MoveForward(0), MoveBack(0), MoveLeft(0), MoveRight(0), ShiftDown(0), ControlDown(0), PostProcess(PostProcess_Distortion) +{ +} + +OculusRoomTinyApp::~OculusRoomTinyApp() +{ + RemoveHandlerFromDevices(); + pSensor.Clear(); + pHMD.Clear(); + destroyWindow(); +} + +bool OculusRoomTinyApp::setupWindow() +{ + glfwInit(); + glfwWindowHint(GLFW_DEPTH_BITS, 16); + glfwWindowHint(GLFW_DECORATED, 0); + window = glfwCreateWindow(Width, Height, "OculusRoomTiny", NULL, NULL); + assert(window != 0); + glfwSetWindowPos(window, HMDInfo.DesktopX, HMDInfo.DesktopY); + glfwSetWindowUserPointer(window, this); + glfwSetKeyCallback(window, glfwKeyCallback); + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + glewInit(); + return window != 0; +} + +void OculusRoomTinyApp::destroyWindow() +{ + pRender.Clear(); + + if (window) + { + // Release window resources. + glfwDestroyWindow(window); + window = 0; + Width = Height = 0; + } +} + +int OculusRoomTinyApp::OnStartup(const char* args) +{ + OVR_UNUSED(args); + // *** 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); + + bool initDone = false; + while (!initDone) { + // Release Sensor/HMD in case this is a retry. + pSensor.Clear(); + pHMD.Clear(); + RenderParams.MonitorName.Clear(); + + pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice(); + if (pHMD) + { + pSensor = *pHMD->GetSensor(); + + // This will initialize HMDInfo with information about configured IPD, + // screen size and other variables needed for correct projection. + // We pass HMD DisplayDeviceName into the renderer to select the + // correct monitor in full-screen mode. + if (pHMD->GetDeviceInfo(&HMDInfo)) + { + RenderParams.MonitorName = HMDInfo.DisplayDeviceName; + RenderParams.DisplayId = HMDInfo.DisplayId; + SConfig.SetHMDInfo(HMDInfo); + } + } + else + { + // If we didn't detect an HMD, try to create the sensor directly. + // This is useful for debugging sensor interaction; it is not needed in + // a shipping app. + pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice(); + } + + + const char* detectionMessage; + + if (!pHMD && !pSensor) + detectionMessage = "Oculus Rift not detected."; + else if (!pHMD) + detectionMessage = "Oculus Sensor detected; HMD Display not detected."; + else if (!pSensor) + detectionMessage = "Oculus HMD Display detected; Sensor not detected."; + else if (HMDInfo.DisplayDeviceName[0] == '\0') + detectionMessage = "Oculus Sensor detected; HMD display EDID not detected."; + else + detectionMessage = 0; + + if (detectionMessage) { + String messageText(detectionMessage); + messageText += "\n\n" + "Press 'Try Again' to run retry detection.\n" + "Press 'Continue' to run full-screen anyway."; + MessageBoxResult result = MessageBox(messageText); + // Yeah, this is confusing, but it's correct. + switch (result) { + // semantic Retry means 'continue the loop' + case Retry: + continue; + + case Cancel: + return 1; + + // semantic Continue means exit the loop + case Continue: + initDone = true; + break; + } + } else { + initDone = true; + } + } + + if (HMDInfo.HResolution > 0) + { + Width = HMDInfo.HResolution; + Height = HMDInfo.VResolution; + } + + if (!setupWindow()) + return 1; + + 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 + + // Enable multi-sampling by default. + RenderParams.Multisample = 4; + RenderParams.Fullscreen = true; + + // *** 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); + } + + SConfig.Set2DAreaFov(DegreeToRad(85.0f)); + + // Setup Graphics. + pRender = *RenderTiny::RenderDevice::CreateDevice(RenderParams, window); + if (!pRender) + return 1; + pRender->SetSceneRenderScale(SConfig.GetDistortionScale()); + pRender->SetWindowSize(0, 0); + + // *** Populate Room Scene + + // This creates lights and models. + PopulateRoomScene(&Scene, pRender); + + LastUpdate = GetAppTime(); + return 0; +} + +void OculusRoomTinyApp::OnMessage(const Message& msg) +{ + if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager) + { + LogText("DeviceManager reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager) + { + LogText("DeviceManager reported device removed.\n"); + } + else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor) + { + LogText("Sensor reported device added.\n"); + } + else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor) + { + LogText("Sensor reported device removed.\n"); + } +} + + +void OculusRoomTinyApp::OnGamepad(float padLx, float padLy, float padRx, float padRy) +{ + GamepadMove = Vector3f(padLx * padLx * (padLx > 0 ? 1 : -1), + 0, + padLy * padLy * (padLy > 0 ? -1 : 1)); + GamepadRotate = Vector3f(2 * padRx, -2 * padRy, 0); +} + +void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers) +{ + OVR_UNUSED(modifiers); + + // Mouse motion here is always relative. + 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. + EyeYaw -= (Sensitivity * dx)/ 360.0f; + + if (!pSensor) + { + EyePitch -= (Sensitivity * dy)/ 360.0f; + + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } +} + +void OculusRoomTinyApp::OnKey(unsigned vk, bool down) +{ + switch (vk) + { + case GLFW_KEY_Q: + if (down && ControlDown) + Quit = true; + break; + case GLFW_KEY_ESCAPE: + if (!down) + Quit = true; + break; + + // Handle player movement keys. + // We just update movement state here, while the actual translation is done in OnIdle() + // based on time. + case GLFW_KEY_W: MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break; + case GLFW_KEY_S: MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break; + case GLFW_KEY_A: MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break; + case GLFW_KEY_D: MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break; + case GLFW_KEY_UP: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break; + case GLFW_KEY_DOWN: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break; + + case GLFW_KEY_R: + SFusion.Reset(); + break; + + case GLFW_KEY_P: + if (down) + { + // Toggle chromatic aberration correction on/off. + RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader(); + + if (shader == RenderDevice::PostProcessShader_Distortion) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb); + } + else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb) + { + pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion); + } + else + OVR_ASSERT(false); + } + break; + + // Switch rendering modes/distortion. + case GLFW_KEY_F1: + SConfig.SetStereoMode(Stereo_None); + PostProcess = PostProcess_None; + break; + case GLFW_KEY_F2: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_None; + break; + case GLFW_KEY_F3: + SConfig.SetStereoMode(Stereo_LeftRight_Multipass); + PostProcess = PostProcess_Distortion; + break; + + // Stereo IPD adjustments, in meter (default IPD is 64mm). + case GLFW_KEY_KP_ADD: + case GLFW_KEY_INSERT: + if (down) + SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + case GLFW_KEY_KP_SUBTRACT: + case GLFW_KEY_DELETE: + if (down) + SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f)); + break; + + case GLFW_KEY_BACKSLASH: + if (down) + // Swap eye positions. + SConfig.SetIPD(SConfig.GetIPD() * -1); + break; + + // Holding down Shift key accelerates adjustment velocity. + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + ShiftDown = down; + break; + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + ControlDown = down; + break; + } +} + + +void OculusRoomTinyApp::OnIdle() +{ + double curtime = GetAppTime(); + float dt = float(curtime - LastUpdate); + LastUpdate = curtime; + + // 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.GetOrientation(); + float yaw = 0.0f; + + hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll); + + EyeYaw += (yaw - LastSensorYaw); + LastSensorYaw = yaw; + } + + // Gamepad rotation. + EyeYaw -= GamepadRotate.x * dt; + + if (!pSensor) + { + // Allow gamepad to look up/down, but only if there is no Rift sensor. + EyePitch -= GamepadRotate.y * dt; + + const float maxPitch = ((3.1415f/2)*0.98f); + if (EyePitch > maxPitch) + EyePitch = maxPitch; + if (EyePitch < -maxPitch) + EyePitch = -maxPitch; + } + + // Handle keyboard movement. + // This translates EyePos based on Yaw vector direction and keys pressed. + // Note that Pitch and Roll do not affect movement (they only affect view). + if (MoveForward || MoveBack || MoveLeft || MoveRight) + { + Vector3f localMoveVector(0,0,0); + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + + if (MoveForward) + localMoveVector = ForwardVector; + else if (MoveBack) + localMoveVector = -ForwardVector; + + if (MoveRight) + localMoveVector += RightVector; + else if (MoveLeft) + localMoveVector -= RightVector; + + // Normalize vector so we don't move faster diagonally. + localMoveVector.Normalize(); + Vector3f orientationVector = yawRotate.Transform(localMoveVector); + orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f); + + EyePos += orientationVector; + } + + else if (GamepadMove.LengthSq() > 0) + { + Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + Vector3f orientationVector = yawRotate.Transform(GamepadMove); + orientationVector *= MoveSpeed * dt; + EyePos += orientationVector; + } + + + // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates. + // + Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) * + Matrix4f::RotationZ(EyeRoll); + Vector3f up = rollPitchYaw.Transform(UpVector); + Vector3f forward = rollPitchYaw.Transform(ForwardVector); + + + // Minimal head modelling. + 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 = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame); + shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height + + View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up); + + // This is what transformation would be without head modeling. + // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up); + + switch(SConfig.GetStereoMode()) + { + case Stereo_None: + Render(SConfig.GetEyeRenderParams(StereoEye_Center)); + break; + + case Stereo_LeftRight_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(); +} + +// Render the scene for one eye. +void OculusRoomTinyApp::Render(const StereoEyeParams& stereo) +{ + pRender->BeginScene(PostProcess); + + // Apply Viewport/Projection for the eye. + pRender->ApplyStereoParams(stereo); + pRender->Clear(); + pRender->SetDepthMode(true, true); + + Scene.Render(pRender, stereo.ViewAdjust * View); + + pRender->FinishScene(); +} + + + + +int OculusRoomTinyApp::Run() +{ + pRender->SetWindowSize(Width, Height); + pRender->SetViewport(0, 0, Width, Height); + while (!Quit) { + glfwPollEvents(); + OnIdle(); + } + return 0; +} + +//------------------------------------------------------------------------------------- +// ***** Program Startup +#ifdef WIN32 +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +#else +int main(int argc, char ** argv) +#endif + +{ + int exitCode = 0; + + // Initializes LibOVR. This LogMask_All enables maximum logging. + // Custom allocator can also be specified here. + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + + // Scope to force application destructor before System::Destroy. + { + OculusRoomTinyApp app; + exitCode = app.OnStartup(0); + if (!exitCode) + { + // Processes messages and calls OnIdle() to do rendering. + exitCode = app.Run(); + } + } + + // No OVR functions involving memory are allowed after this. + OVR::System::Destroy(); + return exitCode; +} |