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"