Commit 22fb2144 authored by Alexis Girault's avatar Alexis Girault
Browse files

ENH: Improve Viewer Simulation control

(1) `SimulationManager::startSimulation()` will check if a Viewer is
already rendering, and will only start a rendering/interaction loop
in the main thread if it isn't. This allows us to work with two
possible cases:
(a) a QT Application with an existing widget for rendering, in which
case we do not want to control the rendering window ourself in a loop
(b) a standalone `vtkRenderWindow` application, in which case we do
want to create a window when our simulation starts and loop until the
window is closed.

(2) `SimulationManager::startSimulation()` can be launched with a debug
boolean option (off by default), which will allow to not automatically
simulate the dynamics of the scene, to be able to interact with the
scene beforehand (using the mouse).

(3) `imstk::InteractorStyle` is based on vtkInteractorStyle specifically
the trackballCamera and allows to ignore mouse events in simulation mode,
and uses key events to control the simulation:
's' : start simulation
'e' : end simulation
' ' (space) : pause/run simulation

(4) The `Viewer` class and the `InteractorStyle` class have been moved
to the `SimulationManager` module, since they are more drivers of the
application themselves.

(5) The 'Sandbox' example has been refactored to make it easier to test
autonomous tests, and updated to use the new driver architecture brought
with this commit.
parent 97e252c0
......@@ -31,7 +31,6 @@
#include "g3log/g3log.hpp"
namespace imstk {
Renderer::Renderer(std::shared_ptr<Scene> scene)
{
......@@ -55,21 +54,24 @@ Renderer::Renderer(std::shared_ptr<Scene> scene)
}
}
// Global Axis
auto axes = vtkSmartPointer<vtkAxesActor>::New();
m_debugVtkActors.push_back( axes );
// Camera and camera actor
m_sceneVtkCamera = scene->getCamera()->getVtkCamera();
auto camActor = vtkSmartPointer<vtkCameraActor>::New();
camActor->SetCamera( m_sceneVtkCamera );
m_debugVtkActors.push_back( camActor );
// Global Axis
auto axes = vtkSmartPointer<vtkAxesActor>::New();
m_debugVtkActors.push_back( axes );
///TODO : based on scene properties
// Customize background colors
m_vtkRenderer->SetBackground(0.66,0.66,0.66);
m_vtkRenderer->SetBackground2(157.0/255.0*0.66,186/255.0*0.66,192.0/255.0*0.66);
m_vtkRenderer->GradientBackgroundOn();
this->setup(Mode::SIMULATION);
}
vtkSmartPointer<vtkRenderer>
......@@ -83,8 +85,6 @@ Renderer::setup(Mode mode)
{
if( mode == Mode::EMPTY && m_currentMode != Mode::EMPTY )
{
m_vtkRenderer->SetActiveCamera(m_defaultVtkCamera);
this->removeActors(m_objectVtkActors);
m_vtkRenderer->RemoveAllLights();
......@@ -92,11 +92,11 @@ Renderer::setup(Mode mode)
{
this->removeActors(m_debugVtkActors);
}
m_vtkRenderer->SetActiveCamera(m_defaultVtkCamera);
}
else if( mode == Mode::DEBUG && m_currentMode != Mode::DEBUG )
{
m_vtkRenderer->SetActiveCamera(m_defaultVtkCamera);
this->addActors(m_debugVtkActors);
if( m_currentMode == Mode::EMPTY )
......@@ -107,11 +107,12 @@ Renderer::setup(Mode mode)
m_vtkRenderer->AddLight(light);
}
}
m_vtkRenderer->SetActiveCamera(m_defaultVtkCamera);
m_vtkRenderer->ResetCamera();
}
else if ( mode == Mode::SIMULATION && m_currentMode != Mode::SIMULATION )
{
m_vtkRenderer->SetActiveCamera(m_sceneVtkCamera);
if( m_currentMode == Mode::EMPTY )
{
this->addActors(m_objectVtkActors);
......@@ -124,6 +125,9 @@ Renderer::setup(Mode mode)
{
this->removeActors(m_debugVtkActors);
}
m_vtkRenderer->SetActiveCamera(m_sceneVtkCamera);
m_vtkRenderer->ResetCameraClippingRange();
}
m_currentMode = mode;
......
......@@ -57,7 +57,7 @@ protected:
void addActors(const std::vector<vtkSmartPointer<vtkProp>>& actorList);
vtkSmartPointer<vtkRenderer> m_vtkRenderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkCamera> m_defaultVtkCamera = vtkSmartPointer<vtkCamera>::New();
vtkSmartPointer<vtkCamera> m_defaultVtkCamera = m_vtkRenderer->GetActiveCamera();
vtkSmartPointer<vtkCamera> m_sceneVtkCamera;
std::vector<vtkSmartPointer<vtkLight>> m_vtkLights;
std::vector<vtkSmartPointer<vtkProp>> m_objectVtkActors;
......
......@@ -174,6 +174,5 @@ void
Scene::runModule()
{
LOG(DEBUG) << m_name << " : running";
LOG(INFO) << m_camera->getPosition();
}
}
/*=========================================================================
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 "imstkInteractorStyle.h"
#include "imstkSimulationManager.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkAssemblyPath.h"
#include "vtkAbstractPropPicker.h"
vtkStandardNewMacro(imstk::InteractorStyle);
namespace imstk {
void
InteractorStyle::OnChar()
{
vtkRenderWindowInteractor *rwi = this->Interactor;
switch (rwi->GetKeyCode())
{
// Highlight picked actor
case 'p' :
case 'P' :
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
if(this->CurrentRenderer != 0)
{
if (this->State == VTKIS_NONE)
{
vtkAssemblyPath *path = nullptr;
int *eventPos = rwi->GetEventPosition();
this->FindPokedRenderer(eventPos[0], eventPos[1]);
rwi->StartPickCallback();
auto picker = vtkAbstractPropPicker::SafeDownCast(rwi->GetPicker());
if ( picker != nullptr )
{
picker->Pick(eventPos[0], eventPos[1], 0.0, this->CurrentRenderer);
path = picker->GetPath();
}
if ( path == nullptr )
{
this->HighlightProp(nullptr);
this->PropPicked = 0;
}
else
{
this->HighlightProp(path->GetFirstNode()->GetViewProp());
this->PropPicked = 1;
}
rwi->EndPickCallback();
}
}
else
{
vtkWarningMacro(<<"no current renderer on the interactor style.");
}
}
break;
// Fly To picked actor
case 'f' :
case 'F' :
{
if(this->CurrentRenderer != 0)
{
this->AnimState = VTKIS_ANIM_ON;
vtkAssemblyPath *path = nullptr;
int *eventPos = rwi->GetEventPosition();
this->FindPokedRenderer(eventPos[0], eventPos[1]);
rwi->StartPickCallback();
auto picker = vtkAbstractPropPicker::SafeDownCast(rwi->GetPicker());
if ( picker != nullptr )
{
picker->Pick(eventPos[0], eventPos[1], 0.0, this->CurrentRenderer);
path = picker->GetPath();
}
if (path != nullptr)
{
rwi->FlyTo(this->CurrentRenderer, picker->GetPickPosition());
}
this->AnimState = VTKIS_ANIM_OFF;
}
else
{
vtkWarningMacro(<<"no current renderer on the interactor style.");
}
}
break;
// Reset Camera
case 'r' :
case 'R' :
{
if(this->CurrentRenderer!=0)
{
this->CurrentRenderer->ResetCamera();
this->CurrentRenderer->GetActiveCamera()->SetFocalPoint(0,0,0);
}
else
{
vtkWarningMacro(<<"no current renderer on the interactor style.");
}
rwi->Render();
}
break;
// Stop Simulation
case 's' :
case 'S' :
{
m_simManager->startSimulation();
}
break;
// End Simulation
case 'q' :
case 'Q' :
case 'e' :
case 'E' :
{
m_simManager->endSimulation();
}
break;
// Play/Pause Simulation
case ' ' :
{
if (m_simManager->getStatus() == SimulationStatus::RUNNING)
{
m_simManager->pauseSimulation();
}
else if (m_simManager->getStatus() == SimulationStatus::PAUSED)
{
m_simManager->runSimulation();
}
}
break;
}
}
void
InteractorStyle::OnMouseMove()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnMouseMove();
}
void
InteractorStyle::OnLeftButtonDown()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnLeftButtonDown();
}
void
InteractorStyle::OnLeftButtonUp()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnLeftButtonUp();
}
void
InteractorStyle::OnMiddleButtonDown()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnMiddleButtonDown();
}
void
InteractorStyle::OnMiddleButtonUp()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnMiddleButtonUp();
}
void
InteractorStyle::OnRightButtonDown()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnRightButtonDown();
}
void
InteractorStyle::OnRightButtonUp()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnRightButtonUp();
}
void
InteractorStyle::OnMouseWheelForward()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnMouseWheelForward();
}
void
InteractorStyle::OnMouseWheelBackward()
{
if (m_simManager->getStatus() != SimulationStatus::INACTIVE)
{
return;
}
vtkBaseInteractorStyle::OnMouseWheelBackward();
}
void
InteractorStyle::setSimulationManager(SimulationManager *simManager)
{
m_simManager = simManager;
}
}
/*=========================================================================
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.
=========================================================================*/
#ifndef imstkInteractorStyle_h
#define imstkInteractorStyle_h
#include <memory>
#include "vtkInteractorStyleTrackballCamera.h"
namespace imstk {
class SimulationManager;
using vtkBaseInteractorStyle = vtkInteractorStyleTrackballCamera;
class InteractorStyle : public vtkBaseInteractorStyle
{
public:
static InteractorStyle *New();
vtkTypeMacro(InteractorStyle, vtkBaseInteractorStyle);
virtual void OnChar() override;
virtual void OnMouseMove() override;
virtual void OnLeftButtonDown() override;
virtual void OnLeftButtonUp() override;
virtual void OnMiddleButtonDown() override;
virtual void OnMiddleButtonUp() override;
virtual void OnRightButtonDown() override;
virtual void OnRightButtonUp() override;
virtual void OnMouseWheelForward() override;
virtual void OnMouseWheelBackward() override;
void setSimulationManager(SimulationManager* simManager);
private:
SimulationManager* m_simManager;
};
}
#endif // ifndef imstkInteractorStyle_h
......@@ -114,103 +114,103 @@ SimulationManager::getViewer() const
}
void
SimulationManager::startSimulation(std::string sceneName, Renderer::Mode renderMode)
SimulationManager::setCurrentScene(std::string newSceneName, bool unloadCurrentScene)
{
LOG(INFO) << "Starting simulation.";
if (m_status != SimulationStatus::INACTIVE)
{
LOG(WARNING) << "Simulation already active.";
return;
}
std::shared_ptr<Scene> startingScene = this->getScene(sceneName);
if (!startingScene)
{
LOG(WARNING) << "Simulation canceled.";
return;
}
if (startingScene->getStatus() != ModuleStatus::INACTIVE)
{
LOG(WARNING) << "Scene '" << sceneName << "' is already active.\n"
<< "Simulation canceled.";
return;
}
// Init viewer
m_viewer->setCurrentScene(startingScene);
m_viewer->setRenderingMode(renderMode);
// Start scene
this->startModuleInNewThread(startingScene);
m_currentSceneName = sceneName;
// Update simulation status
m_status = SimulationStatus::RUNNING;
// Start Rendering
m_viewer->startRenderingLoop();
this->endSimulation();
}
void
SimulationManager::switchScene(std::string newSceneName, bool unloadCurrentScene)
{
LOG(INFO) << "Switching scene.";
if ((m_status != SimulationStatus::RUNNING) &&
(m_status != SimulationStatus::PAUSED))
{
LOG(WARNING) << "Simulation not active, can not switch scenes.";
return;
}
LOG(INFO) << "Setting current scene.";
if (newSceneName == m_currentSceneName)
{
LOG(WARNING) << "Scene '" << newSceneName << "' is already running.";
LOG(WARNING) << "Scene '" << newSceneName << "' is already current.";
return;
}
std::shared_ptr<Scene> newScene = this->getScene(newSceneName);
if (!newScene)
{
LOG(WARNING) << "Can not switch scenes.";
LOG(WARNING) << "Can not find scene.";
return;
}
// Update viewer
m_viewer->setCurrentScene(newScene);
if (m_status == SimulationStatus::INACTIVE)
{
m_viewer->setRenderingMode(Renderer::Mode::DEBUG);
m_currentSceneName = newSceneName;
return;
}
m_viewer->setRenderingMode(Renderer::Mode::SIMULATION);
// Stop/Pause running scene
if (unloadCurrentScene)
{
// Stop current scene
LOG(INFO) << "Unloading '" << m_currentSceneName << "'.";
m_sceneMap.at(m_currentSceneName)->end();
m_threadMap.at(m_currentSceneName).join();
}
else
{
// Pause current scene
m_sceneMap.at(m_currentSceneName)->pause();
}
// Start/Run new scene
if (newScene->getStatus() == ModuleStatus::INACTIVE)
{
// Start new scene
this->startModuleInNewThread(newScene);
}
else if (newScene->getStatus() == ModuleStatus::PAUSED)
{
// Run new scene
newScene->run();
}
m_currentSceneName = newSceneName;
}
void
SimulationManager::startSimulation(bool debug)
{
if (m_status != SimulationStatus::INACTIVE)
{
LOG(WARNING) << "Simulation already active.";
return;
}
std::shared_ptr<Scene> startingScene = this->getScene(m_currentSceneName);
if (!startingScene)
{
LOG(WARNING) << "Simulation canceled.";
return;
}
if (startingScene->getStatus() != ModuleStatus::INACTIVE)
{
LOG(WARNING) << "Scene '" << m_currentSceneName << "' is already active.";
return;
}
// Start Simulation
if( !debug )
{
LOG(INFO) << "Starting simulation.";
m_viewer->setRenderingMode(Renderer::Mode::SIMULATION);
this->startModuleInNewThread(startingScene);
m_status = SimulationStatus::RUNNING;
}
// Start Rendering
if( !m_viewer->isRendering() )
{
LOG(INFO) << "Starting viewer.";
m_viewer->startRenderingLoop();
LOG(INFO) << "Closing viewer.";
// End simulation if active when loop exits
if (m_status != SimulationStatus::INACTIVE)
{
this->endSimulation();
}
}
}
void
SimulationManager::runSimulation()
{
......@@ -259,6 +259,9 @@ SimulationManager::endSimulation()
return;
}
// Update Renderer
m_viewer->setRenderingMode(Renderer::Mode::DEBUG);
// End all scenes
for (auto pair : m_sceneMap)
{
......
......@@ -60,13 +60,11 @@ public:
std::shared_ptr<Viewer> getViewer() const;
// Simulation
void startSimulation(std::string sceneName,
Renderer::Mode renderMode = Renderer::Mode::SIMULATION);
void switchScene(std::string newSceneName,
bool unloadCurrentScene);
void runSimulation();