diff --git a/CMake/External/External_TBB.cmake b/CMake/External/External_TBB.cmake
index 894925a95522ef6dc8965bd770b07d8d35cfde98..820408bb6b838963711e2d7b7812e5b71759f2d2 100644
--- a/CMake/External/External_TBB.cmake
+++ b/CMake/External/External_TBB.cmake
@@ -1,76 +1,3 @@
-
-# Set TBB_SOURCE_DIR and TBB_PREFIX
-imstk_define_external_dirs( TBB )
-
-# Version used to copy files & directories, and download archive from GitHub
-set(TBB_VER "2019_20191006oss")
-
-#-----------------------------------------------------------------------------
-# Set install commands
-#-----------------------------------------------------------------------------
-if(CMAKE_PROJECT_NAME STREQUAL "iMSTK")
-  set(TBB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
-
-  set(copy_tbb_config_command
-    ${CMAKE_COMMAND} -E copy
-      ${TBB_SOURCE_DIR}/tbb${TBB_VER}/cmake/TBBConfig.cmake
-      ${TBB_INSTALL_DIR}/cmake/TBBConfig.cmake
-    )
-
-  set(copy_tbb_headers_command
-    ${CMAKE_COMMAND} -E copy_directory
-      ${TBB_SOURCE_DIR}/tbb${TBB_VER}/include/tbb
-      ${TBB_INSTALL_DIR}/include/tbb
-    )
-
-  if(WIN32)
-    set(_subdir "intel64/vc14/")
-  elseif(APPLE)
-    set(_subdir "")
-  elseif(UNIX)
-    set(_subdir "intel64/gcc4.8/")
-  endif()
-
-  set(copy_tbb_libraries_command
-    ${CMAKE_COMMAND} -E copy_directory
-      ${TBB_SOURCE_DIR}/tbb${TBB_VER}/lib/${_subdir}
-      ${TBB_INSTALL_DIR}/lib/${_subdir}
-    )
-
-  set(TBB_INSTALL_COMMAND
-    INSTALL_COMMAND
-      COMMAND ${copy_tbb_config_command}
-      COMMAND ${copy_tbb_headers_command}
-      COMMAND ${copy_tbb_libraries_command}
-    )
-
-  if(WIN32)
-    set(copy_tbb_dll_to_bin_subdir_command
-      ${CMAKE_COMMAND} -E copy_directory
-        ${TBB_SOURCE_DIR}/tbb${TBB_VER}/bin/${_subdir}
-        ${TBB_INSTALL_DIR}/bin/${_subdir}
-      )
-    # Also copy dlls directly into bin folder to support
-    # calling "gtest_discover_tests" with working directory
-    # set to <CMAKE_INSTALL_PREFIX>/bin
-    set(copy_tbb_dll_to_bin_command
-      ${CMAKE_COMMAND} -E copy_directory
-        ${TBB_SOURCE_DIR}/tbb${TBB_VER}/bin/${_subdir}
-        ${TBB_INSTALL_DIR}/bin/
-      )
-    list(APPEND TBB_INSTALL_COMMAND
-      COMMAND ${copy_tbb_dll_to_bin_subdir_command}
-      COMMAND ${copy_tbb_dll_to_bin_command}
-      )
-  endif()
-
-else()
-  set(TBB_INSTALL_COMMAND
-    INSTALL_COMMAND
-      COMMAND ${SKIP_STEP_COMMAND}
-    )
-endif()
-
 #-----------------------------------------------------------------------------
 # Add External Project
 #-----------------------------------------------------------------------------
@@ -78,22 +5,14 @@ include(imstkAddExternalProject)
 
 # Download options
 if(NOT DEFINED iMSTK_TBB_GIT_SHA)
-  set(iMSTK_TBB_GIT_SHA "2019_U9")
+  # Version 2021.1.1
+  set(iMSTK_TBB_GIT_SHA "46fb877ef1618d9de9a9ba10cee107592b7cdb2d")
 endif()
 if(NOT DEFINED iMSTK_TBB_GIT_REPOSITORY)
-  if(WIN32)
-    set(tbb_file "tbb${TBB_VER}_win.zip")
-    set(tbb_md5 "a061a7c9821a374023201e8592860730")
-  elseif(APPLE)
-    set(tbb_file "tbb${TBB_VER}_mac.tgz")
-    set(tbb_md5 "43a0d6409317ee94f047622fd489a6c8")
-  else()
-    set(tbb_file "tbb${TBB_VER}_lin.tgz")
-    set(tbb_md5 "b5025847fa47040b4d2da8d6bdba0224")
-  endif()
   set(EXTERNAL_PROJECT_DOWNLOAD_OPTIONS
-    URL https://github.com/oneapi-src/oneTBB/releases/download/${iMSTK_TBB_GIT_SHA}/${tbb_file}
-    URL_HASH MD5=${tbb_md5}
+    # 2021.1
+    URL https://github.com/oneapi-src/oneTBB/archive/${iMSTK_TBB_GIT_SHA}.zip
+    URL_HASH MD5=1a0d5676ab568c8b9de57c7ecc082505
     )
 else()
   set(EXTERNAL_PROJECT_DOWNLOAD_OPTIONS
@@ -102,14 +21,8 @@ else()
     )
 endif()
 
-imstk_add_external_project(TBB
+imstk_add_external_project( TBB
   ${EXTERNAL_PROJECT_DOWNLOAD_OPTIONS}
-  DOWNLOAD_DIR ${TBB_PREFIX}
-  SOURCE_DIR ${TBB_SOURCE_DIR}
-  UPDATE_COMMAND ${SKIP_STEP_COMMAND}
-  CONFIGURE_COMMAND ${SKIP_STEP_COMMAND}
-  BUILD_COMMAND ${SKIP_STEP_COMMAND}
-  ${TBB_INSTALL_COMMAND}
-  #RELATIVE_INCLUDE_PATH ""
-  #VERBOSE
+  CMAKE_CACHE_ARGS
+    -DTBB_TEST:BOOL=OFF
 )
diff --git a/CMake/External/External_VTK.cmake b/CMake/External/External_VTK.cmake
index 0ba8773017246adfd3163adaa212de8f1029d6db..dfe44e0bfb6c3d73016f59f58ed52bb6a6f9aad4 100644
--- a/CMake/External/External_VTK.cmake
+++ b/CMake/External/External_VTK.cmake
@@ -3,8 +3,8 @@
 #-----------------------------------------------------------------------------
 set(VTK_DEPENDENCIES "OpenVR")
 
-set(${PROJECT_NAME}_VTK_REPO_SOURCE "9.0" CACHE STRING "Select VTK Source Branch/Tag")
-set(VTK_SOURCES "9.0;master;release;nightly-master" CACHE INTERNAL "List of available VTK branch,tags to get")
+set(${PROJECT_NAME}_VTK_REPO_SOURCE "9.1" CACHE STRING "Select VTK Source Branch/Tag")
+set(VTK_SOURCES "9.1;master;release;nightly-master" CACHE INTERNAL "List of available VTK branch,tags to get")
 set_property(CACHE ${PROJECT_NAME}_VTK_REPO_SOURCE PROPERTY STRINGS ${VTK_SOURCES})
 
 set(VTK_MODULE_SETTINGS
@@ -45,8 +45,8 @@ if (${PROJECT_NAME}_USE_VTK_OSMESA)
 endif()
 set(${PROJECT_NAME}_VTK_SOURCE GIT_REPOSITORY https://gitlab.kitware.com/vtk/vtk.git)
 
-if(${PROJECT_NAME}_VTK_REPO_SOURCE EQUAL "9.0")
-  set(${PROJECT_NAME}_VTK_HASH GIT_TAG ab278e87b181e3a02082bea7361fbaa3ddafb3ad)
+if(${PROJECT_NAME}_VTK_REPO_SOURCE EQUAL "9.1")
+  set(${PROJECT_NAME}_VTK_HASH GIT_TAG 285daeedd58eb890cb90d6e907d822eea3d2d092)
 else()
   set(${PROJECT_NAME}_VTK_HASH GIT_TAG origin/${${PROJECT_NAME}_VTK_REPO_SOURCE})
 endif()
diff --git a/CMake/External/External_iMSTKData.cmake b/CMake/External/External_iMSTKData.cmake
index a7668e594c305701e1bb263fd77587282b726da0..149de78a82e7a67808b424e3b1604c6b44c782d0 100644
--- a/CMake/External/External_iMSTKData.cmake
+++ b/CMake/External/External_iMSTKData.cmake
@@ -16,7 +16,7 @@ set(copy_data_command
   )
 
 include(imstkAddExternalProject)
-set(GIT_SHA "17709bc3169b5f9686225dc2fb9765f1af7c797f")
+set(GIT_SHA "e47eee983a4476af2556b8125c7762c10b5c19d5")
 set(DATA_URL "https://gitlab.kitware.com/iMSTK/imstk-data/-/archive/${GIT_SHA}/imstk-data-${GIT_SHA}.zip")
 
 imstk_add_external_project( iMSTKData
diff --git a/Examples/OpenVRController/CMakeLists.txt b/Examples/OpenVRController/CMakeLists.txt
index b22e1eee19c3b506f452a976f4960d1e649edaa9..5190298dac6aab05718309664d690b6d7b161c03 100644
--- a/Examples/OpenVRController/CMakeLists.txt
+++ b/Examples/OpenVRController/CMakeLists.txt
@@ -21,7 +21,10 @@ project(Example-OpenVRController)
 #-----------------------------------------------------------------------------
 # Create executable
 #-----------------------------------------------------------------------------
-imstk_add_executable(${PROJECT_NAME} OpenVRControllerExample.cpp)
+imstk_add_executable(${PROJECT_NAME}
+	OpenVRControllerExample.cpp
+	CameraOpenVRControl.h
+	CameraOpenVRControl.cpp)
 
 #-----------------------------------------------------------------------------
 # Add the target to Examples folder
diff --git a/Examples/OpenVRController/CameraOpenVRControl.cpp b/Examples/OpenVRController/CameraOpenVRControl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6eb163269527b776c7a35e51e0bc6b73171c243
--- /dev/null
+++ b/Examples/OpenVRController/CameraOpenVRControl.cpp
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "CameraOpenVRControl.h"
+#include "imstkCamera.h"
+#include "imstkLogger.h"
+#include "imstkOpenVRDeviceClient.h"
+
+using namespace imstk;
+
+void
+CameraOpenVRControl::printControls()
+{
+    LOG(INFO) << "Mouse Scene Controls: Only usable in debug mode";
+    LOG(INFO) << "----------------------------------------------------------------------";
+    LOG(INFO) << " | Left Trackpad   - rotate view";
+    LOG(INFO) << " | Right Trakcpad  - translate view";
+    LOG(INFO) << "----------------------------------------------------------------------";
+}
+
+void
+CameraOpenVRControl::update(const double dt)
+{
+    // We may switch cameras on the controller
+    if (m_camera == nullptr)
+    {
+        return;
+    }
+
+    if (m_rotateDevice != nullptr)
+    {
+        const Vec2d& pos  = m_rotateDevice->getTrackpadPosition();
+        const Mat4d& view = m_camera->getView();
+        m_camera->setView(view * mat4dRotation(Rotd(-pos[0] * m_rotateSpeedScale * dt, Vec3d(0.0, 1.0, 0.0))));
+    }
+    if (m_translateDevice != nullptr)
+    {
+        const Vec2d& pos  = m_translateDevice->getTrackpadPosition();
+        const Mat4d& view = m_camera->getView();
+        m_camera->setView(view * mat4dTranslate(Vec3d(pos[0], 0.0, -pos[1]) * m_translateSpeedScale * dt));
+    }
+}
\ No newline at end of file
diff --git a/Examples/OpenVRController/CameraOpenVRControl.h b/Examples/OpenVRController/CameraOpenVRControl.h
new file mode 100644
index 0000000000000000000000000000000000000000..0bad38d1c499d733d08a41d53b8e32be02d81ec0
--- /dev/null
+++ b/Examples/OpenVRController/CameraOpenVRControl.h
@@ -0,0 +1,96 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#pragma once
+
+#include "imstkDeviceControl.h"
+#include "imstkMath.h"
+
+namespace imstk
+{
+class Camera;
+class OpenVRDeviceClient;
+class SceneManager;
+}
+
+using namespace imstk;
+
+///
+/// \class CameraOpenVRControl
+///
+/// \brief Controls pose of a camera given two trackpads from OpenVRDeviceClient's
+///
+///
+class CameraOpenVRControl : public DeviceControl
+{
+public:
+    CameraOpenVRControl() = default;
+    virtual ~CameraOpenVRControl() override = default;
+
+public:
+    ///
+    /// \brief Get/Set how fast the camera translates
+    ///
+    void setTranslateSpeedScale(const double translateSpeedScale) { m_translateSpeedScale = translateSpeedScale; }
+    double getTranslateSpeedScale() const { return m_translateSpeedScale; }
+
+    ///
+    /// \brief Get/Set how fast the camera rotates
+    ///
+    void setRotateSpeedScale(const double rotateSpeedScale) { m_rotateSpeedScale = rotateSpeedScale; }
+    double getRotateSpeedScale() const { return m_rotateSpeedScale; }
+
+    ///
+    /// \brief Get/Set the device that can translate the camera
+    ///
+    void setTranslateDevice(std::shared_ptr<OpenVRDeviceClient> translateDevice) { m_translateDevice = translateDevice; }
+    std::shared_ptr<OpenVRDeviceClient> getTranslateDevice() const { return m_translateDevice; }
+
+    ///
+    /// \brief Get/Set the device that can rotate the camera
+    ///
+    void setRotateDevice(std::shared_ptr<OpenVRDeviceClient> rotateDevice) { m_rotateDevice = rotateDevice; }
+    std::shared_ptr<OpenVRDeviceClient> getRotateDevice() const { return m_rotateDevice; }
+
+    ///
+    /// \brief Get/Set the camera to be controlled
+    ///
+    void setCamera(std::shared_ptr<Camera> camera) { m_camera = camera; }
+    std::shared_ptr<Camera> getCamera() const { return m_camera; }
+
+public:
+    void printControls() override;
+
+    ///
+    /// \brief Updates control based on current device state
+    ///
+    void update(const double dt) override;
+
+protected:
+    std::shared_ptr<OpenVRDeviceClient> m_translateDevice = nullptr;
+    std::shared_ptr<OpenVRDeviceClient> m_rotateDevice    = nullptr;
+
+    std::shared_ptr<Camera> m_camera = nullptr;
+
+    // User changeable values
+    double m_rotateSpeedScale    = 1.0;
+    double m_translateSpeedScale = 1.0;
+};
\ No newline at end of file
diff --git a/Examples/OpenVRController/OpenVRControllerExample.cpp b/Examples/OpenVRController/OpenVRControllerExample.cpp
index e0b66c2a91f3f23f5a375bc37d8490b84c1f2eb2..d864f6f06daed32aaac3ea807f4ba53788fc48f5 100644
--- a/Examples/OpenVRController/OpenVRControllerExample.cpp
+++ b/Examples/OpenVRController/OpenVRControllerExample.cpp
@@ -35,6 +35,10 @@
 #include "imstkVisualModel.h"
 #include "imstkVisualObjectImporter.h"
 #include "imstkVTKOpenVRViewer.h"
+#include "CameraOpenVRControl.h"
+#include "imstkVTKViewer.h"
+
+#include "imstkTimer.h"
 
 using namespace imstk;
 
@@ -125,80 +129,111 @@ main()
 
     {
         // Add a module to run the viewer
-        imstkNew<VTKOpenVRViewer> viewer;
+        imstkNew<VTKViewer> viewer;
+        viewer->setExecutionType(Module::ExecutionType::SEQUENTIAL);
         viewer->setActiveScene(scene);
 
         // Add a module to run the scene
-        imstkNew<SceneManager> sceneManager;
-        sceneManager->setActiveScene(scene);
+        /*imstkNew<SceneManager> sceneManager;
+        sceneManager->setActiveScene(scene);*/
 
         imstkNew<SimulationManager> driver;
         driver->addModule(viewer);
-        driver->addModule(sceneManager);
-
-        // Add a VR controller for the scalpel handle
-        imstkNew<SceneObjectController> controller1(scalpelHandle, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
-        scene->addController(controller1);
-        // Add a VR controller for the scalpel blade
-        imstkNew<SceneObjectController> controller2(scalpelBlade10, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
-        scene->addController(controller2);
-
-        // This button event is emitted from the viewer's thread, thus it is queued to the scene so that we do not
-        // run it while the scene is updating
-        bool blade10InHand = true;
-        queueConnect<ButtonEvent>(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER), &OpenVRDeviceClient::buttonStateChanged, sceneManager,
-            [&](ButtonEvent* e)
+        //driver->addModule(sceneManager);
+        //driver->setDesiredDt(0.01);
+
+        //// Add a VR controller for the scalpel handle
+        //imstkNew<SceneObjectController> controller1(scalpelHandle, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //scene->addController(controller1);
+        //// Add a VR controller for the scalpel blade
+        //imstkNew<SceneObjectController> controller2(scalpelBlade10, viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //scene->addController(controller2);
+
+        //imstkNew<CameraOpenVRControl> camControl;
+        //camControl->setRotateDevice(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER));
+        //camControl->setTranslateDevice(viewer->getVRDeviceClient(OPENVR_LEFT_CONTROLLER));
+        //camControl->setTranslateSpeedScale(1.0);
+        //camControl->setRotateSpeedScale(1.0);
+        //camControl->setCamera(scene->getActiveCamera());
+        //viewer->addControl(camControl); // Only needs to update every render
+
+        StopWatch timer;
+        timer.start();
+        int i = 0;
+        //connect<Event>(viewer, &Viewer::postUpdate, [&](Event*)
+        //    {
+        //        double ms = timer.getTimeElapsed();
+        //        //if (i++ % 100 == 0)
+        //        {
+        //            printf("%f\n", ms);
+        //        }
+        //        timer.start();
+        //    });
+        viewer->init();
+        while (true)
         {
-            // When any button pressed, swap blade
-            if (e->m_buttonState == BUTTON_PRESSED)
-            {
-                const Vec3d& posControl = viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER)->getPosition();
-                if (blade10InHand)
-                {
-                    // Swap to blade 15 only if it's close in space
-                    Vec3d min, max;
-                    scalpelBlade15->getVisualGeometry()->computeBoundingBox(min, max);
-                    const Vec3d posBlade = (min + max) * 0.5;
-                    const double dist    = (posControl - posBlade).norm();
-                    LOG(INFO) << "Dist: " << dist;
-                    if (dist < 2.0)
-                    {
-                        const Vec3d t = scalpelBlade15->getVisualGeometry()->getTranslation();
-                        const Mat3d r = scalpelBlade15->getVisualGeometry()->getRotation();
-
-                        // Set the new blade to move
-                        controller2->setControlledSceneObject(scalpelBlade15);
-                        blade10InHand = false;
-
-                        scalpelBlade10->getVisualGeometry()->setTranslation(t);
-                        scalpelBlade10->getVisualGeometry()->setRotation(r);
-                    }
-                }
-                else
-                {
-                    // Swap to blade 10 only if it's close in space
-                    Vec3d min, max;
-                    scalpelBlade10->getVisualGeometry()->computeBoundingBox(min, max);
-                    const Vec3d posBlade = (min + max) * 0.5;
-                    const double dist    = (posControl - posBlade).norm();
-                    LOG(INFO) << "Dist: " << dist;
-                    if (dist < 2.0)
-                    {
-                        const Vec3d t = scalpelBlade10->getVisualGeometry()->getTranslation();
-                        const Mat3d r = scalpelBlade10->getVisualGeometry()->getRotation();
-
-                        controller2->setControlledSceneObject(scalpelBlade10);
-                        blade10InHand = true;
-
-                        // Swap transforms of the blades
-                        scalpelBlade15->getVisualGeometry()->setTranslation(t);
-                        scalpelBlade15->getVisualGeometry()->setRotation(r);
-                    }
-                }
-            }
-        });
-
-        driver->start();
+            double ms = timer.getTimeElapsed();
+            printf("%f\n", ms);
+            timer.start();
+            viewer->update();
+        }
+
+        //// This button event is emitted from the viewer's thread, thus it is queued to the scene so that we do not
+        //// run it while the scene is updating
+        //bool blade10InHand = true;
+        //queueConnect<ButtonEvent>(viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER), &OpenVRDeviceClient::buttonStateChanged, sceneManager,
+        //    [&](ButtonEvent* e)
+        //{
+        //    // When any button pressed, swap blade
+        //    if (e->m_buttonState == BUTTON_PRESSED)
+        //    {
+        //        const Vec3d& posControl = viewer->getVRDeviceClient(OPENVR_RIGHT_CONTROLLER)->getPosition();
+        //        if (blade10InHand)
+        //        {
+        //            // Swap to blade 15 only if it's close in space
+        //            Vec3d min, max;
+        //            scalpelBlade15->getVisualGeometry()->computeBoundingBox(min, max);
+        //            const Vec3d posBlade = (min + max) * 0.5;
+        //            const double dist    = (posControl - posBlade).norm();
+        //            LOG(INFO) << "Dist: " << dist;
+        //            if (dist < 2.0)
+        //            {
+        //                const Vec3d t = scalpelBlade15->getVisualGeometry()->getTranslation();
+        //                const Mat3d r = scalpelBlade15->getVisualGeometry()->getRotation();
+
+        //                // Set the new blade to move
+        //                controller2->setControlledSceneObject(scalpelBlade15);
+        //                blade10InHand = false;
+
+        //                scalpelBlade10->getVisualGeometry()->setTranslation(t);
+        //                scalpelBlade10->getVisualGeometry()->setRotation(r);
+        //            }
+        //        }
+        //        else
+        //        {
+        //            // Swap to blade 10 only if it's close in space
+        //            Vec3d min, max;
+        //            scalpelBlade10->getVisualGeometry()->computeBoundingBox(min, max);
+        //            const Vec3d posBlade = (min + max) * 0.5;
+        //            const double dist    = (posControl - posBlade).norm();
+        //            LOG(INFO) << "Dist: " << dist;
+        //            if (dist < 2.0)
+        //            {
+        //                const Vec3d t = scalpelBlade10->getVisualGeometry()->getTranslation();
+        //                const Mat3d r = scalpelBlade10->getVisualGeometry()->getRotation();
+
+        //                controller2->setControlledSceneObject(scalpelBlade10);
+        //                blade10InHand = true;
+
+        //                // Swap transforms of the blades
+        //                scalpelBlade15->getVisualGeometry()->setTranslation(t);
+        //                scalpelBlade15->getVisualGeometry()->setRotation(r);
+        //            }
+        //        }
+        //    }
+        //});
+
+        //driver->start();
     }
 
     return 0;
diff --git a/Examples/PBD/PBDInjection/InflatableObject.cpp b/Examples/PBD/PBDInjection/InflatableObject.cpp
index 0e545b9a3adb0d5883e786725e5abd69ecc91696..b268ceeee6c77694a0a977517584a94c5b875c89 100644
--- a/Examples/PBD/PBDInjection/InflatableObject.cpp
+++ b/Examples/PBD/PBDInjection/InflatableObject.cpp
@@ -46,7 +46,6 @@ InflatableObject::InflatableObject(const std::string& name, const Vec3d& tissueS
     // Setup the Geometry
     makeTetGrid(tissueSize, tissueDim, tissueCenter);
     m_objectSurfMesh = m_objectTetMesh->extractSurfaceMesh();
-    m_objectSurfMesh->flipNormals();
     //setXZPlaneTexCoords(4.0);
     setSphereTexCoords(4.0);
 
@@ -90,8 +89,6 @@ InflatableObject::InflatableObject(const std::string& name, const Vec3d& tissueS
 
     // Setup the material
     imstkNew<RenderMaterial> material;
-    material->setBackFaceCulling(false);
-    material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
     material->setShadingModel(RenderMaterial::ShadingModel::PBR);
     auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
     material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
@@ -105,12 +102,6 @@ InflatableObject::InflatableObject(const std::string& name, const Vec3d& tissueS
     visualModel->setRenderMaterial(material);
     addVisualModel(visualModel);
 
-    // Add a visual model to render the normals of the surface
-    imstkNew<VisualModel> normalsVisualModel(m_objectSurfMesh);
-    normalsVisualModel->getRenderMaterial()->setDisplayMode(RenderMaterial::DisplayMode::Surface);
-    normalsVisualModel->getRenderMaterial()->setPointSize(0.5);
-    addVisualModel(normalsVisualModel);
-
     // Setup the Object
     setPhysicsGeometry(m_objectTetMesh);
     setCollidingGeometry(m_objectSurfMesh);
diff --git a/Examples/PBD/PBDTissueContact/PBDTissueContactExample.cpp b/Examples/PBD/PBDTissueContact/PBDTissueContactExample.cpp
index 6c1de0f48bd7f6bf1ff35dc382fcb6e9c2e78588..e10355279a7d5101587afbbe59a08a56e64ac46b 100644
--- a/Examples/PBD/PBDTissueContact/PBDTissueContactExample.cpp
+++ b/Examples/PBD/PBDTissueContact/PBDTissueContactExample.cpp
@@ -249,8 +249,6 @@ makeTissueObj(const std::string& name,
 
     // Setup the material
     imstkNew<RenderMaterial> material;
-    material->setBackFaceCulling(false);
-    //material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
     material->setShadingModel(RenderMaterial::ShadingModel::PBR);
     auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
     material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
diff --git a/Examples/PBD/PBDTissueSurfaceNeedleContact/PBDTissueSurfaceNeedleContactExample.cpp b/Examples/PBD/PBDTissueSurfaceNeedleContact/PBDTissueSurfaceNeedleContactExample.cpp
index 980767ac55f23e648257820301558b8de90d3f94..46d2b496a2f4c177c6da3d7a754e20a19970d727 100644
--- a/Examples/PBD/PBDTissueSurfaceNeedleContact/PBDTissueSurfaceNeedleContactExample.cpp
+++ b/Examples/PBD/PBDTissueSurfaceNeedleContact/PBDTissueSurfaceNeedleContactExample.cpp
@@ -229,7 +229,6 @@ makeTissueObj(const std::string& name,
 
     // Setup the material
     imstkNew<RenderMaterial> material;
-    material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
     material->setShadingModel(RenderMaterial::ShadingModel::PBR);
     auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
     material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
@@ -340,7 +339,7 @@ main()
 
     // Light
     imstkNew<DirectionalLight> light;
-    light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
+    light->setDirection(Vec3d(5.0, -8.0, -5.0));
     light->setIntensity(1.0);
     scene->addLight("Light", light);
 
diff --git a/Examples/PBD/PBDTissueVolumeNeedleContact/PBDTissueVolumeNeedleContactExample.cpp b/Examples/PBD/PBDTissueVolumeNeedleContact/PBDTissueVolumeNeedleContactExample.cpp
index b34412a1eced9ee70191b31cfd9e06923768ed58..f14c32638b3cfd17210e0e7e5c74b5b425f794cf 100644
--- a/Examples/PBD/PBDTissueVolumeNeedleContact/PBDTissueVolumeNeedleContactExample.cpp
+++ b/Examples/PBD/PBDTissueVolumeNeedleContact/PBDTissueVolumeNeedleContactExample.cpp
@@ -245,6 +245,7 @@ makeTissueObj(const std::string& name,
     // Setup the material
     imstkNew<RenderMaterial> material;
     material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
+    material->setBackFaceCulling(false);
     /* material->setShadingModel(RenderMaterial::ShadingModel::PBR);
      auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
      material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
diff --git a/Examples/RenderingColon/CMakeLists.txt b/Examples/RenderingColon/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8cd387cd0f8f2c916f163d3ff306317dec88511e
--- /dev/null
+++ b/Examples/RenderingColon/CMakeLists.txt
@@ -0,0 +1,34 @@
+###########################################################################
+#
+# Copyright (c) Kitware, Inc.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0.txt
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+###########################################################################
+
+project(Example-RenderingColon)
+
+#-----------------------------------------------------------------------------
+# Create executable
+#-----------------------------------------------------------------------------
+imstk_add_executable(${PROJECT_NAME} RenderingColonExample.cpp)
+
+#-----------------------------------------------------------------------------
+# Add the target to Examples folder
+#-----------------------------------------------------------------------------
+SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES FOLDER Examples)
+
+#-----------------------------------------------------------------------------
+# Link libraries to executable
+#-----------------------------------------------------------------------------
+target_link_libraries(${PROJECT_NAME} SimulationManager)
diff --git a/Examples/RenderingColon/RenderingColonExample.cpp b/Examples/RenderingColon/RenderingColonExample.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ab6820628010f62d63e97b2b91a7486fe32ad00
--- /dev/null
+++ b/Examples/RenderingColon/RenderingColonExample.cpp
@@ -0,0 +1,298 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "imstkCamera.h"
+#include "imstkImageData.h"
+#include "imstkKeyboardSceneControl.h"
+#include "imstkLineMesh.h"
+#include "imstkMeshIO.h"
+#include "imstkMouseSceneControl.h"
+#include "imstkNew.h"
+#include "imstkRenderMaterial.h"
+#include "imstkScene.h"
+#include "imstkSceneManager.h"
+#include "imstkSceneObject.h"
+#include "imstkSimulationManager.h"
+#include "imstkSpotLight.h"
+#include "imstkSurfaceMesh.h"
+#include "imstkVecDataArray.h"
+#include "imstkVisualModel.h"
+#include "imstkVTKRenderer.h"
+#include "imstkVTKViewer.h"
+
+#include <vtkImageData.h>
+#include <vtkJPEGReader.h>
+#include <vtkOpenGLRenderer.h>
+#include <vtkSkybox.h>
+#include <vtkTexture.h>
+
+using namespace imstk;
+
+///
+/// \brief Returns interpolated point between p1 and p2, given
+/// a set of 4 vertices on the spline
+///
+static Vec3d
+catmullRom(
+    const Vec3d& p0, const Vec3d& p1,
+    const Vec3d& p2, const Vec3d& p3,
+    const double t)
+{
+    const double t2 = t * t;
+    const double t3 = t2 * t;
+    return p1 + 0.5 * ((-p0 + p2) * t
+                       + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
+                       + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
+}
+
+///
+/// \brief Gets position on the line mesh interpreted as a piecewise spline.
+/// The spline is split up per 4 verts
+///
+static Vec3d
+getSplinePositionFromLineMesh(double dist, std::shared_ptr<LineMesh> lineMesh)
+{
+    std::shared_ptr<VecDataArray<double, 3>> verticesPtr = lineMesh->getVertexPositions();
+    const VecDataArray<double, 3>&           vertices    = *verticesPtr;
+    CHECK(vertices.size() >= 2) << "Must have at least 2 vertices";
+    auto                     vertexDistPtr = std::dynamic_pointer_cast<DataArray<double>>(lineMesh->getVertexAttribute("distances"));
+    const DataArray<double>& vertexDist    = *vertexDistPtr;
+    const double             startLength   = vertexDist[0];
+    const double             endLength     = vertexDist[vertexDist.size() - 1];
+
+    // Extrapolates
+    if (dist <= startLength)
+    {
+        const Vec3d m = (vertices[1] - vertices[0]).normalized();
+        return vertices[0] + m * -dist;
+    }
+    if (dist >= endLength)
+    {
+        const Vec3d m = (vertices[vertices.size() - 1] - vertices[vertices.size() - 2]).normalized();
+        return vertices[vertices.size() - 1] + m * dist;
+    }
+
+    // Find the corresponding segment we are in
+    int j = 0;
+    for (int i = 0; i < vertices.size() - 1; i++)
+    {
+        if (dist > vertexDist[i] && dist <= vertexDist[i + 1])
+        {
+            j = i;
+            break;
+        }
+    }
+
+    // If the user wants a position at either end make sure to clamp
+    const int i0 = std::max(std::min(j - 1, vertices.size() - 1), 0);
+    const int i1 = std::max(std::min(j, vertices.size() - 1), 0);
+    const int i2 = std::max(std::min(j + 1, vertices.size() - 1), 0);
+    const int i3 = std::max(std::min(j + 2, vertices.size() - 1), 0);
+
+    // Get the 4 points on the line, with sample
+    // point fractionally between b & c
+    const Vec3d a = vertices[i0];
+    const Vec3d b = vertices[i1];
+    const Vec3d c = vertices[i2];
+    const Vec3d d = vertices[i3];
+
+    // Compute the fractional distance between b & c
+    const double distb = vertexDist[j];
+    const double distc = vertexDist[j + 1];
+    const double frac  = (dist - distb) / (distc - distb);
+
+    const Vec3d results = catmullRom(a, b, c, d, frac);
+    return results;
+}
+
+///
+/// \brief Demonstrates PBR rendering with clearcoat model
+/// for an organ
+///
+int
+main()
+{
+    // Write log to stdout and file
+    Logger::startLogger();
+
+    imstkNew<Scene> scene("RenderingColon");
+    auto            colonObject = std::make_shared<SceneObject>("colon");
+    {
+        imstkNew<RenderMaterial> colonMaterial;
+        colonMaterial->setDisplayMode(RenderMaterial::DisplayMode::Surface);
+        colonMaterial->setShadingModel(RenderMaterial::ShadingModel::PBR);
+        auto diffuseTexImg =
+            MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/Organs/Colon/colon_BaseColor.png");
+        colonMaterial->addTexture(std::make_shared<Texture>(diffuseTexImg, Texture::Type::Diffuse));
+        auto normalTexImg =
+            MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/Organs/Colon/colon_Normal.png");
+        colonMaterial->addTexture(std::make_shared<Texture>(normalTexImg, Texture::Type::Normal));
+        colonMaterial->setRecomputeVertexNormals(true);
+        colonMaterial->setBackFaceCulling(true);
+        colonMaterial->setMetalness(0.0);
+        colonMaterial->setRoughness(0.26);
+        colonMaterial->setNormalStrength(5.0);
+        colonMaterial->setOcclusionStrength(0.0);
+
+        colonMaterial->addTexture(std::make_shared<Texture>(normalTexImg, Texture::Type::CoatNormal));
+        colonMaterial->setCoatRoughness(0.1);
+        colonMaterial->setCoatStrength(1.0);
+        colonMaterial->setCoatColor(Color::White);
+        colonMaterial->setCoatIOR(3.0);
+        colonMaterial->setBaseIOR(3.0);
+        colonMaterial->setCoatNormalScale(0.5);
+        colonMaterial->setEdgeTint(Color::White);
+
+        auto surfMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Organs/Colon/colon.obj");
+
+        imstkNew<VisualModel> visualModel(surfMesh);
+        visualModel->setRenderMaterial(colonMaterial);
+        colonObject->addVisualModel(visualModel);
+    }
+    scene->addSceneObject(colonObject);
+
+    auto   colonMedialMesh = MeshIO::read<LineMesh>(iMSTK_DATA_ROOT "/Organs/Colon/colonMedialMesh.obj");
+    double totalLength     = 0.0;
+    {
+        // Compute lengths to each vertex along the line
+        std::shared_ptr<VecDataArray<double, 3>> verticesPtr   = colonMedialMesh->getVertexPositions();
+        const VecDataArray<double, 3>&           vertices      = *verticesPtr;
+        auto                                     vertexDistPtr = std::make_shared<DataArray<double>>(verticesPtr->size());
+        DataArray<double>&                       vertexDist    = *vertexDistPtr;
+        colonMedialMesh->setVertexScalars("distances", vertexDistPtr);
+        vertexDist[0] = 0.0;
+        for (int i = 1; i < vertices.size(); i++)
+        {
+            const double length = (vertices[i] - vertices[i - 1]).norm();
+            vertexDist[i] = vertexDist[i - 1] + length;
+        }
+        totalLength = vertexDist[vertexDist.size() - 1];
+    }
+
+    // Lights
+    // Here we use a falloff on the light (via quadratic function)
+    imstkNew<SpotLight> light;
+    light->setSpotAngle(40.0);
+    light->setAttenuationValues(3000.0, 1.0, 0.01);
+    light->setIntensity(10.0);
+    scene->addLight("light", light);
+
+    // Run the simulation
+    {
+        // Setup a viewer to render in its own thread
+        imstkNew<VTKViewer> viewer;
+        viewer->setActiveScene(scene);
+        viewer->setBackgroundColors(Color::Black);
+        // Enable SSAO
+        Vec3d l, u;
+        scene->computeBoundingBox(l, u);
+        const double sceneSize = (u - l).norm();
+
+        auto renderConfig = std::make_shared<RendererConfig>();
+        renderConfig->m_ssaoConfig.m_enableSSAO = true;
+        renderConfig->m_ssaoConfig.m_SSAOBlur   = true;
+        renderConfig->m_ssaoConfig.m_SSAORadius = 50.0 * sceneSize;
+        renderConfig->m_ssaoConfig.m_SSAOBias   = 0.03 * sceneSize;
+        renderConfig->m_ssaoConfig.m_KernelSize = 128;
+        viewer->getActiveRenderer()->setConfig(renderConfig);
+
+        // Setup a scene manager to advance the scene in its own thread
+        imstkNew<SceneManager> sceneManager;
+        sceneManager->setActiveScene(scene);
+        sceneManager->pause(); // Start simulation paused
+
+        imstkNew<SimulationManager> driver;
+        driver->addModule(viewer);
+        driver->addModule(sceneManager);
+
+        // Add mouse and keyboard controls to the viewer
+        {
+            imstkNew<MouseSceneControl> mouseControl(viewer->getMouseDevice());
+            mouseControl->setSceneManager(sceneManager);
+            viewer->addControl(mouseControl);
+
+            imstkNew<KeyboardSceneControl> keyControl(viewer->getKeyboardDevice());
+            keyControl->setSceneManager(sceneManager);
+            keyControl->setModuleDriver(driver);
+            viewer->addControl(keyControl);
+        }
+
+        double                  t   = 0.0;
+        std::shared_ptr<Camera> cam = scene->getActiveCamera();
+        {
+            // Initialize the camera
+            const Vec3d eyePos  = getSplinePositionFromLineMesh(0.0, colonMedialMesh);
+            const Vec3d focalPt = getSplinePositionFromLineMesh(0.07, colonMedialMesh);
+            cam->setPosition(eyePos);
+            cam->setFocalPoint(focalPt);
+            light->setPosition(eyePos);
+            light->setFocalPoint(focalPt);
+        }
+
+        // Advance the camera along the line
+        connect<Event>(sceneManager, &SceneManager::postUpdate,
+            [&](Event*)
+        {
+            t += sceneManager->getDt();
+
+            const double velocity = 0.1;
+            const double dist     = std::min(t * velocity, totalLength);
+            const Vec3d eyePos    = getSplinePositionFromLineMesh(dist, colonMedialMesh);
+            const Vec3d focalPt   = getSplinePositionFromLineMesh(dist + 0.07, colonMedialMesh);
+
+            cam->setPosition(eyePos);
+            cam->setFocalPoint(focalPt);
+            light->setPosition(eyePos);
+            light->setFocalPoint(focalPt);
+            });
+
+        connect<Event>(driver, &SimulationManager::starting, [&](Event*)
+        {
+            vtkSmartPointer<vtkRenderer> ren = std::dynamic_pointer_cast<VTKRenderer>(viewer->getActiveRenderer())->getVtkRenderer();
+            vtkSmartPointer<vtkOpenGLRenderer> oRen = vtkOpenGLRenderer::SafeDownCast(ren);
+            vtkNew<vtkJPEGReader> reader;
+            reader->SetFileName(iMSTK_DATA_ROOT "/Organs/Colon/colon_irradiance_environment_map.jpg");
+            reader->Update();
+
+            vtkNew<vtkTexture> texture;
+            // Enable mipmapping to handle HDR image
+            texture->MipmapOn();
+            texture->InterpolateOn();
+            texture->SetInputData(reader->GetOutput());
+            texture->SetColorModeToDirectScalars();
+            texture->SetCubeMap(false);
+            texture->Update();
+
+            /* vtkNew<vtkSkybox> skybox;
+             skybox->SetTexture(texture);
+             ren->AddActor(skybox);*/
+
+            ren->AutomaticLightCreationOff();
+            oRen->UseSphericalHarmonicsOff();
+            ren->UseImageBasedLightingOn();
+            ren->SetEnvironmentTexture(texture);
+            });
+
+        driver->start();
+    }
+
+    return 0;
+}
diff --git a/Examples/Rendering/CMakeLists.txt b/Examples/RenderingHead/CMakeLists.txt
similarity index 93%
rename from Examples/Rendering/CMakeLists.txt
rename to Examples/RenderingHead/CMakeLists.txt
index b0b6a0069756c95bf5a2bf5b02f8d6c94f841768..a4ed72e6c78cf68ffba9bd5a39887ce2eb057fd9 100644
--- a/Examples/Rendering/CMakeLists.txt
+++ b/Examples/RenderingHead/CMakeLists.txt
@@ -16,12 +16,12 @@
 #
 ###########################################################################
 
-project(Example-Rendering)
+project(Example-RenderingHead)
 
 #-----------------------------------------------------------------------------
 # Create executable
 #-----------------------------------------------------------------------------
-imstk_add_executable(${PROJECT_NAME} RenderingExample.cpp)
+imstk_add_executable(${PROJECT_NAME} RenderingHeadExample.cpp)
 
 #-----------------------------------------------------------------------------
 # Add the target to Examples folder
diff --git a/Examples/Rendering/RenderingExample.cpp b/Examples/RenderingHead/RenderingHeadExample.cpp
similarity index 89%
rename from Examples/Rendering/RenderingExample.cpp
rename to Examples/RenderingHead/RenderingHeadExample.cpp
index 6b772de7728aaccd8cf4e8aecf9f4b73a8a7e529..7566553aa64064ec2f9c37633854c4ab7a226603 100644
--- a/Examples/Rendering/RenderingExample.cpp
+++ b/Examples/RenderingHead/RenderingHeadExample.cpp
@@ -20,7 +20,6 @@
 =========================================================================*/
 
 #include "imstkCamera.h"
-#include "imstkIBLProbe.h"
 #include "imstkKeyboardSceneControl.h"
 #include "imstkDirectionalLight.h"
 #include "imstkMeshIO.h"
@@ -39,8 +38,7 @@
 using namespace imstk;
 
 ///
-/// \brief This example demonstrates configuring the renderer
-/// objects, lights etc.
+/// \brief Demonstrates PBR rendering with SSAO
 ///
 int
 main()
@@ -48,16 +46,8 @@ main()
     // Write log to stdout and file
     Logger::startLogger();
 
-    double          sceneSize;
-    imstkNew<Scene> scene("Rendering");
+    imstkNew<Scene> scene("RenderingHead");
     {
-        // Add IBL Probe
-        imstkNew<IBLProbe> globalIBLProbe(
-            iMSTK_DATA_ROOT "/IBL/roomIrradiance.dds",
-            iMSTK_DATA_ROOT "/IBL/roomRadiance.dds",
-            iMSTK_DATA_ROOT "/IBL/roomBRDF.png");
-        scene->setGlobalIBLProbe(globalIBLProbe);
-
         // Head mesh
         auto surfaceMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/head/head_revised.obj");
 
@@ -119,7 +109,7 @@ main()
         // Enable SSAO
         Vec3d l, u;
         scene->computeBoundingBox(l, u);
-        sceneSize = (u - l).norm();
+        const double sceneSize = (u - l).norm();
 
         auto rendConfig = std::make_shared<RendererConfig>();
         rendConfig->m_ssaoConfig.m_enableSSAO = true;
diff --git a/Examples/VolumeRendering/CMakeLists.txt b/Examples/RenderingVolume/CMakeLists.txt
similarity index 93%
rename from Examples/VolumeRendering/CMakeLists.txt
rename to Examples/RenderingVolume/CMakeLists.txt
index ba361d23204cb78a18f840a9b3ee560356a97aa2..58885798f42f8663c65e2d367fb289b82f41e105 100644
--- a/Examples/VolumeRendering/CMakeLists.txt
+++ b/Examples/RenderingVolume/CMakeLists.txt
@@ -16,12 +16,12 @@
 #
 ###########################################################################
 
-project(Example-VolumeRendering)
+project(Example-RenderingVolume)
 
 #-----------------------------------------------------------------------------
 # Create executable
 #-----------------------------------------------------------------------------
-imstk_add_executable(${PROJECT_NAME} VolumeRenderingExample.cpp)
+imstk_add_executable(${PROJECT_NAME} RenderingVolumeExample.cpp)
 
 #-----------------------------------------------------------------------------
 # Add the target to Examples folder
diff --git a/Examples/VolumeRendering/VolumeRenderingExample.cpp b/Examples/RenderingVolume/RenderingVolumeExample.cpp
similarity index 100%
rename from Examples/VolumeRendering/VolumeRenderingExample.cpp
rename to Examples/RenderingVolume/RenderingVolumeExample.cpp
diff --git a/Source/Common/Parallel/imstkParallelFor.h b/Source/Common/Parallel/imstkParallelFor.h
index 77b26c93e89a844f9e0c90d21f62234830ab2015..89cf784782b3944e333d08dfad91f4366fdf636c 100644
--- a/Source/Common/Parallel/imstkParallelFor.h
+++ b/Source/Common/Parallel/imstkParallelFor.h
@@ -21,7 +21,12 @@
 
 #pragma once
 
+#include "imstkMacros.h"
+
+DISABLE_WARNING_PUSH
+    DISABLE_WARNING_PADDING
 #include <tbb/tbb.h>
+DISABLE_WARNING_POP
 
 namespace imstk
 {
diff --git a/Source/Common/Parallel/imstkParallelReduce.h b/Source/Common/Parallel/imstkParallelReduce.h
index fe661212a26b6e7689d37346acaaee0ff477fffb..e4e40dfa5dd05f34c2d4e818721d36ac7b9a9f8f 100644
--- a/Source/Common/Parallel/imstkParallelReduce.h
+++ b/Source/Common/Parallel/imstkParallelReduce.h
@@ -24,7 +24,10 @@
 #include "imstkMath.h"
 #include "imstkVecDataArray.h"
 
+DISABLE_WARNING_PUSH
+    DISABLE_WARNING_PADDING
 #include <tbb/tbb.h>
+DISABLE_WARNING_POP
 
 #undef min
 #undef max
diff --git a/Source/Common/Parallel/imstkThreadManager.cpp b/Source/Common/Parallel/imstkThreadManager.cpp
index 1aba79875405219af47be5feae8471f1a61c6681..d7f674f9bad71211b9c839ebf045043aad9c2915 100644
--- a/Source/Common/Parallel/imstkThreadManager.cpp
+++ b/Source/Common/Parallel/imstkThreadManager.cpp
@@ -47,7 +47,7 @@ ThreadManager::setThreadPoolSize(const size_t nThreads)
 void
 ThreadManager::setOptimalParallelism()
 {
-    setThreadPoolSize(static_cast<size_t>(tbb::task_scheduler_init::default_num_threads()));
+    setThreadPoolSize(static_cast<size_t>(tbb::info::default_concurrency()));
 }
 
 size_t
diff --git a/Source/Common/Parallel/imstkThreadManager.h b/Source/Common/Parallel/imstkThreadManager.h
index 263bef9a3f8768ed6925ddf373b9a80aded61de1..96774264c8811850ff07c10374226e8dec2dc11e 100644
--- a/Source/Common/Parallel/imstkThreadManager.h
+++ b/Source/Common/Parallel/imstkThreadManager.h
@@ -21,11 +21,13 @@
 
 #pragma once
 
-/// \todo Remove this in TBB 2019 Update 4: https://github.com/intel/tbb/blob/tbb_2019/CHANGES#L117
-#define TBB_PREVIEW_GLOBAL_CONTROL 1
+#include "imstkMacros.h"
 
+DISABLE_WARNING_PUSH
+    DISABLE_WARNING_PADDING
 #include <tbb/tbb.h>
 #include <tbb/global_control.h>
+DISABLE_WARNING_POP
 
 namespace imstk
 {
@@ -34,7 +36,6 @@ namespace ParallelUtils
 class ThreadManager
 {
 public:
-
     ///
     /// \brief Set system-wide thread pool size for parallel computation
     ///
diff --git a/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp b/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp
index 63fb4f6c0e2d391f2cc210ffd69686f6291c4533..821e592e91e0602f558d03f06d536e88d6a03992 100644
--- a/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp
+++ b/Source/Common/TaskGraph/imstkTbbTaskGraphController.cpp
@@ -20,86 +20,59 @@
 =========================================================================*/
 
 #include "imstkTbbTaskGraphController.h"
+#include "imstkMacros.h"
 #include "imstkTaskGraph.h"
 
-#include <tbb/tbb.h>
+DISABLE_WARNING_PUSH
+    DISABLE_WARNING_PADDING
+#include <tbb/flow_graph.h>
+DISABLE_WARNING_POP
+
+using namespace tbb::flow;
 
 namespace imstk
 {
-class NodeTbbTask : public tbb::task
+void
+TbbTaskGraphController::execute()
 {
-public:
-    NodeTbbTask(std::shared_ptr<TaskNode> node) : m_node(node) { }
-
-public:
-    task* execute() override
-    {
-        __TBB_ASSERT(ref_count() == 0, NULL);
+    using TbbContinueNode = continue_node<continue_msg>;
 
-        m_node->execute();
+    graph g;
 
-        for (size_t i = 0; i < successors.size(); i++)
-        {
-            if (successors[i]->decrement_ref_count() == 0)
-            {
-                spawn(*successors[i]);
-            }
-        }
-        return NULL;
-    }
+    broadcast_node<continue_msg> start(g);
 
-public:
-    std::shared_ptr<TaskNode> m_node = nullptr;
-    std::vector<NodeTbbTask*> successors;
-};
+    // Create a continue node for every TaskNode (except start)
+    std::unordered_map<std::shared_ptr<TaskNode>, TbbContinueNode> tbbNodes;
+    using NodeKeyValuePair = std::pair<std::shared_ptr<TaskNode>, TbbContinueNode>;
 
-void
-TbbTaskGraphController::execute()
-{
-    // Create a Task for every node
     const TaskNodeVector& nodes = m_graph->getNodes();
     if (nodes.size() == 0)
     {
         return;
     }
-
-    // Create a task for every node
-    std::unordered_map<std::shared_ptr<TaskNode>, NodeTbbTask*> tasks;
-    tasks.reserve(nodes.size());
-
+    tbbNodes.reserve(nodes.size());
     for (size_t i = 0; i < nodes.size(); i++)
     {
-        std::shared_ptr<TaskNode> node = nodes[i];
-        tasks[node] = new (tbb::task::allocate_root())NodeTbbTask(node);
+        if (m_graph->getSource() != nodes[i])
+        {
+            std::shared_ptr<TaskNode> node = nodes[i];
+            tbbNodes.insert(NodeKeyValuePair(node, TbbContinueNode(g,
+                [node](continue_msg) { node->execute(); })));
+        }
     }
-    // Increment successor reference counts
+
     const TaskNodeAdjList& adjList = m_graph->getAdjList();
-    // For every node in graph
-    for (size_t i = 0; i < nodes.size(); i++)
+    for (const auto& i : adjList)
     {
-        // If it contains outputs
-        if (adjList.count(nodes[i]) != 0)
+        TbbContinueNode& tbbNode1 = tbbNodes.at(i.first);
+        for (const auto& outputNode : i.second)
         {
-            // For every output
-            const TaskNodeSet& outputNodes = adjList.at(nodes[i]);
-            for (TaskNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++)
-            {
-                // Lookup the task of the node
-                NodeTbbTask* successor = tasks[*it];
-                tasks[nodes[i]]->successors.push_back(successor);
-                successor->increment_ref_count();
-            }
+            TbbContinueNode& tbbNode2 = tbbNodes.at(outputNode);
+            make_edge(tbbNode1, tbbNode2);
         }
     }
 
-    NodeTbbTask* startTask = tasks[m_graph->getSource()];
-    NodeTbbTask* finalTask = tasks[m_graph->getSink()];
-
-    // Extra ref count on the final task
-    finalTask->increment_ref_count();
-    finalTask->spawn_and_wait_for_all(*startTask);
-
-    finalTask->execute(); // Execute final task explicitly
-    tbb::task::destroy(*finalTask);
+    start.try_put(continue_msg());
+    g.wait_for_all();
 }
 }
\ No newline at end of file
diff --git a/Source/Common/imstkModule.cpp b/Source/Common/imstkModule.cpp
index 62242c16a50a3aa188a6bceeb9eb6dc3c7f5a14a..1f3fadbcc5b05032658247da8cc75cd0fb234853 100644
--- a/Source/Common/imstkModule.cpp
+++ b/Source/Common/imstkModule.cpp
@@ -22,6 +22,8 @@
 #include "imstkModule.h"
 #include "imstkLogger.h"
 
+#include <thread>
+
 namespace imstk
 {
 void
diff --git a/Source/Common/imstkModule.h b/Source/Common/imstkModule.h
index db676fe44d75aa32f10583566d5df8f1cfd278eb..64dbdcfd1e7fb6f1c43d492159ee85b767a89541 100644
--- a/Source/Common/imstkModule.h
+++ b/Source/Common/imstkModule.h
@@ -24,9 +24,6 @@
 #include "imstkTimer.h"
 #include "imstkEventObject.h"
 
-#include <tbb/atomic.h>
-#include <thread>
-
 namespace imstk
 {
 ///
@@ -117,8 +114,8 @@ public:
     virtual void uninitModule() { }
 
 protected:
-    tbb::atomic<bool> m_init   = false;
-    tbb::atomic<bool> m_paused = false;
+    std::atomic<bool> m_init   = ATOMIC_VAR_INIT(false);
+    std::atomic<bool> m_paused = ATOMIC_VAR_INIT(false);
     double m_dt = 0.0;
     ExecutionType m_executionType = ExecutionType::PARALLEL; // Defaults to parallel, subclass and set
     bool   m_muteUpdateEvents     = false;                   // Avoid posting pre/post update, useful when running modules at extremely fast rates
diff --git a/Source/Common/imstkModuleDriver.h b/Source/Common/imstkModuleDriver.h
index 4725bcff5aad88603abec755b034f30c8309c9c5..d4fca772d8cffb8cece047ec08edbf178dc9e3a6 100644
--- a/Source/Common/imstkModuleDriver.h
+++ b/Source/Common/imstkModuleDriver.h
@@ -23,10 +23,6 @@
 
 #include "imstkEventObject.h"
 
-#include <memory>
-#include <tbb/atomic.h>
-#include <vector>
-
 namespace imstk
 {
 using ModuleDriverStatus = int;
@@ -72,6 +68,6 @@ public:
 protected:
     std::vector<std::shared_ptr<Module>> m_modules;
 
-    tbb::atomic<ModuleDriverStatus> simState = { ModuleDriverRunning };
+    std::atomic<ModuleDriverStatus> simState = { ModuleDriverRunning };
 };
 };
\ No newline at end of file
diff --git a/Source/Devices/imstkOpenVRDeviceClient.h b/Source/Devices/imstkOpenVRDeviceClient.h
index e07eb67febeaeb3f28ca4cf5c9a29ba5505f235d..a0d4051152ff2cd3b51b4da92d14c22773a8ad48 100644
--- a/Source/Devices/imstkOpenVRDeviceClient.h
+++ b/Source/Devices/imstkOpenVRDeviceClient.h
@@ -42,7 +42,7 @@ protected:
     /// This object is only creatable through its New method
     ///
     OpenVRDeviceClient(DeviceType deviceType) : DeviceClient("OpenVRDevice", ""),
-        m_deviceType(deviceType) { }
+        m_deviceType(deviceType), m_trackpadPosition(Vec2d::Zero()) { }
 
     ///
     /// This object is only creatable through this method
@@ -58,6 +58,9 @@ public:
 public:
     DeviceType getDeviceType() const { return m_deviceType; }
 
+    const Vec2d& getTrackpadPosition() { return m_trackpadPosition; }
+    void setTrackpadPosition(const Vec2d& pos) { m_trackpadPosition = pos; }
+
     ///
     /// \brief Set the current position and orientation
     ///
@@ -79,5 +82,6 @@ protected:
 
 private:
     DeviceType m_deviceType;
+    Vec2d      m_trackpadPosition;
 };
 }
\ No newline at end of file
diff --git a/Source/Geometry/Mesh/imstkSurfaceMesh.cpp b/Source/Geometry/Mesh/imstkSurfaceMesh.cpp
index 7bf64e8253662fcc76a0946e6f3e009c6c71dca8..4c2d75aa9ab091ee5f223e023a61fd8c4b038b11 100644
--- a/Source/Geometry/Mesh/imstkSurfaceMesh.cpp
+++ b/Source/Geometry/Mesh/imstkSurfaceMesh.cpp
@@ -361,6 +361,10 @@ SurfaceMesh::computeVertexTangents()
 
         setVertexTangents("tangents", vertexTangentsPtr);
     }
+    else
+    {
+        LOG(FATAL) << "Tried to compute per vertex tangents for mesh with no UVs";
+    }
 }
 
 void
diff --git a/Source/Rendering/Materials/imstkRenderMaterial.cpp b/Source/Rendering/Materials/imstkRenderMaterial.cpp
index 01d4ffa9d370b607948d45656b093afda7601601..4131c75c83798916a4037b2c794fd62152ebc1a6 100644
--- a/Source/Rendering/Materials/imstkRenderMaterial.cpp
+++ b/Source/Rendering/Materials/imstkRenderMaterial.cpp
@@ -43,16 +43,6 @@ RenderMaterial::setDisplayMode(const DisplayMode displayMode)
     }
 }
 
-void
-RenderMaterial::setTessellated(const bool tessellated)
-{
-    if (tessellated != m_tessellated)
-    {
-        m_tessellated = tessellated;
-        postModified();
-    }
-}
-
 void
 RenderMaterial::setLineWidth(const double width)
 {
@@ -161,6 +151,96 @@ RenderMaterial::setEmissivity(const double emissivity)
     }
 }
 
+void
+RenderMaterial::setAnisotropy(const double anisotropy)
+{
+    if (m_anisotropy != anisotropy)
+    {
+        m_anisotropy = anisotropy;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setAnisotropyRotation(const double anisotropyRotation)
+{
+    if (m_anisotropyRotation != anisotropyRotation)
+    {
+        m_anisotropyRotation = anisotropyRotation;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setBaseIOR(const double baseIOR)
+{
+    if (m_baseIOR != baseIOR)
+    {
+        m_baseIOR = baseIOR;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setCoatColor(const Color& coatColor)
+{
+    if (m_coatColor != coatColor)
+    {
+        m_coatColor = coatColor;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setCoatIOR(const double coatIOR)
+{
+    if (m_coatIOR != coatIOR)
+    {
+        m_coatIOR = coatIOR;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setCoatNormalScale(const double coatNormalScale)
+{
+    if (m_coatNormalScale != coatNormalScale)
+    {
+        m_coatNormalScale = coatNormalScale;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setCoatRoughness(const double coatRoughness)
+{
+    if (m_coatRoughness != coatRoughness)
+    {
+        m_coatRoughness = coatRoughness;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setCoatStrength(const double coatStrength)
+{
+    if (m_coatStrength != coatStrength)
+    {
+        m_coatStrength = coatStrength;
+        postModified();
+    }
+}
+
+void
+RenderMaterial::setEdgeTint(const Color& edgeTint)
+{
+    if (m_edgeTint != edgeTint)
+    {
+        m_edgeTint = edgeTint;
+        postModified();
+    }
+}
+
 std::shared_ptr<Texture>
 RenderMaterial::getTexture(Texture::Type type)
 {
@@ -250,16 +330,6 @@ RenderMaterial::setNormalStrength(const double normalStrength)
     }
 }
 
-void
-RenderMaterial::setIndexOfRefraction(const double indexOfRefraction)
-{
-    if (indexOfRefraction == m_indexOfRefraction)
-    {
-        m_indexOfRefraction = indexOfRefraction;
-        postModified();
-    }
-}
-
 void
 RenderMaterial::setEdgeColor(const Color& color)
 {
diff --git a/Source/Rendering/Materials/imstkRenderMaterial.h b/Source/Rendering/Materials/imstkRenderMaterial.h
index a74d25a5be4dc83956ea3193a6b4df3008e8b8de..7ed25c7a98ef2b04688ad24a118d14c06baa5f7c 100644
--- a/Source/Rendering/Materials/imstkRenderMaterial.h
+++ b/Source/Rendering/Materials/imstkRenderMaterial.h
@@ -102,12 +102,6 @@ public:
     const DisplayMode getDisplayMode() const { return m_displayMode; }
     void setDisplayMode(const DisplayMode displayMode);
 
-    ///
-    /// \brief Get/Set tessellated
-    ///
-    const bool getTessellated() const { return m_tessellated; }
-    void setTessellated(const bool tessellated);
-
     ///
     /// \brief Get/Set line width or the wireframe
     ///
@@ -191,6 +185,33 @@ public:
     const double getEmissivity() const { return m_emissivity; }
     void setEmissivity(const double emissivity);
 
+    const double getAnisotropy() const { return m_anisotropy; }
+    void setAnisotropy(const double anisotropy);
+
+    const double getAnisotropyRotation() const { return m_anisotropyRotation; }
+    void setAnisotropyRotation(const double anisotropyRotation);
+
+    const double getBaseIOR() const { return m_baseIOR; }
+    void setBaseIOR(const double baseIOR);
+
+    const Color& getCoatColor() const { return m_coatColor; }
+    void setCoatColor(const Color& coatColor);
+
+    const double getCoatIOR() const { return m_coatIOR; }
+    void setCoatIOR(const double coatIOR);
+
+    const double getCoatNormalScale() const { return m_coatNormalScale; }
+    void setCoatNormalScale(const double coatNormalScale);
+
+    const double getCoatRoughness() const { return m_coatRoughness; }
+    void setCoatRoughness(const double coatRoughness);
+
+    const double getCoatStrength() const { return m_coatStrength; }
+    void setCoatStrength(const double coatStrength);
+
+    const Color& getEdgeTint() const { return m_edgeTint; }
+    void setEdgeTint(const Color& edgeTint);
+
     ///
     /// \brief Add/Remove/Get texture
     ///
@@ -213,13 +234,6 @@ public:
     virtual void setBlendMode(const BlendMode blendMode);
     const BlendMode getBlendMode() const { return m_blendMode; }
 
-    ///
-    /// \brief Checks if the material must be handled uniquely
-    ///
-    bool isDecal() const { return m_isDecal; }
-    bool isParticle() const { return m_isParticle; }
-    bool isLineMesh() const { return m_isLineMesh; }
-
     const DisplayMode getRenderMode() const { return m_displayMode; }
     const ShadingModel getShadingModel() const { return m_shadingModel; }
     void setShadingModel(const ShadingModel& model);
@@ -230,9 +244,6 @@ public:
     double getNormalStrength() const { return m_normalStrength; }
     void setNormalStrength(const double n);
 
-    double getIndexOfRefraction() const { return m_indexOfRefraction; }
-    void setIndexOfRefraction(const double n);
-
     const Color& getEdgeColor() const { return m_edgeColor; }
     void setEdgeColor(const Color& color);
 
@@ -300,20 +311,23 @@ protected:
     double m_roughness = 1.0; ///< Value for roughness with range: [0.0, 1.0]
     double m_occlusionStrength = 1.0;
     double m_normalStrength    = 1.0;
-    double m_indexOfRefraction = 1.0;
 
-    ///---------------------Global states------------------------
-    bool m_imageBasedLighting = false;
+    ///----------------PBR Clearcoat properties-------------------
+    double m_anisotropy = 0.0;
+    double m_anisotropyRotation = 0.0;
+    double m_baseIOR         = 1.5; // Base index of refraction
+    Color  m_coatColor       = Color::White;
+    double m_coatIOR         = 2.0; // Coat index of refraction
+    double m_coatNormalScale = 1.0;
+    double m_coatRoughness   = 0.0;
+    double m_coatStrength    = 0.0;
+    Color  m_edgeTint        = Color::White;
 
     bool m_backfaceCulling = true; ///< For performance, uncommon for this to be false
 
     DisplayMode  m_displayMode  = DisplayMode::Surface;
     ShadingModel m_shadingModel = ShadingModel::Phong;
 
-    bool m_tessellated = false;
-    bool m_isDecal     = false;
-    bool m_isLineMesh  = false;
-    bool m_isParticle  = false;
     bool m_recomputeVertexNormals = true; ///> Update vertex normals when vertex or index data is changed
 
     std::shared_ptr<ColorFunction> m_lookupTable;
diff --git a/Source/Rendering/Materials/imstkTexture.cpp b/Source/Rendering/Materials/imstkTexture.cpp
index 2d1c389ac498d4b4d4b6c489e465b293dff23570..454b11f4149c4bd3d62e86452095e89b077f2cb8 100644
--- a/Source/Rendering/Materials/imstkTexture.cpp
+++ b/Source/Rendering/Materials/imstkTexture.cpp
@@ -39,64 +39,6 @@ Texture::getType() const
     return m_type;
 }
 
-std::string
-Texture::getTypeAsString() const
-{
-    return Texture::getTypeAsString(m_type);
-}
-
-std::string
-Texture::getTypeAsString(Type type)
-{
-    switch (type)
-    {
-    case Type::Diffuse:
-    {
-        return "Diffuse";
-    }
-    case Type::Normal:
-    {
-        return "Normal";
-    }
-    case Type::Roughness:
-    {
-        return "Roughness";
-    }
-    case Type::Metalness:
-    {
-        return "Metalness";
-    }
-    case Type::SubsurfaceScattering:
-    {
-        return "Subsurface_scattering";
-    }
-    case Type::AmbientOcclusion:
-    {
-        return "Ambient_Occlusion";
-    }
-    case Type::Cavity:
-    {
-        return "Cavity";
-    }
-    case Type::IrradianceCubeMap:
-    {
-        return "Irradiance_Cubemap";
-    }
-    case Type::RadianceCubeMap:
-    {
-        return "Radiance_Cubemap";
-    }
-    case Type::BRDF_LUT:
-    {
-        return "BRDF_LUT";
-    }
-    default:
-    {
-        return "None";
-    }
-    }
-}
-
 const Texture::FileType
 Texture::getFileType()
 {
diff --git a/Source/Rendering/Materials/imstkTexture.h b/Source/Rendering/Materials/imstkTexture.h
index 06972e1378b9be85736472ebd9e10f5d399c81c0..66896c519845f2ae958d466d77d79350f8471b7a 100644
--- a/Source/Rendering/Materials/imstkTexture.h
+++ b/Source/Rendering/Materials/imstkTexture.h
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "imstkEventObject.h"
+#include "imstkColor.h"
 
 #include <string>
 #include <memory>
@@ -53,9 +54,11 @@ public:
         Cubemap,
         IrradianceCubeMap,
         RadianceCubeMap,
+        ORM,
         BRDF_LUT,
         Emissive,
-        ORM,
+        Anistropy,
+        CoatNormal,
         None
     };
 
@@ -71,6 +74,13 @@ public:
         Dds
     };
 
+    enum class WrapType
+    {
+        CLAMP_TO_EDGE,   // Clamps without border color
+        CLAMP_TO_BORDER, // Pixels outside [0,1] use border color
+        REPEAT           // Pixels outside [0,1] repeat back to [0,1] in a modulus fashion. Such that 1.3, becomes 0.3
+    };
+
     ///
     /// \brief Constructor
     /// \param path Path to the texture source file
@@ -103,16 +113,6 @@ public:
     ///
     Type getType() const;
 
-    ///
-    /// \brief Get type as a string
-    ///
-    std::string getTypeAsString() const;
-
-    ///
-    /// \brief Convert a Type into a string
-    ///
-    static std::string getTypeAsString(Type type);
-
     ///
     /// \brief Get path
     ///
@@ -129,9 +129,21 @@ public:
     const bool getMipmapsEnabled() const { return m_mipmapsEnabled; }
 
     ///
-    /// \brief Get if repeat is enabled, if off it clamps
+    /// \brief Get the wrapping type
     ///
-    const bool getRepeating() const { return m_repeating; }
+    const WrapType getWrapType() const { return m_wrapType; }
+    void setWrapType(const WrapType repeat)
+    {
+        m_wrapType = repeat;
+        postModified();
+    }
+
+    const Color& getBorderColor() const { return m_borderColor; }
+    void setBorderColor(const Color& color)
+    {
+        m_borderColor = color;
+        postModified();
+    }
 
     ///
     /// \brief Get if anisotropic filtering is enabled
@@ -175,8 +187,8 @@ protected:
     // Helps with texture aliasing (and a little with performance)
     bool m_mipmapsEnabled = true;
 
-    // Repeating
-    bool m_repeating = true;
+    WrapType m_wrapType    = WrapType::REPEAT;
+    Color    m_borderColor = Color::Black;
 
     // Helps sharpen mipmapped textures at more extreme angles
     bool   m_anisotropyEnabled = true;
diff --git a/Source/Rendering/Testing/imstkRenderScreenshotTest.cpp b/Source/Rendering/Testing/imstkRenderScreenshotTest.cpp
index 46e8b8340d208bbe80d6c0ffcdfd3c2d521345a4..164cbd180fa65b971e71ea4cae45a94e5b86effa 100644
--- a/Source/Rendering/Testing/imstkRenderScreenshotTest.cpp
+++ b/Source/Rendering/Testing/imstkRenderScreenshotTest.cpp
@@ -40,9 +40,9 @@ TEST(imstkRenderScreenshotTest, ScreenshotTest)
     const std::string screenshotFileName = "Screenshot-" + std::to_string(0) + ".png";
     if (vtksys::SystemTools::FileExists(screenshotFileName))
     {
-        const bool fileWasRemoved = vtksys::SystemTools::RemoveFile(screenshotFileName);
-        EXPECT_TRUE(fileWasRemoved) << "Failed to remove existing screenshot file before screenshotTest";
-        if (!fileWasRemoved)
+        const vtksys::Status fileStatus = vtksys::SystemTools::RemoveFile(screenshotFileName);
+        EXPECT_FALSE(fileStatus.IsSuccess()) << "Failed to remove existing screenshot file before screenshotTest";
+        if (!fileStatus.IsSuccess())
         {
             return;
         }
diff --git a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKPolyDataRenderDelegate.cpp b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKPolyDataRenderDelegate.cpp
index 2f0238fe381617761e962c017600fc4682d19d31..87973a1f61955459df74ac162b5a2b0d57b9415f 100644
--- a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKPolyDataRenderDelegate.cpp
+++ b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKPolyDataRenderDelegate.cpp
@@ -83,6 +83,8 @@ VTKPolyDataRenderDelegate::updateRenderProperties()
     const Color& edgeColor     = material->getEdgeColor();
     const Color& vertexColor   = material->getVertexColor();
     const Color& surfaceColor  = material->getColor();
+    const Color& coatColor     = material->getCoatColor();
+    const Color& edgeTintColor = material->getEdgeTint();
 
     // Phong
     actorProperty->SetDiffuseColor(diffuseColor.r, diffuseColor.g, diffuseColor.b);
@@ -98,6 +100,16 @@ VTKPolyDataRenderDelegate::updateRenderProperties()
     actorProperty->SetRoughness(material->getRoughness());
     actorProperty->SetMetallic(material->getMetalness());
     actorProperty->SetNormalScale(material->getNormalStrength());
+    // PBR clearcoat
+    actorProperty->SetAnisotropy(material->getAnisotropy());
+    actorProperty->SetAnisotropyRotation(material->getAnisotropyRotation());
+    actorProperty->SetBaseIOR(material->getBaseIOR());
+    actorProperty->SetCoatColor(coatColor.r, coatColor.g, coatColor.b);
+    actorProperty->SetCoatIOR(material->getCoatIOR());
+    actorProperty->SetCoatNormalScale(material->getCoatNormalScale());
+    actorProperty->SetCoatRoughness(material->getCoatRoughness());
+    actorProperty->SetCoatStrength(material->getCoatStrength());
+    actorProperty->SetEdgeTint(edgeTintColor.r, edgeTintColor.g, edgeTintColor.b);
 
     // Base
     actorProperty->SetColor(surfaceColor.r, surfaceColor.g, surfaceColor.b);
@@ -111,14 +123,8 @@ VTKPolyDataRenderDelegate::updateRenderProperties()
 
     if (material->getShadingModel() == RenderMaterial::ShadingModel::PBR)
     {
-        /*actorProperty->UseImageBasedLightingOn();
-            actorProperty->SetEnvironmentCubeMap(getVTKTexture(cubemap));*/
-
         actorProperty->LightingOn();
         actorProperty->SetInterpolationToPBR();
-
-        // configure the basic properties
-        //actorProperty->SetColor(surfaceColor.r, surfaceColor.g, surfaceColor.b);
     }
     else if (material->getShadingModel() == RenderMaterial::ShadingModel::Phong)
     {
diff --git a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
index 87ce817b68db6c320b60b73db147836927529ca3..bab1eb26362a005ced186c6afe5f3b7e29d3bf9e 100644
--- a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
+++ b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKSurfaceMeshRenderDelegate.cpp
@@ -512,7 +512,7 @@ VTKSurfaceMeshRenderDelegate::initializeTextures()
         */
 
         // Set texture
-        auto currentTexture = textureDelegate->getVtkTexture();
+        vtkSmartPointer<vtkTexture> currentTexture = textureDelegate->getVtkTexture();
         if (material->getShadingModel() == RenderMaterial::ShadingModel::PBR)
         {
             switch (texture->getType())
@@ -533,6 +533,16 @@ VTKSurfaceMeshRenderDelegate::initializeTextures()
                 actor->GetProperty()->SetORMTexture(currentTexture);
                 break;
             }
+            case Texture::Type::Anistropy:
+            {
+                actor->GetProperty()->SetAnisotropyTexture(currentTexture);
+                break;
+            }
+            case Texture::Type::CoatNormal:
+            {
+                actor->GetProperty()->SetCoatNormalTexture(currentTexture);
+                break;
+            }
             default:
             {
             }
diff --git a/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp b/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp
index 84ad4d2b49f272d98a52a4469c38d1504320bf0e..2049fbf0768d5ce7b3791b57099f8cf337246c25 100644
--- a/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp
+++ b/Source/Rendering/VTKRenderer/imstkVTKRenderer.cpp
@@ -396,20 +396,24 @@ VTKRenderer::updateCamera()
     std::shared_ptr<Camera> cam = m_scene->getActiveCamera();
     getVtkRenderer()->SetActiveCamera(m_camera);
 
-    // Update the camera to obtain corrected view/proj matrices
-    cam->update();
+    // As long as we don't have a VR camera apply the camera view
+    if (vtkOpenVRCamera::SafeDownCast(m_camera) == nullptr)
+    {
+        // Update the camera to obtain corrected view/proj matrices
+        cam->update();
 
-    // Get the view matrix
-    const Mat4d& invView = cam->getInvView();
+        // Get the view matrix
+        const Mat4d& invView = cam->getInvView();
 
-    const double eyePos[3]  = { invView(0, 3), invView(1, 3), invView(2, 3) };
-    const double forward[3] = { invView(0, 2), invView(1, 2), invView(2, 2) };
-    const double up[3]      = { invView(0, 1), invView(1, 1), invView(2, 1) };
+        const double eyePos[3]  = { invView(0, 3), invView(1, 3), invView(2, 3) };
+        const double forward[3] = { invView(0, 2), invView(1, 2), invView(2, 2) };
+        const double up[3]      = { invView(0, 1), invView(1, 1), invView(2, 1) };
 
-    m_camera->SetPosition(eyePos);
-    m_camera->SetFocalPoint(eyePos[0] - forward[0], eyePos[1] - forward[1], eyePos[2] - forward[2]);
-    m_camera->SetViewUp(up[0], up[1], up[2]);
-    m_camera->SetViewAngle(cam->getFieldOfView());
+        m_camera->SetPosition(eyePos);
+        m_camera->SetFocalPoint(eyePos[0] - forward[0], eyePos[1] - forward[1], eyePos[2] - forward[2]);
+        m_camera->SetViewUp(up[0], up[1], up[2]);
+        m_camera->SetViewAngle(cam->getFieldOfView());
+    }
     m_camera->SetClippingRange(cam->getNearZ(), cam->getFarZ());
 
     // Copy the projection back to the camera
diff --git a/Source/Rendering/VTKRenderer/imstkVTKTextureDelegate.cpp b/Source/Rendering/VTKRenderer/imstkVTKTextureDelegate.cpp
index 6826d7cce6e1586159fa6cb3cd29a2392b081e8e..9516e0666966add68c350126eb5c2bc5c6d4fd6b 100644
--- a/Source/Rendering/VTKRenderer/imstkVTKTextureDelegate.cpp
+++ b/Source/Rendering/VTKRenderer/imstkVTKTextureDelegate.cpp
@@ -20,6 +20,7 @@
 =========================================================================*/
 
 #include "imstkVTKTextureDelegate.h"
+#include "imstkColor.h"
 #include "imstkGeometryUtilities.h"
 #include "imstkLogger.h"
 #include "imstkTexture.h"
@@ -82,7 +83,25 @@ VTKTextureDelegate::VTKTextureDelegate(std::shared_ptr<Texture> texture) : m_vtk
             imgReader->SetFileName(tFileName.c_str());
             imgReader->Update();
             m_vtkTexture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD);
-            m_vtkTexture->SetRepeat(m_texture->getRepeating());
+            const Texture::WrapType wrapType = m_texture->getWrapType();
+            if (wrapType == Texture::WrapType::REPEAT)
+            {
+                m_vtkTexture->SetWrap(vtkTexture::Repeat);
+            }
+            else if (wrapType == Texture::WrapType::CLAMP_TO_BORDER)
+            {
+                m_vtkTexture->SetWrap(vtkTexture::ClampToBorder);
+            }
+            else if (wrapType == Texture::WrapType::CLAMP_TO_EDGE)
+            {
+                m_vtkTexture->SetWrap(vtkTexture::ClampToEdge);
+            }
+            const Color& borderColor = m_texture->getBorderColor();
+            m_vtkTexture->SetBorderColor(
+                static_cast<float>(borderColor.r),
+                static_cast<float>(borderColor.g),
+                static_cast<float>(borderColor.b),
+                static_cast<float>(borderColor.a));
             m_vtkTexture->SetInterpolate(m_texture->getInterpolation());
             m_vtkTexture->SetInputConnection(0, imgReader->GetOutputPort());
 
@@ -98,7 +117,26 @@ VTKTextureDelegate::VTKTextureDelegate(std::shared_ptr<Texture> texture) : m_vtk
         vtkSmartPointer<vtkImageData> vtkImgData = GeometryUtils::coupleVtkImageData(imstkImgData);
         m_vtkTexture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD);
         m_vtkTexture->SetInterpolate(m_texture->getInterpolation());
-        m_vtkTexture->SetRepeat(m_texture->getRepeating());
+
+        const Texture::WrapType wrapType = m_texture->getWrapType();
+        if (wrapType == Texture::WrapType::REPEAT)
+        {
+            m_vtkTexture->SetWrap(vtkTexture::Repeat);
+        }
+        else if (wrapType == Texture::WrapType::CLAMP_TO_BORDER)
+        {
+            m_vtkTexture->SetWrap(vtkTexture::ClampToBorder);
+        }
+        else if (wrapType == Texture::WrapType::CLAMP_TO_EDGE)
+        {
+            m_vtkTexture->SetWrap(vtkTexture::ClampToEdge);
+        }
+        const Color& borderColor = m_texture->getBorderColor();
+        m_vtkTexture->SetBorderColor(
+            static_cast<float>(borderColor.r),
+            static_cast<float>(borderColor.g),
+            static_cast<float>(borderColor.b),
+            static_cast<float>(borderColor.a));
         m_vtkTexture->SetInputData(vtkImgData);
 
         if (texture->getType() == Texture::Type::Diffuse)
diff --git a/Source/SceneEntities/CMakeLists.txt b/Source/SceneEntities/CMakeLists.txt
index 6c0b815f8cb44b7ebdffd3eefcbcf0861f368e3b..25c83e2864e4d2037c0548da62404c1372313988 100644
--- a/Source/SceneEntities/CMakeLists.txt
+++ b/Source/SceneEntities/CMakeLists.txt
@@ -33,6 +33,6 @@ imstk_add_library(SceneEntities
 #-----------------------------------------------------------------------------
 # Testing
 #-----------------------------------------------------------------------------
-#if( ${PROJECT_NAME}_BUILD_TESTING )
-#  add_subdirectory( Testing )
-#endif()
\ No newline at end of file
+if( ${PROJECT_NAME}_BUILD_TESTING )
+  add_subdirectory(Testing)
+endif()
\ No newline at end of file
diff --git a/Source/SceneEntities/Camera/imstkCamera.h b/Source/SceneEntities/Camera/imstkCamera.h
index 4bf635e86fe4e16692700bcc0ed1e7a528286a54..1105bae36964995ebe6e6cc167c0eb4f0817dc2e 100644
--- a/Source/SceneEntities/Camera/imstkCamera.h
+++ b/Source/SceneEntities/Camera/imstkCamera.h
@@ -258,8 +258,8 @@ protected:
 
 protected:
     // Lookat camera parameters
-    Vec3d m_position   = Vec3d(0.0, 2.0, 5.0); ///> camera position
-    Vec3d m_focalPoint = Vec3d::Zero();        ///> camera focal point
+    Vec3d m_position   = Vec3d(0.0, 0.0, 0.0); ///> camera position
+    Vec3d m_focalPoint = -Vec3d::UnitZ();      ///> camera focal point
     Vec3d m_viewUp     = Vec3d::UnitY();       ///> camera up vector
 };
 }
diff --git a/Source/SceneEntities/Testing/CMakeLists.txt b/Source/SceneEntities/Testing/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2e8b104f5d3b67ef0aa009ca435f165a47a1cebd
--- /dev/null
+++ b/Source/SceneEntities/Testing/CMakeLists.txt
@@ -0,0 +1,2 @@
+include(imstkAddTest)
+imstk_add_test( SceneEntities )
\ No newline at end of file
diff --git a/Source/SceneEntities/Testing/imstkCameraTest.cpp b/Source/SceneEntities/Testing/imstkCameraTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc7a1209b04d68806fcaa37ebaaa889b76c0bf66
--- /dev/null
+++ b/Source/SceneEntities/Testing/imstkCameraTest.cpp
@@ -0,0 +1,50 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include "imstkCamera.h"
+
+#include <gtest/gtest.h>
+
+using namespace imstk;
+
+TEST(imstkCameraTest, camera_defaults)
+{
+    Camera cam;
+
+    // Ensure the camera is initialized to identity
+    EXPECT_EQ(cam.getView(), Mat4d::Identity())
+        << "Expected: " << Mat4d::Identity()
+        << " Actual: " << cam.getView();
+    EXPECT_EQ(cam.getInvView(), Mat4d::Identity())
+        << "Expected: " << Mat4d::Identity()
+        << " Actual: " << cam.getInvView();
+
+    cam.update();
+
+    // Ensure camera is initialized to identity even after applying
+    // lookat
+    EXPECT_EQ(cam.getView(), Mat4d::Identity())
+        << "Expected: " << Mat4d::Identity()
+        << " Actual: " << cam.getView();
+    EXPECT_EQ(cam.getInvView(), Mat4d::Identity())
+        << "Expected: " << Mat4d::Identity()
+        << " Actual: " << cam.getInvView();
+}
\ No newline at end of file
diff --git a/Source/SimulationManager/CMakeLists.txt b/Source/SimulationManager/CMakeLists.txt
index 56803b65a7ff696f9fdb985b1408de417d3d13a4..fee6891be7b96f1f892d9167a9b8d70036fa5973 100644
--- a/Source/SimulationManager/CMakeLists.txt
+++ b/Source/SimulationManager/CMakeLists.txt
@@ -5,14 +5,6 @@
 file(GLOB VTK_H_FILES VTKRenderer/imstk*.h)
 file(GLOB VTK_CPP_FILES VTKRenderer/imstk*.cpp)
 
-list(APPEND ExclusionFiles "")
-# Don't build with X11 if on windows or (on linux and building headless)
-if(WIN32 OR (UNIX AND iMSTK_USE_VTK_OSMESA))
-  list(APPEND ExclusionFiles
-    VTKRenderer/imstkVtkXRenderWindowInteractor2.h
-    VTKRenderer/imstkVtkXRenderWindowInteractor2.cpp)
-endif()
-
 file(GLOB SIMMANAGER_H_FILES imstk*.h)
 file(GLOB SIMMANAGER_CPP_FILES imstk*.cpp)
 
@@ -29,13 +21,20 @@ imstk_add_library(SimulationManager
     ${SIMULATIONMANAGER_CPP_FILES}
   SUBDIR_LIST
     ${SIMULATIONMANAGER_SUBDIR}
-  EXCLUDE_FILES
-    ${ExclusionFiles}
   DEPENDS
     Scene
     Rendering
   )
 
+# Install VR actions json
+file(GLOB VTK_VR_JSON_FILES VTKRenderer/vtk_openvr_*.json)
+if (MSVC)
+  # Show them in visual studios
+  target_sources(SimulationManager PRIVATE ${VTK_VR_JSON_FILES})
+  set_source_files_properties(${VTK_VR_JSON_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
+endif()
+install(FILES ${VTK_VR_JSON_FILES} DESTINATION bin)
+
 #-----------------------------------------------------------------------------
 # Testing
 #-----------------------------------------------------------------------------
diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.cpp
index 39a16f8eba76e43a378f98c51300c399b081768f..af5c294ff53b71010cc36b009611bd10a43652ef 100644
--- a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.cpp
+++ b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.cpp
@@ -20,8 +20,11 @@
 =========================================================================*/
 
 #include "imstkVTKInteractorStyleVR.h"
+#include "imstkLogger.h"
 #include "imstkOpenVRDeviceClient.h"
 
+#include <vtkMath.h>
+#include <vtkEventData.h>
 #include <vtkObjectFactory.h>
 #include <vtkOpenVRRenderWindowInteractor.h>
 
@@ -35,6 +38,88 @@ vtkInteractorStyleVR::vtkInteractorStyleVR()
     m_hmdDeviceClient = imstk::OpenVRDeviceClient::New(OPENVR_HMD);
 }
 
+void
+vtkInteractorStyleVR::OnButtonPress(vtkEventData* data, int buttonId)
+{
+    vtkEventDataForDevice*   eventDataButton = data->GetAsEventDataForDevice();
+    const vtkEventDataAction action = eventDataButton->GetAction();
+    const vtkEventDataDevice device = eventDataButton->GetDevice();
+
+    if (device == vtkEventDataDevice::LeftController)
+    {
+        if (action == vtkEventDataAction::Press)
+        {
+            m_leftControllerDeviceClient->emitButtonPress(buttonId);
+        }
+        else if (action == vtkEventDataAction::Release)
+        {
+            m_leftControllerDeviceClient->emitButtonRelease(buttonId);
+        }
+    }
+    else if (device == vtkEventDataDevice::RightController)
+    {
+        if (action == vtkEventDataAction::Press)
+        {
+            m_rightControllerDeviceClient->emitButtonPress(buttonId);
+        }
+        else if (action == vtkEventDataAction::Release)
+        {
+            m_rightControllerDeviceClient->emitButtonRelease(buttonId);
+        }
+    }
+}
+
+void
+vtkInteractorStyleVR::addMovementActions()
+{
+    vtkOpenVRRenderWindowInteractor* iren =
+        vtkOpenVRRenderWindowInteractor::SafeDownCast(GetInteractor());
+    CHECK(iren->GetInitialized()) << "Cannot addMovementActions to style until "
+        "interactor has been initialized";
+    iren->AddAction("/actions/vtk/in/LeftGripMovement", true,
+        [this](vtkEventData* ed)
+    {
+        vtkEventDataDevice3D* edd = ed->GetAsEventDataDevice3D();
+        const double* pos = edd->GetTrackPadPosition();
+        m_leftControllerDeviceClient->setTrackpadPosition(imstk::Vec2d(pos[0], pos[1]));
+        });
+    iren->AddAction("/actions/vtk/in/RightGripMovement", true,
+        [this](vtkEventData* ed)
+    {
+        vtkEventDataDevice3D* edd = ed->GetAsEventDataDevice3D();
+        const double* pos = edd->GetTrackPadPosition();
+        m_rightControllerDeviceClient->setTrackpadPosition(imstk::Vec2d(pos[0], pos[1]));
+        });
+}
+
+void
+vtkInteractorStyleVR::addButtonActions()
+{
+    vtkOpenVRRenderWindowInteractor* iren =
+        vtkOpenVRRenderWindowInteractor::SafeDownCast(GetInteractor());
+    CHECK(iren->GetInitialized()) << "Cannot addButtonActions to style until "
+        "interactor has been initialized";
+
+    // Called when buttons are pressed/released
+    std::array<std::string, 6> buttonActionNames =
+    {
+        "/actions/vtk/in/Button0Pressed",
+        "/actions/vtk/in/Button1Pressed",
+        "/actions/vtk/in/Button2Pressed",
+        "/actions/vtk/in/Button3Pressed",
+        "/actions/vtk/in/GripPressed",
+        "/actions/vtk/in/TriggerPressed"
+    };
+    for (int i = 0; i < 6; i++)
+    {
+        iren->AddAction(buttonActionNames[i], false,
+            [this, i](vtkEventData* ed)
+        {
+            OnButtonPress(ed, i);
+            });
+    }
+}
+
 void
 vtkInteractorStyleVR::OnMove3D(vtkEventData* eventData)
 {
@@ -70,63 +155,4 @@ vtkInteractorStyleVR::OnMove3D(vtkEventData* eventData)
         m_hmdDeviceClient->setPose(pos, imstk::Quatd(imstk::Rotd(vtkMath::RadiansFromDegrees(orientation[0]),
             imstk::Vec3d(orientation[1], orientation[2], orientation[3]))));
     }
-}
-
-void
-vtkInteractorStyleVR::OnButton3D(vtkEventData* eventData)
-{
-    if (eventData->GetType() != vtkCommand::Button3DEvent)
-    {
-        return;
-    }
-    vtkEventDataButton3D*         eventDataButton = static_cast<vtkEventDataButton3D*>(eventData);
-    const vtkEventDataDevice      device = eventDataButton->GetDevice();
-    const vtkEventDataAction      action = eventDataButton->GetAction();
-    const vtkEventDataDeviceInput input  = eventDataButton->GetInput();
-
-    switch (action)
-    {
-    case vtkEventDataAction::Press:
-        if (device == vtkEventDataDevice::LeftController)
-        {
-            m_leftControllerDeviceClient->emitButtonPress(static_cast<int>(input));
-        }
-        else if (device == vtkEventDataDevice::RightController)
-        {
-            m_rightControllerDeviceClient->emitButtonPress(static_cast<int>(input));
-        }
-        break;
-    case vtkEventDataAction::Release:
-        if (device == vtkEventDataDevice::LeftController)
-        {
-            m_leftControllerDeviceClient->emitButtonRelease(static_cast<int>(input));
-        }
-        else if (device == vtkEventDataDevice::RightController)
-        {
-            m_rightControllerDeviceClient->emitButtonRelease(static_cast<int>(input));
-        }
-        break;
-    case vtkEventDataAction::Touch:
-        if (device == vtkEventDataDevice::LeftController)
-        {
-            m_leftControllerDeviceClient->emitButtonTouched(static_cast<int>(input));
-        }
-        else if (device == vtkEventDataDevice::RightController)
-        {
-            m_rightControllerDeviceClient->emitButtonTouched(static_cast<int>(input));
-        }
-        break;
-    case vtkEventDataAction::Untouch:
-        if (device == vtkEventDataDevice::LeftController)
-        {
-            m_leftControllerDeviceClient->emitButtonUntouched(static_cast<int>(input));
-        }
-        else if (device == vtkEventDataDevice::RightController)
-        {
-            m_rightControllerDeviceClient->emitButtonUntouched(static_cast<int>(input));
-        }
-        break;
-    default:
-        break;
-    }
 }
\ No newline at end of file
diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.h b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.h
index 2e41869663f89bd6384676584eaeb00f353679dc..7828e23c9facb3e22e609c22504c2108b6992ca3 100644
--- a/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.h
+++ b/Source/SimulationManager/VTKRenderer/imstkVTKInteractorStyleVR.h
@@ -38,7 +38,16 @@ public:
     vtkTypeMacro(vtkInteractorStyleVR, vtkInteractorStyle3D);
 
     void OnMove3D(vtkEventData* edata) override;
-    void OnButton3D(vtkEventData* edata) override;
+
+    ///
+    /// \brief Adds button actions
+    ///
+    void addButtonActions();
+
+    ///
+    /// \brief Adds thumbstick movement actions
+    ///
+    void addMovementActions();
 
     std::shared_ptr<imstk::OpenVRDeviceClient> getLeftControllerDeviceClient() const { return m_leftControllerDeviceClient; }
     std::shared_ptr<imstk::OpenVRDeviceClient> getRightControllerDeviceClient() const { return m_rightControllerDeviceClient; }
@@ -47,6 +56,9 @@ public:
 public:
     vtkInteractorStyleVR();
 
+protected:
+    void OnButtonPress(vtkEventData* data, int buttonId);
+
 public:
     std::shared_ptr<imstk::OpenVRDeviceClient> m_leftControllerDeviceClient;
     std::shared_ptr<imstk::OpenVRDeviceClient> m_rightControllerDeviceClient;
diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKOpenVRViewer.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKOpenVRViewer.cpp
index ea7f63e3981b6d8f0e6d26259b6e2d4c825d17fc..7179a5f90c7ae88a448af8dbe6ee8d51f907fe14 100644
--- a/Source/SimulationManager/VTKRenderer/imstkVTKOpenVRViewer.cpp
+++ b/Source/SimulationManager/VTKRenderer/imstkVTKOpenVRViewer.cpp
@@ -20,6 +20,7 @@
 =========================================================================*/
 
 #include "imstkVTKOpenVRViewer.h"
+#include "imstkCamera.h"
 #include "imstkDeviceControl.h"
 #include "imstkLogger.h"
 #include "imstkOpenVRDeviceClient.h"
@@ -28,10 +29,10 @@
 #include "imstkVTKInteractorStyleVR.h"
 #include "imstkVTKRenderer.h"
 
+#include <vtkOpenVRRenderWindowInteractor.h>
 #include <vtkMatrix4x4.h>
 #include <vtkOpenVRRenderer.h>
 #include <vtkOpenVRRenderWindow.h>
-#include <vtkOpenVRRenderWindowInteractor.h>
 #include <vtkOpenVRModel.h>
 
 namespace imstk
@@ -157,25 +158,32 @@ VTKOpenVRViewer::initModule()
 
     // VR interactor doesn't support timers, here we throw timer event every update
     // another option would be to conform VTKs VR interactor
-    vtkSmartPointer<vtkOpenVRRenderWindowInteractor> iren = vtkOpenVRRenderWindowInteractor::SafeDownCast(m_vtkRenderWindow->GetInteractor());
+    auto iren = vtkOpenVRRenderWindowInteractor::SafeDownCast(m_vtkRenderWindow->GetInteractor());
     //iren->Start(); // Cannot use
     if (iren->HasObserver(vtkCommand::StartEvent))
     {
         iren->InvokeEvent(vtkCommand::StartEvent, nullptr);
         return true;
     }
+
+    auto renWin = vtkOpenVRRenderWindow::SafeDownCast(m_vtkRenderWindow);
+    renWin->Initialize();
+
     iren->Initialize();
 
     // Hide the device overlays
     // \todo: Display devices in debug mode
-    vtkSmartPointer<vtkOpenVRRenderWindow> renWin = vtkOpenVRRenderWindow::SafeDownCast(m_vtkRenderWindow);
-    renWin->Initialize();
     renWin->Render(); // Must do one render to initialize vtkOpenVRModel's to then hide the devices
 
-    // Hide all controllers
+    // Actions must be added after initialization of interactor
+    vtkInteractorStyleVR* iStyle = vtkInteractorStyleVR::SafeDownCast(m_vtkInteractorStyle.get());
+    iStyle->addButtonActions();
+    iStyle->addMovementActions();
+
+    // Hide all controller models
     for (uint32_t i = 0; i < vr::k_unMaxTrackedDeviceCount; i++)
     {
-        vtkOpenVRModel* trackedDeviceModel = renWin->GetTrackedDeviceModel(i);
+        vtkVRModel* trackedDeviceModel = renWin->GetTrackedDeviceModel(i);
         if (trackedDeviceModel != nullptr)
         {
             trackedDeviceModel->SetVisibility(false);
@@ -194,6 +202,12 @@ VTKOpenVRViewer::updateModule()
         return;
     }
 
+    // For the VR view we can't supply the a camera in the normal sense
+    // we need to pre multiply a "user view"
+    std::shared_ptr<Camera> cam  = getActiveScene()->getActiveCamera();
+    const Mat4d&            view = cam->getView();
+    setPhysicalToWorldTransform(view);
+
     // Update Camera
     // \todo: No programmatic control over VR camera currently
     //renderer->updateSceneCamera(getActiveScene()->getCamera());
diff --git a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp
index d5c3ca86dede379b224e76f8fd5a0a633ac42462..ff47818924190d8ba8f2bf0b956d7b8e4099d6bd 100644
--- a/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp
+++ b/Source/SimulationManager/VTKRenderer/imstkVTKViewer.cpp
@@ -41,7 +41,7 @@
 #ifdef iMSTK_USE_VTK_OSMESA
 #include <vtkGenericRenderWindowInteractor.h>
 #else
-#include "imstkVtkXRenderWindowInteractor2.h"
+#include <vtkXRenderWindowInteractor.h>
 #endif
 #endif
 
@@ -65,7 +65,7 @@ VTKViewer::VTKViewer(std::string name) : AbstractVTKViewer(name),
     vtkSmartPointer<vtkGenericRenderWindowInteractor> iren = vtkSmartPointer<vtkGenericRenderWindowInteractor>::New();
     iren->SetInteractorStyle(m_vtkInteractorStyle.get());
 #else
-    vtkSmartPointer<vtkXRenderWindowInteractor2> iren = vtkSmartPointer<vtkXRenderWindowInteractor2>::New();
+    vtkSmartPointer<vtkXRenderWindowInteractor> iren = vtkSmartPointer<vtkXRenderWindowInteractor>::New();
     iren->SetInteractorStyle(m_vtkInteractorStyle.get());
 #endif
 #endif
diff --git a/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.cpp b/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.cpp
deleted file mode 100644
index 7ebd5b2ad2f37946164e13fa035eb278f90f2ce7..0000000000000000000000000000000000000000
--- a/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.cpp
+++ /dev/null
@@ -1,885 +0,0 @@
-/*=========================================================================
-
-  Program:   Visualization Toolkit
-  Module:    vtkXRenderWindowInteractor2.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 "imstkVtkXRenderWindowInteractor2.h"
-
-#include <algorithm>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <sys/time.h>
-
-#include "vtkActor.h"
-#include "vtkCallbackCommand.h"
-#include "vtkCommand.h"
-#include "vtkInteractorStyle.h"
-#include "vtkObjectFactory.h"
-#include "vtkRenderWindow.h"
-#include "vtkStringArray.h"
-
-#include <vtksys/SystemTools.hxx>
-
-#include <X11/X.h>
-#include <X11/Xatom.h>
-#include <X11/Xutil.h>
-#include <X11/keysym.h>
-
-#include <climits>
-#include <cmath>
-#include <map>
-#include <set>
-#include <sstream>
-
-vtkStandardNewMacro(vtkXRenderWindowInteractor2);
-
-template<int EventType>
-int
-XEventTypeEquals(Display*, XEvent* event, XPointer)
-{
-    return event->type == EventType;
-}
-
-struct vtkXRenderWindowInteractor2Timer
-{
-    unsigned long duration;
-    timeval lastFire;
-};
-
-// Map between the X native id to our own integer count id.  Note this
-// is separate from the TimerMap in the vtkRenderWindowInteractor
-// superclass.  This is used to avoid passing 64-bit values back
-// through the "int" return type of InternalCreateTimer.
-class vtkXRenderWindowInteractor2Internals
-{
-public:
-    vtkXRenderWindowInteractor2Internals() { this->TimerIdCount = 1; }
-    ~vtkXRenderWindowInteractor2Internals() = default;
-
-    // duration is in milliseconds
-    int CreateLocalTimer(unsigned long duration)
-    {
-        int id = this->TimerIdCount++;
-        this->LocalToTimer[id].duration = duration;
-        gettimeofday(&this->LocalToTimer[id].lastFire, nullptr);
-        return id;
-    }
-
-    void DestroyLocalTimer(int id) { this->LocalToTimer.erase(id); }
-
-    void GetTimeToNextTimer(timeval& tv)
-    {
-        uint64_t lowestDelta = 1000000;
-        if (!this->LocalToTimer.empty())
-        {
-            timeval ctv;
-            gettimeofday(&ctv, nullptr);
-            for (auto& timer : this->LocalToTimer)
-            {
-                uint64_t delta = (ctv.tv_sec - timer.second.lastFire.tv_sec) * 1000000 + ctv.tv_usec -
-                                 timer.second.lastFire.tv_usec;
-                if (delta < lowestDelta)
-                {
-                    lowestDelta = delta;
-                }
-            }
-        }
-        tv.tv_sec  = lowestDelta / 1000000;
-        tv.tv_usec = lowestDelta % 1000000;
-    }
-
-    void FireTimers(vtkXRenderWindowInteractor2* rwi)
-    {
-        if (!this->LocalToTimer.empty())
-        {
-            timeval ctv;
-            gettimeofday(&ctv, nullptr);
-            std::vector<unsigned long> expired;
-            for (auto& timer : this->LocalToTimer)
-            {
-                int64_t delta = (ctv.tv_sec - timer.second.lastFire.tv_sec) * 1000000 + ctv.tv_usec -
-                                timer.second.lastFire.tv_usec;
-                if (delta / 1000 >= static_cast<int64_t>(timer.second.duration))
-                {
-                    int timerId = rwi->GetVTKTimerId(timer.first);
-                    rwi->InvokeEvent(vtkCommand::TimerEvent, &timerId);
-                    if (rwi->IsOneShotTimer(timerId))
-                    {
-                        expired.push_back(timer.first);
-                    }
-                    else
-                    {
-                        timer.second.lastFire.tv_sec  = ctv.tv_sec;
-                        timer.second.lastFire.tv_usec = ctv.tv_usec;
-                    }
-                }
-            }
-            for (auto exp : expired)
-            {
-                this->DestroyLocalTimer(exp);
-            }
-        }
-    }
-
-    static std::set<vtkXRenderWindowInteractor2*> Instances;
-
-private:
-    int TimerIdCount;
-    std::map<int, vtkXRenderWindowInteractor2Timer> LocalToTimer;
-};
-
-std::set<vtkXRenderWindowInteractor2*> vtkXRenderWindowInteractor2Internals::Instances;
-
-// for some reason the X11 def of KeySym is getting messed up
-typedef XID vtkKeySym;
-
-//------------------------------------------------------------------------------
-vtkXRenderWindowInteractor2::vtkXRenderWindowInteractor2()
-{
-    this->Internal           = new vtkXRenderWindowInteractor2Internals;
-    this->DisplayId          = nullptr;
-    this->WindowId           = 0;
-    this->KillAtom           = 0;
-    this->XdndSource         = 0;
-    this->XdndPositionAtom   = 0;
-    this->XdndDropAtom       = 0;
-    this->XdndActionCopyAtom = 0;
-    this->XdndStatusAtom     = 0;
-    this->XdndFinishedAtom   = 0;
-}
-
-//------------------------------------------------------------------------------
-vtkXRenderWindowInteractor2::~vtkXRenderWindowInteractor2()
-{
-    this->Disable();
-
-    delete this->Internal;
-}
-
-//------------------------------------------------------------------------------
-// TerminateApp() notifies the event loop to exit.
-// The event loop is started by Start() or by one own's method.
-// This results in Start() returning to its caller.
-void
-vtkXRenderWindowInteractor2::TerminateApp()
-{
-    if (this->Done)
-    {
-        return;
-    }
-
-    this->Done = true;
-
-    // Send a VTK_BreakXtLoop ClientMessage event to be sure we pop out of the
-    // event loop.  This "wakes up" the event loop.  Otherwise, it might sit idle
-    // waiting for an event before realizing an exit was requested.
-    XClientMessageEvent client;
-    memset(&client, 0, sizeof(client));
-
-    client.type = ClientMessage;
-    // client.serial; //leave zeroed
-    // client.send_event; //leave zeroed
-    client.display      = this->DisplayId;
-    client.window       = this->WindowId;
-    client.message_type = XInternAtom(this->DisplayId, "VTK_BreakXtLoop", False);
-    client.format       = 32; // indicates size of data chunks: 8, 16 or 32 bits...
-    // client.data; //leave zeroed
-
-    XSendEvent(client.display, client.window, True, NoEventMask, reinterpret_cast<XEvent*>(&client));
-    XFlush(client.display);
-    this->RenderWindow->Finalize();
-}
-
-void
-vtkXRenderWindowInteractor2::ProcessEvents()
-{
-    XEvent event;
-    while (XPending(this->DisplayId) && !this->Done)
-    {
-        XNextEvent(this->DisplayId, &event);
-        this->DispatchEvent(&event);
-    }
-}
-
-//------------------------------------------------------------------------------
-// This will start up the X event loop. If you
-// call this method it will loop processing X events until the
-// loop is exited.
-void
-vtkXRenderWindowInteractor2::StartEventLoop()
-{
-    std::vector<int> rwiFileDescriptors;
-    fd_set           in_fds;
-    struct timeval   tv;
-    struct timeval   minTv;
-
-    for (auto rwi : vtkXRenderWindowInteractor2Internals::Instances)
-    {
-        rwi->Done = false;
-        rwiFileDescriptors.push_back(ConnectionNumber(rwi->DisplayId));
-    }
-
-    bool done = true;
-    do
-    {
-        bool wait = true;
-        done = true;
-        minTv.tv_sec  = 1000;
-        minTv.tv_usec = 1000;
-        XEvent event;
-        for (auto rwi = vtkXRenderWindowInteractor2Internals::Instances.begin();
-             rwi != vtkXRenderWindowInteractor2Internals::Instances.end();)
-        {
-            if (XPending((*rwi)->DisplayId) == 0)
-            {
-                // get how long to wait for the next timer
-                (*rwi)->Internal->GetTimeToNextTimer(tv);
-                minTv.tv_sec  = std::min(tv.tv_sec, minTv.tv_sec);
-                minTv.tv_usec = std::min(tv.tv_usec, minTv.tv_usec);
-            }
-            else
-            {
-                // If events are pending, dispatch them to the right RenderWindowInteractor
-                XNextEvent((*rwi)->DisplayId, &event);
-                (*rwi)->DispatchEvent(&event);
-                wait = false;
-            }
-            (*rwi)->FireTimers();
-
-            // Check if all RenderWindowInteractors have been terminated
-            done = done && (*rwi)->Done;
-
-            // If current RenderWindowInteractor have been terminated, handle its last event,
-            // then remove it from the Instance vector
-            if ((*rwi)->Done)
-            {
-                // Empty the event list
-                while (XPending((*rwi)->DisplayId) != 0)
-                {
-                    XNextEvent((*rwi)->DisplayId, &event);
-                    (*rwi)->DispatchEvent(&event);
-                }
-                // Adjust the file descriptors vector
-                int rwiPosition =
-                    std::distance(vtkXRenderWindowInteractor2Internals::Instances.begin(), rwi);
-                rwi = vtkXRenderWindowInteractor2Internals::Instances.erase(rwi);
-                rwiFileDescriptors.erase(rwiFileDescriptors.begin() + rwiPosition);
-            }
-            else
-            {
-                ++rwi;
-            }
-        }
-
-        if (wait && !done)
-        {
-            // select will wait until 'tv' elapses or something else wakes us
-            FD_ZERO(&in_fds);
-            for (auto rwiFileDescriptor : rwiFileDescriptors)
-            {
-                FD_SET(rwiFileDescriptor, &in_fds);
-            }
-            int maxFileDescriptor =
-                *std::max_element(rwiFileDescriptors.begin(), rwiFileDescriptors.end());
-            select(maxFileDescriptor + 1, &in_fds, nullptr, nullptr, &minTv);
-        }
-    }
-    while (!done);
-}
-
-//------------------------------------------------------------------------------
-// Initializes the event handlers without an XtAppContext.  This is
-// good for when you don't have a user interface, but you still
-// want to have mouse interaction.
-void
-vtkXRenderWindowInteractor2::Initialize()
-{
-    if (this->Initialized)
-    {
-        return;
-    }
-
-    vtkRenderWindow* ren;
-    int*             size;
-
-    // make sure we have a RenderWindow and camera
-    if (!this->RenderWindow)
-    {
-        vtkErrorMacro(<< "No renderer defined!");
-        return;
-    }
-
-    this->Initialized = 1;
-    ren = this->RenderWindow;
-
-    this->DisplayId = static_cast<Display*>(ren->GetGenericDisplayId());
-    if (!this->DisplayId)
-    {
-        vtkDebugMacro("opening display");
-        this->DisplayId = XOpenDisplay(nullptr);
-        vtkDebugMacro("opened display");
-        ren->SetDisplayId(this->DisplayId);
-    }
-
-    vtkXRenderWindowInteractor2Internals::Instances.insert(this);
-
-    size    = ren->GetActualSize();
-    size[0] = ((size[0] > 0) ? size[0] : 300);
-    size[1] = ((size[1] > 0) ? size[1] : 300);
-    XSync(this->DisplayId, False);
-
-    ren->Start();
-    ren->End();
-
-    this->WindowId = reinterpret_cast<Window>(ren->GetGenericWindowId());
-
-    XWindowAttributes attribs;
-    //  Find the current window size
-    XGetWindowAttributes(this->DisplayId, this->WindowId, &attribs);
-
-    size[0] = attribs.width;
-    size[1] = attribs.height;
-    ren->SetSize(size[0], size[1]);
-
-    this->Enable();
-    this->Size[0] = size[0];
-    this->Size[1] = size[1];
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::Enable()
-{
-    // avoid cycles of calling Initialize() and Enable()
-    if (this->Enabled)
-    {
-        return;
-    }
-
-    // Add the event handler to the system.
-    // If we change the types of events processed by this handler, then
-    // we need to change the Disable() routine to match.  In order for Disable()
-    // to work properly, both the callback function AND the client data
-    // passed to XtAddEventHandler and XtRemoveEventHandler must MATCH
-    // PERFECTLY
-    XSelectInput(this->DisplayId, this->WindowId,
-    KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask |
-      StructureNotifyMask | EnterWindowMask | LeaveWindowMask | PointerMotionHintMask |
-      PointerMotionMask);
-
-    // Setup for capturing the window deletion
-    this->KillAtom = XInternAtom(this->DisplayId, "WM_DELETE_WINDOW", False);
-    XSetWMProtocols(this->DisplayId, this->WindowId, &this->KillAtom, 1);
-
-    // Enable drag and drop
-    Atom xdndAwareAtom = XInternAtom(this->DisplayId, "XdndAware", False);
-    char xdndVersion   = 5;
-    XChangeProperty(this->DisplayId, this->WindowId, xdndAwareAtom, XA_ATOM, 32, PropModeReplace,
-        (unsigned char*)&xdndVersion, 1);
-    this->XdndPositionAtom   = XInternAtom(this->DisplayId, "XdndPosition", False);
-    this->XdndDropAtom       = XInternAtom(this->DisplayId, "XdndDrop", False);
-    this->XdndActionCopyAtom = XInternAtom(this->DisplayId, "XdndActionCopy", False);
-    this->XdndStatusAtom     = XInternAtom(this->DisplayId, "XdndStatus", False);
-    this->XdndFinishedAtom   = XInternAtom(this->DisplayId, "XdndFinished", False);
-
-    this->Enabled = 1;
-
-    this->Modified();
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::Disable()
-{
-    if (!this->Enabled)
-    {
-        return;
-    }
-
-    this->Enabled = 0;
-
-    this->Modified();
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::PrintSelf(ostream& os, vtkIndent indent)
-{
-    this->Superclass::PrintSelf(os, indent);
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::UpdateSize(int x, int y)
-{
-    // if the size changed send this on to the RenderWindow
-    if ((x != this->Size[0]) || (y != this->Size[1]))
-    {
-        this->Size[0] = x;
-        this->Size[1] = y;
-        this->RenderWindow->SetSize(x, y);
-    }
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::UpdateSizeNoXResize(int x, int y)
-{
-    // if the size changed send this on to the RenderWindow
-    if ((x != this->Size[0]) || (y != this->Size[1]))
-    {
-        this->Size[0] = x;
-        this->Size[1] = y;
-        // static_cast<vtkXOpenGLRenderWindow*>(this->RenderWindow)->SetSizeNoXResize(x, y);
-        this->RenderWindow->SetSize(x, y);
-    }
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::FireTimers()
-{
-    if (this->GetEnabled())
-    {
-        this->Internal->FireTimers(this);
-    }
-}
-
-//------------------------------------------------------------------------------
-// X always creates one shot timers
-int
-vtkXRenderWindowInteractor2::InternalCreateTimer(
-    int vtkNotUsed(timerId), int vtkNotUsed(timerType), unsigned long duration)
-{
-    duration = (duration > 0 ? duration : this->TimerDuration);
-    int platformTimerId = this->Internal->CreateLocalTimer(duration);
-    return platformTimerId;
-}
-
-//------------------------------------------------------------------------------
-int
-vtkXRenderWindowInteractor2::InternalDestroyTimer(int platformTimerId)
-{
-    this->Internal->DestroyLocalTimer(platformTimerId);
-    return 1;
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::DispatchEvent(XEvent* event)
-{
-    int xp, yp;
-
-    switch (event->type)
-    {
-    case Expose:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        XEvent result;
-        while (XCheckTypedWindowEvent(this->DisplayId, this->WindowId, Expose, &result))
-        {
-            // just getting the expose configure event
-            event = &result;
-        }
-        XExposeEvent* exposeEvent = reinterpret_cast<XExposeEvent*>(event);
-        this->SetEventSize(exposeEvent->width, exposeEvent->height);
-        xp = exposeEvent->x;
-        yp = exposeEvent->y;
-        yp = this->Size[1] - yp - 1;
-        this->SetEventPosition(xp, yp);
-
-        // only render if we are currently accepting events
-        if (this->Enabled)
-        {
-            this->InvokeEvent(vtkCommand::ExposeEvent, nullptr);
-            this->Render();
-        }
-    }
-    break;
-
-    case MapNotify:
-    {
-        // only render if we are currently accepting events
-        if (this->Enabled && this->GetRenderWindow()->GetNeverRendered())
-        {
-            this->Render();
-        }
-    }
-    break;
-
-    case ConfigureNotify:
-    {
-        XEvent result;
-        while (XCheckTypedWindowEvent(this->DisplayId, this->WindowId, ConfigureNotify, &result))
-        {
-            // just getting the last configure event
-            event = &result;
-        }
-        int width  = (reinterpret_cast<XConfigureEvent*>(event))->width;
-        int height = (reinterpret_cast<XConfigureEvent*>(event))->height;
-        if (width != this->Size[0] || height != this->Size[1])
-        {
-            bool resizeSmaller = width <= this->Size[0] && height <= this->Size[1];
-            this->UpdateSizeNoXResize(width, height);
-            xp = (reinterpret_cast<XButtonEvent*>(event))->x;
-            yp = (reinterpret_cast<XButtonEvent*>(event))->y;
-            this->SetEventPosition(xp, this->Size[1] - yp - 1);
-            // only render if we are currently accepting events
-            if (this->Enabled)
-            {
-                this->InvokeEvent(vtkCommand::ConfigureEvent, nullptr);
-                if (resizeSmaller)
-                {
-                    // Don't call Render when the window is resized to be larger:
-                    //
-                    // - if the window is resized to be larger, an Expose event will
-                    // be triggered by the X server which will trigger a call to
-                    // Render().
-                    // - if the window is resized to be smaller, no Expose event will
-                    // be triggered by the X server, as no new area become visible.
-                    // only in this case, we need to explicitly call Render()
-                    // in ConfigureNotify.
-                    this->Render();
-                }
-            }
-        }
-    }
-    break;
-
-    case ButtonPress:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        int ctrl  = ((reinterpret_cast<XButtonEvent*>(event))->state & ControlMask) ? 1 : 0;
-        int shift = ((reinterpret_cast<XButtonEvent*>(event))->state & ShiftMask) ? 1 : 0;
-        int alt   = ((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0;
-        xp = (reinterpret_cast<XButtonEvent*>(event))->x;
-        yp = (reinterpret_cast<XButtonEvent*>(event))->y;
-
-        // check for double click
-        static int MousePressTime = 0;
-        int        repeat = 0;
-        // 400 ms threshold by default is probably good to start
-        int eventTime = static_cast<int>(reinterpret_cast<XButtonEvent*>(event)->time);
-        if ((eventTime - MousePressTime) < 400)
-        {
-            MousePressTime -= 2000; // no double click next time
-            repeat = 1;
-        }
-        else
-        {
-            MousePressTime = eventTime;
-        }
-
-        this->SetEventInformationFlipY(xp, yp, ctrl, shift, 0, repeat);
-        this->SetAltKey(alt);
-        switch ((reinterpret_cast<XButtonEvent*>(event))->button)
-        {
-        case Button1:
-            this->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
-            break;
-        case Button2:
-            this->InvokeEvent(vtkCommand::MiddleButtonPressEvent, nullptr);
-            break;
-        case Button3:
-            this->InvokeEvent(vtkCommand::RightButtonPressEvent, nullptr);
-            break;
-        case Button4:
-            this->InvokeEvent(vtkCommand::MouseWheelForwardEvent, nullptr);
-            break;
-        case Button5:
-            this->InvokeEvent(vtkCommand::MouseWheelBackwardEvent, nullptr);
-            break;
-        }
-    }
-    break;
-
-    case ButtonRelease:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        int ctrl  = ((reinterpret_cast<XButtonEvent*>(event))->state & ControlMask) ? 1 : 0;
-        int shift = ((reinterpret_cast<XButtonEvent*>(event))->state & ShiftMask) ? 1 : 0;
-        int alt   = ((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0;
-        xp = (reinterpret_cast<XButtonEvent*>(event))->x;
-        yp = (reinterpret_cast<XButtonEvent*>(event))->y;
-        this->SetEventInformationFlipY(xp, yp, ctrl, shift);
-        this->SetAltKey(alt);
-        switch ((reinterpret_cast<XButtonEvent*>(event))->button)
-        {
-        case Button1:
-            this->InvokeEvent(vtkCommand::LeftButtonReleaseEvent, nullptr);
-            break;
-        case Button2:
-            this->InvokeEvent(vtkCommand::MiddleButtonReleaseEvent, nullptr);
-            break;
-        case Button3:
-            this->InvokeEvent(vtkCommand::RightButtonReleaseEvent, nullptr);
-            break;
-        }
-    }
-    break;
-
-    case EnterNotify:
-    {
-        // Force the keyboard focus to be this render window
-        XSetInputFocus(this->DisplayId, this->WindowId, RevertToPointerRoot, CurrentTime);
-        if (this->Enabled)
-        {
-            XEnterWindowEvent* e = reinterpret_cast<XEnterWindowEvent*>(event);
-            this->SetEventInformationFlipY(
-          e->x, e->y, (e->state & ControlMask) != 0, (e->state & ShiftMask) != 0);
-            this->SetAltKey(((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0);
-            this->InvokeEvent(vtkCommand::EnterEvent, nullptr);
-        }
-    }
-    break;
-
-    case LeaveNotify:
-    {
-        if (this->Enabled)
-        {
-            XLeaveWindowEvent* e = reinterpret_cast<XLeaveWindowEvent*>(event);
-            this->SetEventInformationFlipY(
-          e->x, e->y, (e->state & ControlMask) != 0, (e->state & ShiftMask) != 0);
-            this->SetAltKey(((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0);
-            this->InvokeEvent(vtkCommand::LeaveEvent, nullptr);
-        }
-    }
-    break;
-
-    case KeyPress:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        int         ctrl  = ((reinterpret_cast<XButtonEvent*>(event))->state & ControlMask) ? 1 : 0;
-        int         shift = ((reinterpret_cast<XButtonEvent*>(event))->state & ShiftMask) ? 1 : 0;
-        int         alt   = ((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0;
-        vtkKeySym   ks;
-        static char buffer[20];
-        buffer[0] = '\0';
-        XLookupString(reinterpret_cast<XKeyEvent*>(event), buffer, 20, &ks, nullptr);
-        xp = (reinterpret_cast<XKeyEvent*>(event))->x;
-        yp = (reinterpret_cast<XKeyEvent*>(event))->y;
-        this->SetEventInformationFlipY(xp, yp, ctrl, shift, buffer[0], 1, XKeysymToString(ks));
-        this->SetAltKey(alt);
-        this->InvokeEvent(vtkCommand::KeyPressEvent, nullptr);
-        this->InvokeEvent(vtkCommand::CharEvent, nullptr);
-    }
-    break;
-
-    case KeyRelease:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        int         ctrl  = ((reinterpret_cast<XButtonEvent*>(event))->state & ControlMask) ? 1 : 0;
-        int         shift = ((reinterpret_cast<XButtonEvent*>(event))->state & ShiftMask) ? 1 : 0;
-        int         alt   = ((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0;
-        vtkKeySym   ks;
-        static char buffer[20];
-        buffer[0] = '\0';
-        XLookupString(reinterpret_cast<XKeyEvent*>(event), buffer, 20, &ks, nullptr);
-        xp = (reinterpret_cast<XKeyEvent*>(event))->x;
-        yp = (reinterpret_cast<XKeyEvent*>(event))->y;
-        this->SetEventInformationFlipY(xp, yp, ctrl, shift, buffer[0], 1, XKeysymToString(ks));
-        this->SetAltKey(alt);
-        this->InvokeEvent(vtkCommand::KeyReleaseEvent, nullptr);
-    }
-    break;
-
-    case MotionNotify:
-    {
-        if (!this->Enabled)
-        {
-            return;
-        }
-        int ctrl  = ((reinterpret_cast<XButtonEvent*>(event))->state & ControlMask) ? 1 : 0;
-        int shift = ((reinterpret_cast<XButtonEvent*>(event))->state & ShiftMask) ? 1 : 0;
-        int alt   = ((reinterpret_cast<XButtonEvent*>(event))->state & Mod1Mask) ? 1 : 0;
-
-        // Note that even though the (x,y) location of the pointer is event structure,
-        // we must call XQueryPointer for the hints (motion event compression) to
-        // work properly.
-        this->GetMousePosition(&xp, &yp);
-        this->SetEventInformation(xp, yp, ctrl, shift);
-        this->SetAltKey(alt);
-        this->InvokeEvent(vtkCommand::MouseMoveEvent, nullptr);
-    }
-    break;
-
-    // Selection request for drag and drop has been delivered
-    case SelectionNotify:
-    {
-        // Sanity checks
-        if (!event->xselection.property || !this->XdndSource)
-        {
-            return;
-        }
-
-        // Recover the dropped file
-        char*         data = nullptr;
-        Atom          actualType;
-        int           actualFormat;
-        unsigned long itemCount, bytesAfter;
-        XGetWindowProperty(this->DisplayId, event->xselection.requestor, event->xselection.property,
-        0, LONG_MAX, False, event->xselection.target, &actualType, &actualFormat, &itemCount,
-        &bytesAfter, (unsigned char**)&data);
-
-        // Conversion checks
-        if ((event->xselection.target != AnyPropertyType && actualType != event->xselection.target)
-            || itemCount == 0)
-        {
-            return;
-        }
-
-        // Recover filepaths from uris and invoke DropFilesEvent
-        std::stringstream      uris(data);
-        std::string            uri, protocol, hostname, filePath;
-        std::string            unused0, unused1, unused2, unused3;
-        vtkNew<vtkStringArray> filePaths;
-        while (std::getline(uris, uri, '\n'))
-        {
-            if (vtksys::SystemTools::ParseURL(
-              uri, protocol, unused0, unused1, hostname, unused3, filePath, true))
-            {
-                if (protocol == "file" && (hostname.empty() || hostname == "localhost"))
-                {
-                    // The uris can be crlf delimited, remove ending \r if any
-                    if (filePath.back() == '\r')
-                    {
-                        filePath.pop_back();
-                    }
-
-                    // The extracted filepath miss the first slash
-                    filePath.insert(0, "/");
-
-                    filePaths->InsertNextValue(filePath);
-                }
-            }
-        }
-        this->InvokeEvent(vtkCommand::DropFilesEvent, filePaths);
-        XFree(data);
-
-        // Inform the source the the drag and drop operation was sucessfull
-        XEvent reply;
-        memset(&reply, 0, sizeof(reply));
-
-        reply.type = ClientMessage;
-        reply.xclient.window       = event->xclient.data.l[0];
-        reply.xclient.message_type = this->XdndFinishedAtom;
-        reply.xclient.format       = 32;
-        reply.xclient.data.l[0]    = this->WindowId;
-        reply.xclient.data.l[1]    = itemCount;
-        reply.xclient.data.l[2]    = this->XdndActionCopyAtom;
-
-        XSendEvent(this->DisplayId, this->XdndSource, False, NoEventMask, &reply);
-        XFlush(this->DisplayId);
-        this->XdndSource = 0;
-    }
-    break;
-
-    case ClientMessage:
-    {
-        if (event->xclient.message_type == this->XdndPositionAtom)
-        {
-            // Drag and drop event inside the window
-
-            // Recover the position
-            int    xWindow, yWindow;
-            int    xRoot = event->xclient.data.l[2] >> 16;
-            int    yRoot = event->xclient.data.l[2] & 0xffff;
-            Window root  = DefaultRootWindow(this->DisplayId);
-            Window child;
-            XTranslateCoordinates(
-          this->DisplayId, root, this->WindowId, xRoot, yRoot, &xWindow, &yWindow, &child);
-
-            // Convert it to VTK compatible location
-            double location[2];
-            location[0] = static_cast<double>(xWindow);
-            location[1] = static_cast<double>(this->Size[1] - yWindow - 1);
-            this->InvokeEvent(vtkCommand::UpdateDropLocationEvent, location);
-
-            // Reply that we are ready to copy the dragged data
-            XEvent reply;
-            memset(&reply, 0, sizeof(reply));
-
-            reply.type = ClientMessage;
-            reply.xclient.window       = event->xclient.data.l[0];
-            reply.xclient.message_type = this->XdndStatusAtom;
-            reply.xclient.format       = 32;
-            reply.xclient.data.l[0]    = this->WindowId;
-            reply.xclient.data.l[1]    = 1; // Always accept the dnd with no rectangle
-            reply.xclient.data.l[2]    = 0; // Specify an empty rectangle
-            reply.xclient.data.l[3]    = 0;
-            reply.xclient.data.l[4]    = this->XdndActionCopyAtom;
-
-            XSendEvent(this->DisplayId, event->xclient.data.l[0], False, NoEventMask, &reply);
-            XFlush(this->DisplayId);
-        }
-        else if (event->xclient.message_type == this->XdndDropAtom)
-        {
-            // Item dropped in the window
-            // Store the source of the drag and drop
-            this->XdndSource = event->xclient.data.l[0];
-
-            // Ask for a conversion of the selection. This will trigger a SelectioNotify event later.
-            Atom xdndSelectionAtom = XInternAtom(this->DisplayId, "XdndSelection", False);
-            XConvertSelection(this->DisplayId, xdndSelectionAtom,
-          XInternAtom(this->DisplayId, "UTF8_STRING", False), xdndSelectionAtom, this->WindowId,
-          CurrentTime);
-        }
-        else if (static_cast<Atom>(event->xclient.data.l[0]) == this->KillAtom)
-        {
-            this->ExitCallback();
-        }
-    }
-    break;
-    }
-}
-
-//------------------------------------------------------------------------------
-void
-vtkXRenderWindowInteractor2::GetMousePosition(int* x, int* y)
-{
-    Window       root, child;
-    int          root_x, root_y;
-    unsigned int keys;
-
-    XQueryPointer(this->DisplayId, this->WindowId, &root, &child, &root_x, &root_y, x, y, &keys);
-
-    *y = this->Size[1] - *y - 1;
-}
-
-//------------------------------------------------------------------------------
-// void vtkXRenderWindowInteractor2::Timer(XtPointer client_data, XtIntervalId* id)
-// {
-//   vtkXRenderWindowInteractor2Timer(client_data, id);
-// }
-
-//------------------------------------------------------------------------------
-// void vtkXRenderWindowInteractor2::Callback(
-//   Widget w, XtPointer client_data, XEvent* event, Boolean* ctd)
-// {
-//   vtkXRenderWindowInteractor2Callback(w, client_data, event, ctd);
-// }
diff --git a/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.h b/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.h
deleted file mode 100644
index 026ea6ca1800c40bf10c02da1eadb95ed441d0b2..0000000000000000000000000000000000000000
--- a/Source/SimulationManager/VTKRenderer/imstkVtkXRenderWindowInteractor2.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*=========================================================================
-
-   Library: iMSTK
-
-   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
-   & Imaging in Medicine, Rensselaer Polytechnic Institute.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0.txt
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-=========================================================================*/
-
-#pragma once
-
-//===========================================================
-// now we define the C++ class
-
-#include "vtkRenderWindowInteractor.h"
-#include <X11/Xlib.h>             // Needed for X types in the public interface
-
-class vtkCallbackCommand;
-class vtkXRenderWindowInteractor2Internals;
-
-///
-/// \class vtkXRenderWindowInteractor2
-///
-/// \brief This class exists to fix bugs in VTK 9.0, which are, as typing this
-/// already merged into VTK master, this class should be deleted when upgrading
-/// to yet to be released VTK 10
-///
-class vtkXRenderWindowInteractor2 : public vtkRenderWindowInteractor
-{
-public:
-    static vtkXRenderWindowInteractor2* New();
-    vtkTypeMacro(vtkXRenderWindowInteractor2, vtkRenderWindowInteractor);
-    void PrintSelf(ostream& os, vtkIndent indent) override;
-
-    /**
-     * Initializes the event handlers without an XtAppContext.  This is
-     * good for when you don't have a user interface, but you still
-     * want to have mouse interaction.
-     */
-    void Initialize() override;
-
-    /**
-     * Break the event loop on 'q','e' keypress. Want more ???
-     */
-    void TerminateApp() override;
-
-    /**
-     * Run the event loop and return. This is provided so that you can
-     * implement your own event loop but yet use the vtk event handling as
-     * well.
-     */
-    void ProcessEvents() override;
-
-    //@{
-    /**
-     * Enable/Disable interactions.  By default interactors are enabled when
-     * initialized.  Initialize() must be called prior to enabling/disabling
-     * interaction. These methods are used when a window/widget is being
-     * shared by multiple renderers and interactors.  This allows a "modal"
-     * display where one interactor is active when its data is to be displayed
-     * and all other interactors associated with the widget are disabled
-     * when their data is not displayed.
-     */
-    void Enable() override;
-    void Disable() override;
-    //@}
-
-    /**
-     * Update the Size data member and set the associated RenderWindow's
-     * size.
-     */
-    void UpdateSize(int, int) override;
-
-    /**
-     * Re-defines virtual function to get mouse position by querying X-server.
-     */
-    void GetMousePosition(int* x, int* y) override;
-
-    void DispatchEvent(XEvent*);
-
-protected:
-    vtkXRenderWindowInteractor2();
-    ~vtkXRenderWindowInteractor2() override;
-
-    /**
-     * Update the Size data member and set the associated RenderWindow's
-     * size but do not resize the XWindow.
-     */
-    void UpdateSizeNoXResize(int, int);
-
-    // Using static here to avoid destroying context when many apps are open:
-    static int NumAppInitialized;
-
-    Display* DisplayId;
-    Window   WindowId;
-    Atom     KillAtom;
-    int      PositionBeforeStereo[2];
-    vtkXRenderWindowInteractor2Internals* Internal;
-
-    // Drag and drop related
-    Window XdndSource;
-    Atom   XdndPositionAtom;
-    Atom   XdndDropAtom;
-    Atom   XdndActionCopyAtom;
-    Atom   XdndStatusAtom;
-    Atom   XdndFinishedAtom;
-
-    //@{
-    /**
-     * X-specific internal timer methods. See the superclass for detailed
-     * documentation.
-     */
-    int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
-    int InternalDestroyTimer(int platformTimerId) override;
-    //@}
-
-    void FireTimers();
-
-    /**
-     * This will start up the X event loop and never return. If you
-     * call this method it will loop processing X events until the
-     * application is exited.
-     */
-    void StartEventLoop() override;
-
-private:
-    vtkXRenderWindowInteractor2(const vtkXRenderWindowInteractor2&) = delete;
-    void operator=(const vtkXRenderWindowInteractor2&) = delete;
-};
\ No newline at end of file
diff --git a/Source/SimulationManager/VTKRenderer/vtk_openvr_actions.json b/Source/SimulationManager/VTKRenderer/vtk_openvr_actions.json
new file mode 100644
index 0000000000000000000000000000000000000000..73761aa18108e062e77540b68f75536775fda971
--- /dev/null
+++ b/Source/SimulationManager/VTKRenderer/vtk_openvr_actions.json
@@ -0,0 +1,67 @@
+{
+  "actions": [
+    {
+      "name": "/actions/vtk/in/TriggerPressed",
+      "type": "boolean"
+    },
+    {
+      "name": "/actions/vtk/in/RightGripMovement",
+      "type": "vector2"
+    },
+    {
+      "name": "/actions/vtk/in/LeftGripMovement",
+      "type": "vector2"
+    },
+    {
+      "name": "/actions/vtk/in/GripPressed",
+      "type": "boolean"
+    },
+    {
+      "name": "/actions/vtk/in/Button0Pressed",
+      "type": "boolean"
+    },
+    {
+      "name": "/actions/vtk/in/Button1Pressed",
+      "type": "boolean"
+    },
+    {
+      "name": "/actions/vtk/in/Button2Pressed",
+      "type": "boolean"
+    },
+    {
+      "name": "/actions/vtk/in/Button3Pressed",
+      "type": "boolean"
+    }
+  ],
+  "default_bindings": [
+    {
+      "binding_url": "vtk_openvr_binding_vive_controller.json",
+      "controller_type": "vive_controller"
+    },
+    {
+      "binding_url": "vtk_openvr_binding_oculus_touch.json",
+      "controller_type": "generic"
+    },
+    {
+      "binding_url": "vtk_openvr_binding_oculus_touch.json",
+      "controller_type": "oculus_touch"
+    },
+    {
+      "binding_url": "vtk_openvr_binding_hpmotioncontroller.json",
+      "controller_type": "hpmotioncontroller"
+    }
+  ],
+  "localization": [
+    {
+      "/actions/vtk/in/GripPressed": "Grip Pressed",
+      "/actions/vtk/in/RightGripMovement": "Right Grip Movement",
+      "/actions/vtk/in/LeftGripMovement": "Left Grip Movement",
+      "/actions/vtk/in/TriggerPressed": "Trigger Pressed",
+      "/actions/vtk/in/Button0Pressed": "Button0 Pressed",
+      "/actions/vtk/in/Button1Pressed": "Button1 Pressed",
+      "/actions/vtk/in/Button2Pressed": "Button2 Pressed",
+      "/actions/vtk/in/Button3Pressed": "Button3 Pressed",
+      "language_tag": "en_US"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Source/SimulationManager/VTKRenderer/vtk_openvr_binding_oculus_touch.json b/Source/SimulationManager/VTKRenderer/vtk_openvr_binding_oculus_touch.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c71f517b9a28c375ed3e516be366c74a4e8bf8c
--- /dev/null
+++ b/Source/SimulationManager/VTKRenderer/vtk_openvr_binding_oculus_touch.json
@@ -0,0 +1,115 @@
+{
+  "action_manifest_version": 0,
+  "alias_info": {},
+  "app_key": "system.generated.paraview.exe",
+  "bindings": {
+    "/actions/vtk": {
+      "sources": [
+        {
+          "inputs": {
+            "position": {
+              "output": "/actions/vtk/in/RightGripMovement"
+            }
+          },
+          "mode": "joystick",
+          "path": "/user/hand/right/input/joystick"
+        },
+        {
+          "inputs": {
+            "position": {
+              "output": "/actions/vtk/in/LeftGripMovement"
+            }
+          },
+          "mode": "joystick",
+          "path": "/user/hand/left/input/joystick"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/GripPressed"
+            }
+          },
+          "mode": "button",
+          "parameters": {
+            "click_activate_threshold": "0.8",
+            "click_deactivate_threshold": "0.7"
+          },
+          "path": "/user/hand/left/input/grip"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/GripPressed"
+            }
+          },
+          "mode": "button",
+          "parameters": {
+            "click_activate_threshold": "0.8",
+            "click_deactivate_threshold": "0.7"
+          },
+          "path": "/user/hand/right/input/grip"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/TriggerPressed"
+            }
+          },
+          "mode": "trigger",
+          "path": "/user/hand/left/input/trigger"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/TriggerPressed"
+            }
+          },
+          "mode": "trigger",
+          "path": "/user/hand/right/input/trigger"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/Button1Pressed"
+            }
+          },
+          "mode": "button",
+          "path": "/user/hand/right/input/b"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/Button0Pressed"
+            }
+          },
+          "mode": "button",
+          "path": "/user/hand/right/input/a"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/Button2Pressed"
+            }
+          },
+          "mode": "button",
+          "path": "/user/hand/left/input/x"
+        },
+        {
+          "inputs": {
+            "click": {
+              "output": "/actions/vtk/in/Button3Pressed"
+            }
+          },
+          "mode": "button",
+          "path": "/user/hand/left/input/y"
+        }
+      ]
+    }
+  },
+  "category": "steamvr_input",
+  "controller_type": "oculus_touch",
+  "description": "",
+  "name": "vtk-oculus",
+  "options": {},
+  "simulated_actions": []
+}
\ No newline at end of file
diff --git a/Source/SimulationManager/imstkSimulationManager.cpp b/Source/SimulationManager/imstkSimulationManager.cpp
index 21ca2aef815e651929a8858429d6d8cde7d66d0a..8459272e0a288e9b4af4ef251aaf7e809a1b1a78 100644
--- a/Source/SimulationManager/imstkSimulationManager.cpp
+++ b/Source/SimulationManager/imstkSimulationManager.cpp
@@ -20,33 +20,15 @@
 =========================================================================*/
 
 #include "imstkSimulationManager.h"
-#include "imstkViewer.h"
+#include "imstkMacros.h"
 #include "imstkTimer.h"
+#include "imstkViewer.h"
 
 #include <thread>
-#include <tbb/task.h>
-
-class FuncTask : public tbb::task
-{
-public:
-    FuncTask(std::shared_ptr<imstk::Module> module, std::function<void(std::shared_ptr<imstk::Module>)> func) :
-        m_func(func), m_module(module)
-    {
-    }
-
-    task* execute() override
-    {
-        __TBB_ASSERT(ref_count() == 0, NULL);
-
-        m_func(m_module);
-
-        return NULL;
-    }
-
-protected:
-    std::function<void(std::shared_ptr<imstk::Module>)> m_func;
-    std::shared_ptr<imstk::Module> m_module;
-};
+DISABLE_WARNING_PUSH
+    DISABLE_WARNING_PADDING
+#include <tbb/task_group.h>
+DISABLE_WARNING_POP
 
 namespace imstk
 {
@@ -76,18 +58,16 @@ SimulationManager::start()
     }
 
     // Start parallel modules
-    tbb::task_list           taskList;
+    tbb::task_group          tasks;
     std::vector<std::thread> threads(m_asyncModules.size());
     {
         if (m_threadType == ThreadingType::TBB)
         {
             for (auto module : m_asyncModules)
             {
-                FuncTask* moduleTask = new(tbb::task::allocate_root())FuncTask(module,
-                                                                               std::bind(&SimulationManager::runModuleParallel, this, std::placeholders::_1));
-                taskList.push_back(*moduleTask);
+                tasks.run([this, module]() { runModuleParallel(module); });
             }
-            tbb::task::spawn_root_and_wait(taskList);
+            tasks.wait();
         }
         else if (m_threadType == ThreadingType::STL)
         {
@@ -190,6 +170,7 @@ SimulationManager::start()
 
                 for (auto viewer : m_viewers)
                 {
+                    viewer->setDt(m_numSteps * m_dt);
                     viewer->update();
                 }
             }