diff --git a/CMakeLists.txt b/CMakeLists.txt index 09fea1e0e..08c9243f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ OPTION(BUILD_SPREADSHEET "Build the FreeCAD spreadsheet module" ON) OPTION(BUILD_START "Build the FreeCAD start module" ON) OPTION(BUILD_TEST "Build the FreeCAD test module" ON) OPTION(BUILD_WEB "Build the FreeCAD web module" ON) +OPTION(BUILD_VR "Build the FreeCAD Oculus Rift support (need Oculus SDK 4.x or higher)" OFF) if(MSVC) OPTION(FREECAD_USE_3DCONNEXION "Use the 3D connexion SDK to support 3d mouse." ON) @@ -647,6 +648,10 @@ else(FREECAD_LIBPACK_USE) endif(FREECAD_LIBPACK_USE) +if(BUILD_VR) + find_package(Rift) +endif(BUILD_VR) + # copy build convenient files for M$ if(WIN32) if (EXISTS BuildAll.bat) @@ -680,7 +685,7 @@ IF(MSVC) SET (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /EHa") SET (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFC_DEBUG") # set default libs - SET (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib ") + SET (CMAKE_C_STANDARD_LIBRARIES "kernel32.lib user32.lib gdi32.lib winspool.lib SHFolder.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib comsupp.lib Ws2_32.lib dbghelp.lib ") set (CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}") # set linker flag /nodefaultlib set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB") diff --git a/cMake/FindRift.cmake b/cMake/FindRift.cmake new file mode 100644 index 000000000..55dd21b51 --- /dev/null +++ b/cMake/FindRift.cmake @@ -0,0 +1,100 @@ +# Find OCULUS +# +# This module defines +# OCULUS_FOUND +# OCULUS_INCLUDE_DIRS +# OCULUS_LIBRARIES +# +# Copyright (c) 2012 I-maginer +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place - Suite 330, Boston, MA 02111-1307, USA, or go to +# http://www.gnu.org/copyleft/lesser.txt +# + +# On a new cmake run, we do not need to be verbose +IF(OCULUS_INCLUDE_DIR AND OCULUS_LIBRARY) + SET(OCULUS_FIND_QUIETLY FALSE) +ENDIF() + +# If OCULUS_ROOT was defined in the environment, use it. +if (NOT OCULUS_ROOT) + if(NOT "$ENV{OCULUS_ROOT}" STREQUAL "") + set(OCULUS_ROOT $ENV{OCULUS_ROOT}) + else() + set(OCULUS_ROOT $ENV{SCOL_DEPENDENCIES_PATH}/oculus/LibOVR) + endif() +endif() + +# concat all the search paths +IF(OCULUS_ROOT) + SET(OCULUS_INCLUDE_SEARCH_DIRS + ${OCULUS_INCLUDE_SEARCH_DIRS} + ${OCULUS_ROOT}/include + ) + SET(OCULUS_LIBRARY_SEARCH_RELEASE_DIRS + ${OCULUS_LIBRARY_SEARCH_DIRS} + ${OCULUS_ROOT}/Lib/x64/VS2012 + ) + SET(OCULUS_LIBRARY_SEARCH_DEBUG_DIRS + ${OCULUS_LIBRARY_SEARCH_DIRS} + ${OCULUS_ROOT}/Lib/x64/VS2012 + ) +ENDIF() + +# log message +IF (NOT OCULUS_FIND_QUIETLY) + MESSAGE(STATUS "Checking for OCULUS library") +ENDIF() + +# Search for header files +FIND_PATH(OCULUS_INCLUDE_DIR OVR.h + PATHS ${OCULUS_INCLUDE_SEARCH_DIRS}) + +# Search for libraries files (release mode) +FIND_LIBRARY(OCULUS_LIBRARY_RELEASE libovr64 + PATHS ${OCULUS_LIBRARY_SEARCH_RELEASE_DIRS}) + +# Search for libraries files (debug mode) +FIND_LIBRARY(OCULUS_LIBRARY_DEBUG libovr64d + PATHS ${OCULUS_LIBRARY_SEARCH_DEBUG_DIRS}) + +# Configure libraries for debug/release +SET(OCULUS_INCLUDE_DIRS ${OCULUS_INCLUDE_DIR} CACHE PATH "Directory containing OCULUS header files") +SET(OCULUS_LIBRARY debug ${OCULUS_LIBRARY_DEBUG} optimized ${OCULUS_LIBRARY_RELEASE}) +SET(OCULUS_LIBRARIES ${OCULUS_LIBRARY} CACHE STRING "OCULUS libraries files") + +#IF(OCULUS_INCLUDE_DIR AND OCULUS_LIBRARY) + SET(OCULUS_FOUND TRUE) +#ENDIF() + +# Hide those variables in GUI +SET(OCULUS_INCLUDE_DIR ${OCULUS_INCLUDE_DIR} CACHE INTERNAL "") +SET(OCULUS_LIBRARY_RELEASE ${OCULUS_LIBRARY_RELEASE} CACHE INTERNAL "") +SET(OCULUS_LIBRARY_DEBUG ${OCULUS_LIBRARY_DEBUG} CACHE INTERNAL "") +SET(OCULUS_LIBRARY ${OCULUS_LIBRARY} CACHE INTERNAL "") + +# log find result +IF(OCULUS_FOUND) + IF(NOT OCULUS_FIND_QUIETLY) + MESSAGE(STATUS " libraries: ${OCULUS_LIBRARIES}") + MESSAGE(STATUS " includes: ${OCULUS_INCLUDE_DIRS}") + ENDIF() +ELSE(OCULUS_FOUND) + IF(NOT OCULUS_LIBRARIES) + MESSAGE(STATUS, "OCULUS library or one of it dependencies could not be found.") + ENDIF() + IF(NOT OCULUS_INCLUDE_DIRS) + MESSAGE(STATUS "OCULUS include files could not be found.") + ENDIF() +ENDIF(OCULUS_FOUND) \ No newline at end of file diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index c6d379b05..722e875dd 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1,13 +1,17 @@ #add_subdirectory(Icons) if(WIN32) -add_definitions(-DFCGui -DQIIS_MAKEDLL) + add_definitions(-DFCGui -DQIIS_MAKEDLL -DOVR_OS_WIN32) endif(WIN32) if (FREECAD_USE_3DCONNEXION) -add_definitions(-D_USE_3DCONNEXION_SDK) + add_definitions(-D_USE_3DCONNEXION_SDK) endif(FREECAD_USE_3DCONNEXION) +if (BUILD_VR) + add_definitions(-DBUILD_VR ) +endif(BUILD_VR) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} @@ -61,6 +65,17 @@ IF(SPNAV_FOUND) ) ENDIF(SPNAV_FOUND) +IF(OCULUS_FOUND) + add_definitions(-DOCULUS_FOUND) + include_directories( + ${OCULUS_INCLUDE_DIRS} + ) + set(FreeCADGui_LIBS + ${FreeCADGui_LIBS} + ${OCULUS_LIBRARIES} + ) +ENDIF(OCULUS_FOUND) + if(SHIBOKEN_INCLUDE_DIR) add_definitions(-DHAVE_SHIBOKEN) include_directories( @@ -645,6 +660,8 @@ SET(View3D_CPP_SRCS View3DInventor.cpp View3DInventorExamples.cpp View3DInventorViewer.cpp + View3DInventorRiftViewer.cpp + CoinRiftWidget.cpp View3DPy.cpp ) SET(View3D_SRCS @@ -659,6 +676,9 @@ SET(View3D_SRCS View3DInventorExamples.h View3DInventorViewer.h View3DPy.h + View3DInventorRiftViewer.h + CoinRiftWidget.h + ) SOURCE_GROUP("View3D" FILES ${View3D_SRCS}) diff --git a/src/Gui/CoinRiftWidget.cpp b/src/Gui/CoinRiftWidget.cpp new file mode 100644 index 000000000..ba1b89ffd --- /dev/null +++ b/src/Gui/CoinRiftWidget.cpp @@ -0,0 +1,505 @@ +/**************************************************************************\ +* Copyright (c) Bastiaan Veelo (Bastiaan a_t Veelo d_o_t net) & Juergen Riegel (FreeCAD@juergen-riegel.net) +* All rights reserved. Contact me if the below is too restrictive for you. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\**************************************************************************/ + +#include "PreCompiled.h" +#include "CoinRiftWidget.h" + +#include + +#if BUILD_VR + + + +#undef max + + + + +CoinRiftWidget::CoinRiftWidget() : QGLWidget() +{ + for (int eye = 0; eye < 2; eye++) { + reinterpret_cast(&eyeTexture[eye])->TexId = 0; +#ifdef USE_FRAMEBUFFER + frameBufferID[eye] = 0; + depthBufferID[eye] = 0; +#endif + } + + // OVR will do the swapping. + setAutoBufferSwap(false); + + hmd = ovrHmd_Create(0); + if (!hmd) { + qDebug() << "Could not find Rift device."; + throw; + } + + if (!ovrHmd_ConfigureTracking (hmd, ovrTrackingCap_Orientation | + ovrTrackingCap_MagYawCorrection | + ovrTrackingCap_Position, + ovrTrackingCap_Orientation | + ovrTrackingCap_MagYawCorrection | + ovrTrackingCap_Position + )) { // Capabilities we require. + qDebug() << "Could not start Rift motion sensor."; + throw; + } + + resize(hmd->Resolution.w, hmd->Resolution.h); + + // Configure stereo settings. + ovrSizei recommenedTex0Size = ovrHmd_GetFovTextureSize(hmd, ovrEye_Left, + hmd->DefaultEyeFov[0], 1.0f); + ovrSizei recommenedTex1Size = ovrHmd_GetFovTextureSize(hmd, ovrEye_Right, + hmd->DefaultEyeFov[1], 1.0f); + +#ifdef USE_SO_OFFSCREEN_RENDERER + renderer = new SoOffscreenRenderer(SbViewportRegion(std::max(recommenedTex0Size.w, recommenedTex0Size.w), + std::max(recommenedTex1Size.h, recommenedTex1Size.h))); + renderer->setComponents(SoOffscreenRenderer::RGB_TRANSPARENCY); + BackgroundColor = SbColor(.0f, .0f, .8f); + renderer->setBackgroundColor(BackgroundColor); +#endif +#ifdef USE_FRAMEBUFFER + m_sceneManager = new SoSceneManager(); + m_sceneManager->setViewportRegion(SbViewportRegion(std::max(recommenedTex0Size.w, recommenedTex0Size.w), + std::max(recommenedTex1Size.h, recommenedTex1Size.h))); + m_sceneManager->setBackgroundColor(SbColor(.0f, .0f, .8f)); +#endif + basePosition = SbVec3f(0.0f, 0.0f, -2.0f); + + // light handling + SoDirectionalLight *light = new SoDirectionalLight(); + light->direction.setValue(1,-1,-1); + + SoDirectionalLight *light2 = new SoDirectionalLight(); + light2->direction.setValue(-1,-1,-1); + light2->intensity.setValue(0.6); + light2->color.setValue(0.8,0.8,1); + + + scene = new SoSeparator(0); // Placeholder. + for (int eye = 0; eye < 2; eye++) { + rootScene[eye] = new SoSeparator(); + rootScene[eye]->ref(); + camera[eye] = new SoFrustumCamera(); + camera[eye]->position.setValue(basePosition); + camera[eye]->focalDistance.setValue(5.0f); + camera[eye]->viewportMapping.setValue(SoCamera::LEAVE_ALONE); + rootScene[eye]->addChild(camera[eye]); + rootScene[eye]->addChild(light); + rootScene[eye]->addChild(light2); + rootScene[eye]->addChild(scene); + } + + // Populate ovrEyeDesc[2]. + eyeRenderDesc[0].Eye = ovrEye_Left; + eyeRenderDesc[1].Eye = ovrEye_Right; + eyeRenderDesc[0].Fov = hmd->DefaultEyeFov[0]; + eyeRenderDesc[1].Fov = hmd->DefaultEyeFov[1]; +#ifdef USE_SO_OFFSCREEN_RENDERER + eyeTexture[0].Header.TextureSize.w = renderer->getViewportRegion().getViewportSizePixels().getValue()[0]; + eyeTexture[0].Header.TextureSize.h = renderer->getViewportRegion().getViewportSizePixels().getValue()[1]; + eyeTexture[1].Header.TextureSize = eyeTexture[0].Header.TextureSize; +#endif +#ifdef USE_FRAMEBUFFER + eyeTexture[0].Header.TextureSize = recommenedTex0Size; + eyeTexture[1].Header.TextureSize = recommenedTex1Size; +#endif + eyeTexture[0].Header.RenderViewport.Pos.x = 0; + eyeTexture[0].Header.RenderViewport.Pos.y = 0; + eyeTexture[0].Header.RenderViewport.Size = eyeTexture[0].Header.TextureSize; + eyeTexture[1].Header.RenderViewport.Pos = eyeTexture[0].Header.RenderViewport.Pos; + eyeTexture[1].Header.RenderViewport.Size = eyeTexture[1].Header.TextureSize; + + const int backBufferMultisample = 0; // TODO This is a guess? + ovrGLConfig cfg; + cfg.OGL.Header.API = ovrRenderAPI_OpenGL; + cfg.OGL.Header.RTSize = hmd->Resolution; + cfg.OGL.Header.Multisample = backBufferMultisample; + cfg.OGL.Window = reinterpret_cast(winId()); + makeCurrent(); + //cfg.OGL.WglContext = wglGetCurrentContext(); // http://stackoverflow.com/questions/17532033/qglwidget-get-gl-contextes-for-windows + cfg.OGL.DC = wglGetCurrentDC(); + qDebug() << "Window:" << cfg.OGL.Window; + //qDebug() << "Context:" << cfg.OGL.WglContext; + qDebug() << "DC:" << cfg.OGL.DC; + + int DistortionCaps = 0; + DistortionCaps |= ovrDistortionCap_Chromatic; +// DistortionCaps |= ovrDistortionCap_TimeWarp; // Produces black screen... + DistortionCaps |= ovrDistortionCap_Vignette; + DistortionCaps |= ovrDistortionCap_HqDistortion; + + bool VSyncEnabled(false); // TODO This is a guess. + if (!ovrHmd_ConfigureRendering( hmd, + &cfg.Config, + /*(VSyncEnabled ? 0 : ovrHmdCap_NoVSync),*/ + DistortionCaps, + hmd->DefaultEyeFov,//eyes, + eyeRenderDesc)) { + qDebug() << "Could not configure OVR rendering."; + throw; + } + static const float nearPlane = 0.01; + + for (int eye = 0; eye < 2; eye++) { + camera[eye]->aspectRatio.setValue((eyeRenderDesc[eye].Fov.LeftTan + eyeRenderDesc[eye].Fov.RightTan) / + (eyeRenderDesc[eye].Fov.UpTan + eyeRenderDesc[eye].Fov.DownTan)); + camera[eye]->nearDistance.setValue(nearPlane); + camera[eye]->farDistance.setValue(10000.0f); + camera[eye]->left.setValue(-eyeRenderDesc[eye].Fov.LeftTan * nearPlane); + camera[eye]->right.setValue(eyeRenderDesc[eye].Fov.RightTan * nearPlane); + camera[eye]->top.setValue(eyeRenderDesc[eye].Fov.UpTan * nearPlane); + camera[eye]->bottom.setValue(-eyeRenderDesc[eye].Fov.DownTan * nearPlane); + } +} + + +CoinRiftWidget::~CoinRiftWidget() +{ +#ifdef USE_SO_OFFSCREEN_RENDERER + delete renderer; +#endif + for (int eye = 0; eye < 2; eye++) { + rootScene[eye]->unref(); + ovrGLTextureData *texData = reinterpret_cast(&eyeTexture[eye]); + if (texData->TexId) { + glDeleteTextures(1, &texData->TexId); + texData->TexId = 0; + } +#ifdef USE_FRAMEBUFFER + if (frameBufferID[eye] != 0) { +// OVR::CAPI::GL::glDeleteFramebuffersExt(1, &frameBufferID[eye]); // TODO + frameBufferID[eye] = 0; + } + if (depthBufferID[eye] != 0) { +// OVR::CAPI::GL::glDeleteRenderbuffersExt(1, &depthBufferID[eye]); // TODO + depthBufferID[eye] = 0; + } +#endif + } + scene = 0; + //ovrHmd_StopSensor(hmd); + ovrHmd_Destroy(hmd); +} + + +void CoinRiftWidget::setBackgroundColor(const SbColor &Col) +{ + BackgroundColor = Col; + renderer->setBackgroundColor(BackgroundColor); +} + + +void CoinRiftWidget::setSceneGraph(SoNode *sceneGraph) +{ + rootScene[0]->replaceChild(scene, sceneGraph); + rootScene[1]->replaceChild(scene, sceneGraph); + scene = sceneGraph; +} + + +void CoinRiftWidget::resizeGL(int width, int height) { + int side = qMin(width, height); + glViewport((width - side) / 2, (height - side) / 2, side, side); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1000.0); + glMatrixMode(GL_MODELVIEW); +} + +void CoinRiftWidget::initializeGL() +{ + makeCurrent(); + // Infer hardware capabilites. +#ifdef USE_FRAMEBUFFER + OVR::CAPI::GL::InitGLExtensions(); + if (OVR::CAPI::GL::glBindFramebuffer == NULL) { + qDebug() << "No GL extensions found."; + exit(4); + } + + // Store old framebuffer. + GLint oldfb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &oldfb); +#endif + + // Create rendering target textures. + glEnable(GL_TEXTURE_2D); + for (int eye = 0; eye < 2; eye++) { +#ifdef USE_FRAMEBUFFER + OVR::CAPI::GL::glGenFramebuffers(1, &frameBufferID[eye]); + OVR::CAPI::GL::glBindFramebuffer(GL_FRAMEBUFFER_EXT, frameBufferID[eye]); + // Create the render buffer. + // TODO: need to check for OpenGl 3 or higher and load the functions JR 2014 + /*OVR::CAPI::GL::*/glGenRenderbuffers(1, &depthBufferID[eye]); + /*OVR::CAPI::GL::*/glBindRenderbuffer(GL_RENDERBUFFER_EXT, depthBufferID[eye]); + /*OVR::CAPI::GL::*/glRenderbufferStorage(GL_RENDERBUFFER_EXT, + GL_DEPTH_COMPONENT16, + eyeTexture[eye].Header.TextureSize.w, + eyeTexture[eye].Header.TextureSize.h); + // Attach renderbuffer to framebuffer. + OVR::CAPI::GL::glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + depthBufferID[eye]); +#endif + ovrGLTextureData *texData = reinterpret_cast(&eyeTexture[eye]); + texData->Header.API = ovrRenderAPI_OpenGL; + texData->Header.TextureSize = eyeTexture[eye].Header.TextureSize; + texData->Header.RenderViewport = eyeTexture[eye].Header.RenderViewport; + glGenTextures(1, &texData->TexId); + glBindTexture(GL_TEXTURE_2D, texData->TexId); + Q_ASSERT(!glGetError()); + // Allocate storage for the texture. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, eyeTexture[eye].Header.TextureSize.w, eyeTexture[eye].Header.TextureSize.h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + Q_ASSERT(!glGetError()); +#ifdef USE_FRAMEBUFFER + // Attach texture to framebuffer color object. + OVR::CAPI::GL::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, texData->TexId, 0); + if (OVR::CAPI::GL::glCheckFramebufferStatus(GL_FRAMEBUFFER) != + GL_FRAMEBUFFER_COMPLETE) + qDebug() << "ERROR: FrameBuffer is not operational!"; +#endif + } + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + +#ifdef USE_FRAMEBUFFER + // Continue rendering to the orgiginal frame buffer (likely 0, the onscreen buffer). + OVR::CAPI::GL::glBindFramebuffer(GL_FRAMEBUFFER_EXT, oldfb); +#endif + doneCurrent(); +} + + +void CoinRiftWidget::paintGL() +{ + const int ms(1000 / 75 /*fps*/); + QTimer::singleShot(ms, this, SLOT(updateGL())); + + // handling the sfety warning + handlingSafetyWarning(); + + makeCurrent(); + + ovrPosef eyePose[2]; + + glEnable(GL_TEXTURE_2D); + + ovrFrameTiming hmdFrameTiming = ovrHmd_BeginFrame(hmd, 0); + for (int eyeIndex = 0; eyeIndex < ovrEye_Count; eyeIndex++) { + ovrEyeType eye = hmd->EyeRenderOrder[eyeIndex]; + eyePose[eye] = ovrHmd_GetEyePose(hmd, eye); + + + SbRotation riftOrientation( eyePose[eye].Orientation.x, + eyePose[eye].Orientation.y, + eyePose[eye].Orientation.z, + eyePose[eye].Orientation.w); + + camera[eye]->orientation.setValue(riftOrientation); + + SbVec3f riftPosition = SbVec3f(eyePose[eye].Position.x, + eyePose[eye].Position.y, + eyePose[eye].Position.z); + + + //SbVec3f originalPosition(camera[eye]->position.getValue()); + SbVec3f viewAdjust(eyeRenderDesc[eye].ViewAdjust.x, + eyeRenderDesc[eye].ViewAdjust.y, + eyeRenderDesc[eye].ViewAdjust.z); + + riftOrientation.multVec(viewAdjust,viewAdjust); + + camera[eye]->position.setValue(basePosition - viewAdjust + riftPosition); + + //Base::Console().Log("Eye(%d) Pos: %f, %f, %f ViewAdjust: %f, %f, %f \n",eye, eyePose[eye].Position.x, + // eyePose[eye].Position.y, + // eyePose[eye].Position.z, + // eyeRenderDesc[eye].ViewAdjust.x, + // eyeRenderDesc[eye].ViewAdjust.y, + // eyeRenderDesc[eye].ViewAdjust.z); + +#ifdef USE_SO_OFFSCREEN_RENDERER + ovrGLTextureData *texData = reinterpret_cast(&eyeTexture[eye]); + glBindTexture(GL_TEXTURE_2D, texData->TexId); + renderer->render(rootScene[eye]); + Q_ASSERT(!glGetError()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + eyeTexture[eye].Header.TextureSize.w, + eyeTexture[eye].Header.TextureSize.h, + 0, GL_RGBA /*GL_BGRA*/, GL_UNSIGNED_BYTE, renderer->getBuffer()); + Q_ASSERT(!glGetError()); + glBindTexture(GL_TEXTURE_2D, 0); +#endif +#ifdef USE_FRAMEBUFFER + // Clear state pollution from OVR SDK. + glBindTexture(GL_TEXTURE_2D, 0); // You need this, at least if (hmdDesc.DistortionCaps & ovrDistortion_Chromatic). + OVR::CAPI::GL::glUseProgram(0); // You need this even more. + + GLint oldfb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &oldfb); + // Set up framebuffer for rendering. + OVR::CAPI::GL::glBindFramebuffer(GL_FRAMEBUFFER_EXT, frameBufferID[eye]); + + m_sceneManager->setSceneGraph(rootScene[eye]); +// m_sceneManager->setCamera(camera[eye]); // SoSceneManager does this implicitly. + m_sceneManager->render(); + + // Continue rendering to the orgiginal frame buffer (likely 0, the onscreen buffer). + OVR::CAPI::GL::glBindFramebuffer(GL_FRAMEBUFFER_EXT, oldfb); + Q_ASSERT(!glGetError()); +#endif + + //camera[eye]->position.setValue(originalPosition); + + } + + // Submit the texture for distortion. + ovrHmd_EndFrame(hmd, eyePose, eyeTexture); + + // Swap buffers. + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + //ovrHmd_EndFrame(hmd); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glClearDepth(1.0); + + doneCurrent(); +} + +void CoinRiftWidget::handlingSafetyWarning(void) +{ + // Health and Safety Warning display state. + ovrHSWDisplayState hswDisplayState; + ovrHmd_GetHSWDisplayState(hmd, &hswDisplayState); + if (hswDisplayState.Displayed) + { + // Dismiss the warning if the user pressed the appropriate key or if the user + // is tapping the side of the HMD. + // If the user has requested to dismiss the warning via keyboard or controller input... + //if (Util_GetAndResetHSWDismissedState()) + ovrHmd_DismissHSWDisplay(hmd); + //else + //{ + // // Detect a moderate tap on the side of the HMD. + // ovrTrackingState ts = ovrHmd_GetTrackingState(hmd, ovr_GetTimeInSeconds()); + // if (ts.StatusFlags & ovrStatus_OrientationTracked) + // { + // const OVR::Vector3f v(ts.RawSensorData.Accelerometer.x, + // ts.RawSensorData.Accelerometer.y, + // ts.RawSensorData.Accelerometer.z); + // // Arbitrary value and representing moderate tap on the side of the DK2 Rift. + // if (v.LengthSq() > 250.f) + // ovrHmd_DismissHSWDisplay(hmd); + // } + //} + } + +} + + +#ifdef BUILD_RIFT_TEST_MAIN + +int main(int argc, char *argv[]) +{ + SoDB::init(); + + QApplication app(argc, argv); + qAddPostRoutine(cleanup); + + // Moved here because of https://developer.oculusvr.com/forums/viewtopic.php?f=17&t=7915&p=108503#p108503 + // Init libovr. + if (!ovr_Initialize()) { + qDebug() << "Could not initialize Oculus SDK."; + exit(1); + } + + CoinRiftWidget window; + window.show(); + + // An example scene. + static const char * inlineSceneGraph[] = { + "#Inventor V2.1 ascii\n", + "\n", + "Separator {\n", + " Rotation { rotation 1 0 0 0.3 }\n", + " Cone { }\n", + " BaseColor { rgb 1 0 0 }\n", + " Scale { scaleFactor .7 .7 .7 }\n", + " Cube { }\n", + "\n", + " DrawStyle { style LINES }\n", + " ShapeHints { vertexOrdering COUNTERCLOCKWISE }\n", + " Coordinate3 {\n", + " point [\n", + " -2 -2 1.1, -2 -1 1.1, -2 1 1.1, -2 2 1.1,\n", + " -1 -2 1.1, -1 -1 1.1, -1 1 1.1, -1 2 1.1\n", + " 1 -2 1.1, 1 -1 1.1, 1 1 1.1, 1 2 1.1\n", + " 2 -2 1.1, 2 -1 1.1, 2 1 1.1, 2 2 1.1\n", + " ]\n", + " }\n", + "\n", + " Complexity { value 0.7 }\n", + " NurbsSurface {\n", + " numUControlPoints 4\n", + " numVControlPoints 4\n", + " uKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n", + " vKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n", + " }\n", + "}\n", + NULL + }; + + SoInput in; + in.setStringArray(inlineSceneGraph); + + window.setSceneGraph(SoDB::readAll(&in)); + + return app.exec(); +} + +#endif //BUILD_RIFT_TEST_MAIN + +#endif //BUILD_VR \ No newline at end of file diff --git a/src/Gui/CoinRiftWidget.h b/src/Gui/CoinRiftWidget.h new file mode 100644 index 000000000..343d58775 --- /dev/null +++ b/src/Gui/CoinRiftWidget.h @@ -0,0 +1,118 @@ +/**************************************************************************\ +* Copyright (c) Bastiaan Veelo (Bastiaan a_t Veelo d_o_t net) & Juergen Riegel (FreeCAD@juergen-riegel.net) +* All rights reserved. Contact me if the below is too restrictive for you. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\**************************************************************************/ +#ifndef GUI_CoinRiftWidget +#define GUI_CoinRiftWidget + +#if BUILD_VR + +// defines which methode to use to render +#define USE_SO_OFFSCREEN_RENDERER +//#define USE_FRAMEBUFFER + +#ifdef USE_SO_OFFSCREEN_RENDERER +# ifdef USE_FRAMEBUFFER +# error "Mutually exclusive options defined." +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_SO_OFFSCREEN_RENDERER +# include +#endif +#ifdef USE_FRAMEBUFFER +# include +#endif +#include +#include +#include + +#include +#include +#include +//#include +#include <../Src/OVR_CAPI_GL.h> +#include <../Src/CAPI/GL/CAPI_GL_Util.h> // For framebuffer functions. + + + +class CoinRiftWidget : public QGLWidget +{ + ovrHmd hmd; + //ovrHmdDesc hmdDesc; + //ovrEyeType eyes[2]; + ovrEyeRenderDesc eyeRenderDesc[2]; + ovrTexture eyeTexture[2]; + +#ifdef USE_FRAMEBUFFER + GLuint frameBufferID[2], depthBufferID[2]; + // A SoSceneManager has a SoRenderManager to do the rendering -- should we not use SoRenderManager instead? + // We are probably not that interested in events. SoSceneManager::setSceneGraph() searches for the camera + // and sets it in SoRenderManager, but its is actually only used for built-in stereo rendering. We sould + // probably eliminate that search... + SoSceneManager *m_sceneManager; +#endif +#ifdef USE_SO_OFFSCREEN_RENDERER + SoOffscreenRenderer *renderer; +#endif + SoSeparator *rootScene[2]; + SoFrustumCamera *camera[2]; + SoNode *scene; +public: + explicit CoinRiftWidget(); + ~CoinRiftWidget(); + virtual void setSceneGraph(SoNode *sceneGraph); + void setBase(const SbVec3f &pos){basePosition=pos;} + void setBackgroundColor(const SbColor &Col); + + SbVec3f basePosition; + SbRotation baseOrientation; + +protected: + void handlingSafetyWarning(void); + void initializeGL(); + void paintGL(); + void resizeGL(int width, int height); + + SbColor BackgroundColor; + SoTranslation *lightTranslation; +}; + + +#endif //BUILD_VR + +#endif GUI_CoinRiftWidget \ No newline at end of file diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index 8afd1aa77..8bd70449b 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -1408,6 +1408,37 @@ bool StdViewDockUndockFullscreen::isActive(void) return false; } + +//=========================================================================== +// Std_ViewVR +//=========================================================================== +DEF_STD_CMD_A(StdCmdViewVR); + +StdCmdViewVR::StdCmdViewVR() + : Command("Std_ViewVR") +{ + sGroup = QT_TR_NOOP("Standard-View"); + sMenuText = QT_TR_NOOP("FreeCAD-VR"); + sToolTipText = QT_TR_NOOP("Extend the FreeCAD 3D Window to a Oculus Rift"); + sWhatsThis = "Std_ViewVR"; + sStatusTip = QT_TR_NOOP("Extend the FreeCAD 3D Window to a Oculus Rift"); + sPixmap = "view-zoom-all"; + eType = Alter3DView; +} + +void StdCmdViewVR::activated(int iMsg) +{ + //doCommand(Command::Gui,"Gui.activeDocument().activeView().fitAll()"); + doCommand(Command::Gui,"Gui.SendMsgToActiveView(\"ViewVR\")"); +} + +bool StdCmdViewVR::isActive(void) +{ + return getGuiApplication()->sendHasMsgToActiveView("ViewVR"); +} + + + //=========================================================================== // Std_ViewScreenShot //=========================================================================== @@ -2453,6 +2484,7 @@ void CreateViewStdCommands(void) rcCmdMgr.addCommand(new StdCmdViewTop()); rcCmdMgr.addCommand(new StdCmdViewAxo()); rcCmdMgr.addCommand(new StdCmdViewFitAll()); + rcCmdMgr.addCommand(new StdCmdViewVR()); rcCmdMgr.addCommand(new StdCmdViewFitSelection()); rcCmdMgr.addCommand(new StdCmdViewRotateLeft()); rcCmdMgr.addCommand(new StdCmdViewRotateRight()); diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 61d417f79..153cf3402 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -366,7 +366,7 @@ void View3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::M _viewer->turnDeltaDimensionsOn(); else _viewer->turnDeltaDimensionsOff(); - } + } else{ unsigned long col1 = rGrp.GetUnsigned("BackgroundColor",3940932863UL); unsigned long col2 = rGrp.GetUnsigned("BackgroundColor2",859006463UL); // default color (dark blue) @@ -545,6 +545,11 @@ bool View3DInventor::onMsg(const char* pMsg, const char** ppReturn) _viewer->viewAll(); return true; } + else if (strcmp("ViewVR",pMsg) == 0) { + // call the VR portion of the viewer + _viewer->viewVR(); + return true; + } else if(strcmp("ViewSelection",pMsg) == 0) { _viewer->viewSelection(); return true; @@ -728,6 +733,12 @@ bool View3DInventor::onHasMsg(const char* pMsg) const return true; else if(strcmp("ViewFit",pMsg) == 0) return true; + else if(strcmp("ViewVR",pMsg) == 0) +#ifdef BUILD_VR + return true; +#else + return false; +#endif else if(strcmp("ViewSelection",pMsg) == 0) return true; else if(strcmp("ViewBottom",pMsg) == 0) diff --git a/src/Gui/View3DInventorRiftViewer.cpp b/src/Gui/View3DInventorRiftViewer.cpp new file mode 100644 index 000000000..5d8613411 --- /dev/null +++ b/src/Gui/View3DInventorRiftViewer.cpp @@ -0,0 +1,228 @@ +/*************************************************************************** + * Copyright (c) 2014 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + + +#if BUILD_VR + +#include +#include "View3DInventorRiftViewer.h" +#include + +#define new DEBUG_CLIENTBLOCK + +using namespace Gui; + +View3DInventorRiftViewer::View3DInventorRiftViewer() : CoinRiftWidget() +{ + workplace = new SoGroup(); + + //translation = new SoTranslation ; + //translation->translation.setValue(0,-1,0); + //workplace->addChild(translation); + + rotation1 = new SoRotationXYZ ; + rotation1->axis.setValue(SoRotationXYZ::X); + rotation1->angle.setValue(-M_PI/2); + workplace->addChild(rotation1); + + rotation2 = new SoRotationXYZ ; + rotation2->axis.setValue(SoRotationXYZ::Z); + rotation2->angle.setValue(0); + workplace->addChild(rotation2); + + + scale = new SoScale ; + scale->scaleFactor.setValue(0.001f,0.001f,0.001f); // scale from mm to m as neede by the Rift + workplace->addChild(scale); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Oculus"); + + this->setGeometry( hGrp->GetInt("RenderWindowPosX",100) , + hGrp->GetInt("RenderWindowPosY",100) , + hGrp->GetInt("RenderWindowSizeW",1920) , + hGrp->GetInt("RenderWindowSizeH",1080) + ); + + + setBackgroundColor(SbColor(51,51,101)); + basePosition = SbVec3f(0.0f, 0.5f, 0.8f); +} + +//void saveWinPostion(void) +//{ +// +// +// +//} + +View3DInventorRiftViewer::~View3DInventorRiftViewer() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Oculus"); + + // remeber last postion on close + hGrp->SetInt("RenderWindowPosX",pos().x()); + hGrp->SetInt("RenderWindowPosY",pos().y()); + hGrp->SetInt("RenderWindowSizeW",size().width()); + hGrp->SetInt("RenderWindowSizeH",size().height()); + + Base::Console().Log("pos: %d %d size: %d %d \n",pos().x(),pos().y(), + size().width(),size().height()); +} + +void View3DInventorRiftViewer::setSceneGraph(SoNode *sceneGraph) +{ + + workplace->addChild(sceneGraph); + + CoinRiftWidget::setSceneGraph(workplace); +} + + + + +void View3DInventorRiftViewer::keyPressEvent(QKeyEvent *event) +{ + static const float increment = 0.02; // move two centimeter per key + static const float rotIncrement = M_PI/4; // move two 90° per key + + + if (event->key() == Qt::Key_Plus) { + scale->scaleFactor.setValue(scale->scaleFactor.getValue() * 2.0f) ; + } else if (event->key() == Qt::Key_Minus) { + scale->scaleFactor.setValue(scale->scaleFactor.getValue() * 0.2f) ; + } else if (event->key() == Qt::Key_S) { + basePosition += SbVec3f(0,0,increment) ; + } else if (event->key() == Qt::Key_W) { + basePosition += SbVec3f(0,0,-increment) ; + } else if (event->key() == Qt::Key_Up) { + basePosition += SbVec3f(0,-increment,0) ; + } else if (event->key() == Qt::Key_Down) { + basePosition += SbVec3f(0,increment,0) ; + } else if (event->key() == Qt::Key_Left) { + rotation2->angle.setValue( rotation2->angle.getValue() + rotIncrement); + } else if (event->key() == Qt::Key_Right) { + rotation2->angle.setValue( rotation2->angle.getValue() - rotIncrement); + } else if (event->key() == Qt::Key_A) { + basePosition += SbVec3f(-increment,0,0) ; + } else if (event->key() == Qt::Key_D) { + basePosition += SbVec3f(increment,0,0) ; + } else { + + CoinRiftWidget::keyPressEvent(event); + } +} + + + + +// static test code ================================================================================ +static View3DInventorRiftViewer *window=0; + +void oculusSetTestScene(View3DInventorRiftViewer *window) +{ + assert(window); + // An example scene. + static const char * inlineSceneGraph[] = { + "#Inventor V2.1 ascii\n", + "\n", + "Separator {\n", + " Rotation { rotation 1 0 0 0.3 }\n", + " Cone { }\n", + " BaseColor { rgb 1 0 0 }\n", + " Scale { scaleFactor .7 .7 .7 }\n", + " Cube { }\n", + "\n", + " DrawStyle { style LINES }\n", + " ShapeHints { vertexOrdering COUNTERCLOCKWISE }\n", + " Coordinate3 {\n", + " point [\n", + " -2 -2 1.1, -2 -1 1.1, -2 1 1.1, -2 2 1.1,\n", + " -1 -2 1.1, -1 -1 1.1, -1 1 1.1, -1 2 1.1\n", + " 1 -2 1.1, 1 -1 1.1, 1 1 1.1, 1 2 1.1\n", + " 2 -2 1.1, 2 -1 1.1, 2 1 1.1, 2 2 1.1\n", + " ]\n", + " }\n", + "\n", + " Complexity { value 0.7 }\n", + " NurbsSurface {\n", + " numUControlPoints 4\n", + " numVControlPoints 4\n", + " uKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n", + " vKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n", + " }\n", + "}\n", + NULL + }; + + SoInput in; + in.setStringArray(inlineSceneGraph); + + window->setSceneGraph(SoDB::readAll(&in)); +} + + +void oculusStop() +{ + //SoDB::finish(); + if(window){ + delete window; + window = 0; + ovr_Shutdown(); + } + +} + +bool oculusUp(void) +{ + return window!=0; +} + +View3DInventorRiftViewer* oculusStart(void) +{ + //SoDB::init(); + + //QApplication app(argc, argv); + //qAddPostRoutine(cleanup); + + // Moved here because of https://developer.oculusvr.com/forums/viewtopic.php?f=17&t=7915&p=108503#p108503 + // Init libovr. + if (!ovr_Initialize()) { + qDebug() << "Could not initialize Oculus SDK."; + return 0; + } + if(window) + return window; + + window = new View3DInventorRiftViewer; + window->show(); + + + return window; + //return app.exec(); +} + + + +#endif //BUILD_VR \ No newline at end of file diff --git a/src/Gui/View3DInventorRiftViewer.h b/src/Gui/View3DInventorRiftViewer.h new file mode 100644 index 000000000..146684ab5 --- /dev/null +++ b/src/Gui/View3DInventorRiftViewer.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2014 Juergen Riegel * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef GUI_View3DInventorRiftViewer_H +#define GUI_View3DInventorRiftViewer_H + +#if BUILD_VR + +#include "CoinRiftWidget.h" + +namespace Gui { + +class View3DInventorRiftViewer : public CoinRiftWidget +{ +public: + View3DInventorRiftViewer(); + ~View3DInventorRiftViewer(); + + virtual void setSceneGraph(SoNode *sceneGraph); + +protected: + SoGroup *workplace; + SoTranslation *translation; + SoRotationXYZ *rotation1; + SoRotationXYZ *rotation2; + SoScale *scale; + +protected: + void keyPressEvent(QKeyEvent *); +}; + + +} //namespace Gui + +#endif //BUILD_VR + +#endif //GUI_View3DInventorRiftViewer_H \ No newline at end of file diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index e7563f3c4..be958c4cf 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -103,6 +103,7 @@ #include "SoFCInteractiveElement.h" #include "SoFCBoundingBox.h" #include "SoAxisCrossKit.h" +#include "View3DInventorRiftViewer.h" #include "Selection.h" #include "SoFCSelectionAction.h" @@ -1636,6 +1637,27 @@ void View3DInventorViewer::animatedViewAll(int steps, int ms) } } +#if BUILD_VR +extern View3DInventorRiftViewer* oculusStart(void); +extern bool oculusUp (void); +extern void oculusStop (void); +void oculusSetTestScene(View3DInventorRiftViewer *window); +#endif + +void View3DInventorViewer::viewVR(void) +{ +#if BUILD_VR + if(oculusUp()) + oculusStop(); + else{ + View3DInventorRiftViewer* riftWin = oculusStart(); + riftWin->setSceneGraph(pcViewProviderRoot); + } +#endif +} + + + void View3DInventorViewer::boxZoom(const SbBox2s& box) { navigation->boxZoom(box); diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 1fe23218c..733059a38 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -310,6 +310,10 @@ public: */ void viewAll(); void viewAll(float factor); + + /// Breaks out a VR window for a Rift + void viewVR(void); + /** * Reposition the current camera so we can see all selected objects * of the scene. Therefore we search for all SOFCSelection nodes, if diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index fa9c70c2f..df1906121 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -487,7 +487,11 @@ MenuItem* StdWorkbench::setupMenuBar() const *view << "Std_ViewCreate" << "Std_OrthographicCamera" << "Std_PerspectiveCamera" << "Separator" << stdviews << "Std_FreezeViews" << "Std_DrawStyle" << "Separator" << view3d << zoom << "Std_ViewDockUndockFullscreen" << "Std_AxisCross" << "Std_ToggleClipPlane" - << "Std_TextureMapping" << "Separator" << visu + << "Std_TextureMapping" +#ifdef BUILD_VR + << "Std_ViewVR" +#endif + << "Separator" << visu << "Std_ToggleVisibility" << "Std_ToggleNavigation" << "Std_SetAppearance" << "Std_RandomColor" << "Separator" << "Std_MeasureDistance" << "Separator"