Commit a38815b8 authored by Ken Martin's avatar Ken Martin
Browse files

Add support for OpenVR and Oculus to VTK

This topic adds support for VTK rendering using the
oculus or OpenVR/Vive VR headsets. It also adds some
core support classes that both these devices use to
add support for 3D interaction events and picking via

  vtkRenderWindowInteractor3D
  vtkPropPicker3D
  vtkInteractorStyle3D

These modules are designed to use the object factory
mechanism to override the standard OpenGL RenderWindow.
If needed, the override of the factory can be turned off
Off is useful for apps that have a normal OpenGL window
but may want to explicitly create an occulus or openVR
window as a result of user input such as a menu option.

This topic adds CMake find modules for SDL2, OculusSDK,
and the OpenVR SDK.
parent 5df53050
Pipeline #23845 passed with stage
# - try to find the Oculus SDK - currently designed for the version 1.6
#
# Cache Variables: (probably not for direct use in your scripts)
# OCULUS_ROOT
#
# Non-cache variables you might use in your CMakeLists.txt:
# OCULUS_FOUND
# OCULUS_INCLUDE_DIRS
# OCULUS_LIBS
# OCULUS_PLATFORM - something like Win32, x64, etc.
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
set(OCULUS_ROOT_DIR
"${OCULUS_ROOT_DIR}"
CACHE
PATH
"Directory to search for Oculus SDK")
set(_root_dirs)
if(OCULUS_ROOT_DIR)
set(_root_dirs "${OCULUS_ROOT_DIR}")
endif()
# todo fails for universal builds
set(_dll_suffix)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(_bitness 64)
if(WIN32)
set(_dll_suffix _x64)
endif()
else()
set(_bitness 32)
endif()
# Test platform
set(_platform)
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(_platform_base osx)
set(OCULUS_PLATFORM osx32)
else()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(_platform_base linux)
set(OCULUS_PLATFORM ${_platform_base})
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(_platform_base Windows)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(OCULUS_PLATFORM ${_platform_base}/x64)
else ()
set(OCULUS_PLATFORM ${_platform_base}/Win32)
endif()
set(_vs_version VS2012)
if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 17.0)
set(_vs_version VS2012)
endif()
if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 18.0)
set(_vs_version VS2013)
endif()
if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 19.0)
set(_vs_version VS2015)
endif()
endif()
set(_libpath Lib/${OCULUS_PLATFORM})
endif()
find_path(OCULUS_INCLUDE_DIR
NAMES
OVR_Version.h
HINTS
PATHS
${_root_dirs}
PATH_SUFFIXES
LibOVR
LibOVR/Include)
FIND_LIBRARY(OCULUS_LIBRARY
NAMES LibOVR.lib
HINTS
PATH_SUFFIXES
${_libpath}
${_libpath}/Release/${_vs_version}
PATHS
${OCULUS_ROOT_DIR}/LibOVR
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OCULUS
DEFAULT_MSG
OCULUS_INCLUDE_DIR OCULUS_LIBRARY)
if(OCULUS_FOUND)
set (OCULUS_LIBRARIES ${OCULUS_LIBRARY})
list(APPEND OCULUS_INCLUDE_DIRS ${OCULUS_INCLUDE_DIR})
endif()
mark_as_advanced(OCULUS_LIBRARY OCULUS_INCLUDE_DIR)
\ No newline at end of file
# - try to find the OpenVR SDK - currently designed for the version on GitHub.
#
# Cache Variables: (probably not for direct use in your scripts)
# OPENVR_INCLUDE_DIR
#
# Non-cache variables you might use in your CMakeLists.txt:
# OPENVR_FOUND
# OPENVR_INCLUDE_DIRS
# OPENVR_PLATFORM - something like Win32, Win64, etc.
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
# Original Author:
# 2015 Ryan A. Pavlik <ryan@sensics.com>
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(OPENVR_ROOT_DIR
"${OPENVR_ROOT_DIR}"
CACHE
PATH
"Directory to search for OpenVR SDK")
set(OPENVR_HEADERS_ROOT_DIR
"${OPENVR_HEADERS_ROOT_DIR}"
CACHE
PATH
"Directory to search for private OpenVR headers")
set(_root_dirs)
if(OPENVR_ROOT_DIR)
set(_root_dirs "${OPENVR_ROOT_DIR}" "${OPENVR_HEADERS_ROOT_DIR}" "${OPENVR_ROOT_DIR}/public")
endif()
# todo fails for universal builds
set(_dll_suffix)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(_bitness 64)
if(WIN32)
set(_dll_suffix _x64)
endif()
else()
set(_bitness 32)
endif()
# Test platform
set(_platform)
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(_platform_base osx)
# SteamVR only supports 32-bit on OS X
set(OPENVR_PLATFORM osx32)
else()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(_platform_base linux)
# TODO Massive hack!
add_definitions(-DGNUC -DPOSIX -DCOMPILER_GCC -D_LINUX -DLINUX -DPOSIX -D_POSIX)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(_platform_base win)
endif()
set(OPENVR_PLATFORM ${_platform_base}${_bitness})
set(_libpath lib/${OPENVR_PLATFORM})
endif()
find_path(OPENVR_INCLUDE_DIR
NAMES
openvr_driver.h
HINTS
"${_libdir}"
"${_libdir}/.."
"${_libdir}/../.."
PATHS
${_root_dirs}
PATH_SUFFIXES
headers
public/headers
steam
public/steam)
FIND_LIBRARY(OPENVR_LIBRARY_TEMP
NAMES openvr_api
HINTS
PATH_SUFFIXES ${_libpath}
PATHS ${OPENVR_ROOT_DIR}
)
IF (OPENVR_LIBRARY_TEMP)
# Set the final string here so the GUI reflects the final state.
SET(OPENVR_LIBRARY ${OPENVR_LIBRARY_TEMP} CACHE STRING "Where the openvr Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(OPENVR_LIBRARY_TEMP "${OPENVR_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(OPENVR_LIBRARY_TEMP)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OpenVR
DEFAULT_MSG
OPENVR_INCLUDE_DIR OPENVR_LIBRARY)
if(OPENVR_FOUND)
list(APPEND OPENVR_INCLUDE_DIRS ${OPENVR_INCLUDE_DIR})
mark_as_advanced(OPENVR_ROOT_DIR)
endif()
mark_as_advanced(OPENVR_INCLUDE_DIR)
\ No newline at end of file
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
${SDL2_PATH}
)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_LIBRARY_TEMP}/../..
)
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIR}/../..
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
......@@ -62,6 +62,7 @@ set(Module_SRCS
vtkProp3DFollower.cxx
vtkPropAssembly.cxx
vtkPropCollection.cxx
vtkPropPicker3D.cxx
vtkProp.cxx
vtkProperty2D.cxx
vtkProperty.cxx
......@@ -74,6 +75,7 @@ set(Module_SRCS
vtkRenderWindowCollection.cxx
vtkRenderWindow.cxx
vtkRenderWindowInteractor.cxx
vtkRenderWindowInteractor3D.cxx
vtkSelectVisiblePoints.cxx
vtkShaderDeviceAdapter2.cxx
vtkTextActor.cxx
......@@ -113,6 +115,7 @@ set(Module_SRCS
# Needed by vtkRenderWindowInteractor, for the default interactor.
vtkInteractorStyle.cxx
vtkInteractorStyleSwitchBase.cxx
vtkInteractorStyle3D.cxx
# vtkTDx stuff dragged in by vtkInteractorStyle
vtkTDxInteractorStyle.cxx
vtkTDxInteractorStyleCamera.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkInteractorStyle3D.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkInteractorStyle3D.h"
#include "vtkCallbackCommand.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPropPicker3D.h"
#include "vtkRenderWindowInteractor3D.h"
#include "vtkProp3D.h"
#include "vtkQuaternion.h"
#include "vtkRenderer.h"
#include "vtkMatrix3x3.h"
#include "vtkTransform.h"
#include "vtkCamera.h"
vtkStandardNewMacro(vtkInteractorStyle3D);
//----------------------------------------------------------------------------
vtkInteractorStyle3D::vtkInteractorStyle3D()
{
this->InteractionProp = NULL;
this->InteractionPicker = vtkPropPicker3D::New();
this->TempMatrix3 = vtkMatrix3x3::New();
this->TempMatrix4 = vtkMatrix4x4::New();
this->AppliedTranslation[0] = 0;
this->AppliedTranslation[1] = 0;
this->AppliedTranslation[2] = 0;
this->TempTransform = vtkTransform::New();
}
//----------------------------------------------------------------------------
vtkInteractorStyle3D::~vtkInteractorStyle3D()
{
this->InteractionPicker->Delete();
this->TempMatrix3->Delete();
this->TempMatrix4->Delete();
this->TempTransform->Delete();
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::OnMouseMove()
{
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
switch (this->State)
{
case VTKIS_ROTATE:
this->FindPokedRenderer(x, y);
this->Rotate();
this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
break;
case VTKIS_DOLLY:
this->FindPokedRenderer(x, y);
this->Dolly();
this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
break;
}
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::OnLeftButtonDown()
{
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
vtkRenderWindowInteractor3D *vriren =
vtkRenderWindowInteractor3D::SafeDownCast(this->Interactor);
double *wpos = vriren->GetWorldEventPosition(
vriren->GetPointerIndex());
this->FindPokedRenderer(x, y);
this->FindPickedActor(wpos[0], wpos[1], wpos[2]);
if (this->CurrentRenderer == NULL || this->InteractionProp == NULL)
{
return;
}
this->GrabFocus(this->EventCallbackCommand);
this->StartRotate();
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::OnLeftButtonUp()
{
this->AppliedTranslation[0] = 0;
this->AppliedTranslation[1] = 0;
this->AppliedTranslation[2] = 0;
switch (this->State)
{
// in our case roate state is used for actor pose adjustments
case VTKIS_ROTATE:
this->EndRotate();
break;
}
if ( this->Interactor )
{
this->ReleaseFocus();
}
}
//----------------------------------------------------------------------------
// We handle all adjustments here
void vtkInteractorStyle3D::Rotate()
{
if (this->CurrentRenderer == NULL || this->InteractionProp == NULL)
{
return;
}
vtkRenderWindowInteractor3D *rwi =
static_cast<vtkRenderWindowInteractor3D *>(this->Interactor);
double *wpos = rwi->GetWorldEventPosition(
rwi->GetPointerIndex());
double *lwpos = rwi->GetLastWorldEventPosition(
rwi->GetPointerIndex());
double trans[3];
for (int i = 0; i < 3; i++)
{
trans[i] = wpos[i] - lwpos[i];
}
if (this->InteractionProp->GetUserMatrix() != NULL)
{
vtkTransform *t = this->TempTransform;
t->PostMultiply();
t->SetMatrix(this->InteractionProp->GetUserMatrix());
t->Translate(trans);
this->InteractionProp->SetUserMatrix(t->GetMatrix());
}
else
{
this->InteractionProp->AddPosition(trans);
}
double *wori = rwi->GetWorldEventOrientation(
rwi->GetPointerIndex());
double *lwori = rwi->GetLastWorldEventOrientation(
rwi->GetPointerIndex());
// compute the net rotation
vtkQuaternion<double> q1;
q1.SetRotationAngleAndAxis(
vtkMath::RadiansFromDegrees(lwori[0]), lwori[1], lwori[2], lwori[3]);
vtkQuaternion<double> q2;
q2.SetRotationAngleAndAxis(
vtkMath::RadiansFromDegrees(wori[0]), wori[1], wori[2], wori[3]);
q1.Conjugate();
q2 = q2*q1;
double axis[4];
axis[0] = vtkMath::DegreesFromRadians(q2.GetRotationAngleAndAxis(axis+1));
double scale[3];
scale[0] = scale[1] = scale[2] = 1.0;
double *rotate = axis;
this->Prop3DTransform(this->InteractionProp,
wpos,
1,
&rotate,
scale);
if (this->AutoAdjustCameraClippingRange)
{
this->CurrentRenderer->ResetCameraClippingRange();
}
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::FindPickedActor(double x, double y, double z)
{
this->InteractionPicker->Pick(x, y, z, this->CurrentRenderer);
vtkProp *prop = this->InteractionPicker->GetViewProp();
if (prop != NULL)
{
this->InteractionProp = vtkProp3D::SafeDownCast(prop);
}
else
{
this->InteractionProp = NULL;
}
}
//----------------------------------------------------------------------------
void vtkInteractorStyle3D::Prop3DTransform(vtkProp3D *prop3D,
double *boxCenter,
int numRotation,
double **rotate,
double *scale)
{
vtkMatrix4x4 *oldMatrix = this->TempMatrix4;
prop3D->GetMatrix(oldMatrix);
double orig[3];
prop3D->GetOrigin(orig);
vtkTransform *newTransform = this->TempTransform;
newTransform->PostMultiply();
if (prop3D->GetUserMatrix() != NULL)