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(); } }