fw4spl
SrcLib/visu/fwRenderVTK/src/fwRenderVTK/SRender.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwRenderVTK/SRender.hpp"
8 
9 #include "fwRenderVTK/IAdaptor.hpp"
10 #include "fwRenderVTK/IVtkRenderWindowInteractorManager.hpp"
11 #include "fwRenderVTK/OffScreenInteractorManager.hpp"
12 #include "fwRenderVTK/registry/adaptors.hpp"
13 #include "fwRenderVTK/vtk/InteractorStyle3DForNegato.hpp"
14 
15 #include <fwCom/Signal.hpp>
16 #include <fwCom/Signal.hxx>
17 #include <fwCom/Slot.hpp>
18 #include <fwCom/Slot.hxx>
19 #include <fwCom/Slots.hpp>
20 #include <fwCom/Slots.hxx>
21 
22 #include <fwData/Color.hpp>
23 #include <fwData/mt/ObjectWriteLock.hpp>
24 
25 #include <fwRuntime/ConfigurationElementContainer.hpp>
26 #include <fwRuntime/utils/GenericExecutableFactoryRegistrar.hpp>
27 
28 #include <fwServices/helper/Config.hpp>
29 #include <fwServices/macros.hpp>
30 #include <fwServices/op/Add.hpp>
31 
32 #include <fwThread/Timer.hpp>
33 
34 #include <fwTools/fwID.hpp>
35 
36 #include <fwVtkIO/vtk.hpp>
37 
38 #include <boost/foreach.hpp>
39 #include <boost/function.hpp>
40 #include <boost/optional.hpp>
41 
42 #include <vtkCellPicker.h>
43 #include <vtkImageFlip.h>
44 #include <vtkInstantiator.h>
45 #include <vtkRenderer.h>
46 #include <vtkRendererCollection.h>
47 #include <vtkRenderWindow.h>
48 #include <vtkSmartPointer.h>
49 #include <vtkTransform.h>
50 #include <vtkWindowToImageFilter.h>
51 
52 #include <functional>
53 
54 using namespace fwServices;
55 
56 namespace fwRenderVTK
57 {
58 const ::fwCom::Signals::SignalKeyType SRender::s_DROPPED_SIG = "dropped";
59 const ::fwCom::Slots::SlotKeyType SRender::s_RENDER_SLOT = "render";
60 const ::fwCom::Slots::SlotKeyType SRender::s_REQUEST_RENDER_SLOT = "requestRender";
61 const ::fwCom::Slots::SlotKeyType SRender::s_TOGGLE_AUTO_RENDER_SLOT = "toggleAutoRender";
62 
63 static const ::fwServices::IService::KeyType s_OFFSCREEN_INOUT = "offScreen";
64 
65 //-----------------------------------------------------------------------------
66 
67 SRender::SRender() noexcept :
68  m_pendingRenderRequest(false),
69  m_renderMode(RenderMode::AUTO),
70  m_width(160),
71  m_height(90),
72  m_offScreen(false),
73  m_flip(false)
74 {
75  newSignal<DroppedSignalType>(s_DROPPED_SIG);
76 
77  newSlot(s_RENDER_SLOT, &SRender::render, this);
78  newSlot(s_REQUEST_RENDER_SLOT, &SRender::requestRender, this);
79  newSlot(s_TOGGLE_AUTO_RENDER_SLOT, &SRender::toggleAutoRender, this);
80 }
81 
82 //-----------------------------------------------------------------------------
83 
84 SRender::~SRender() noexcept
85 {
86 }
87 
88 //-----------------------------------------------------------------------------
89 
90 void SRender::configureRenderer( const ConfigType& rendererConf )
91 {
92  const std::string id = rendererConf.get<std::string>("<xmlattr>.id");
93  const std::string background = rendererConf.get<std::string>("<xmlattr>.background", "");
94 
95  if(m_renderers.count(id) == 0)
96  {
97  m_renderers[id] = vtkRenderer::New();
98 
99 //vtk depth peeling not available on android (Offscreen rendering issues)
100 #ifndef ANDROID
101  m_renderers[id]->SetUseDepthPeeling( 1 );
102  m_renderers[id]->SetMaximumNumberOfPeels( 8 );
103  m_renderers[id]->SetOcclusionRatio( 0. );
104 #endif
105 
106  const int layer = rendererConf.get<int>("<xmlattr>.layer", m_renderers[id]->GetLayer());
107  m_renderers[id]->SetLayer(layer);
108  }
109 
110  if ( !background.empty() )
111  {
112  if(background[0] == '#')
113  {
114  ::fwData::Color::sptr color = ::fwData::Color::New();
115  color->setRGBA(background);
116  m_renderers[id]->SetBackground(color->getRGBA()[0], color->getRGBA()[1], color->getRGBA()[2]);
117  }
118  else
119  {
120  // compatibility with "old" color
121  double color = std::stod(background);
122  m_renderers[id]->SetBackground(color, color, color);
123  }
124  }
125 }
126 
127 //-----------------------------------------------------------------------------
128 
129 void SRender::configurePicker( const ConfigType& pickerConf )
130 {
131  const std::string id = pickerConf.get<std::string>("<xmlattr>.id");
132  const std::string vtkclass = pickerConf.get<std::string>("<xmlattr>.vtkclass", "vtkCellPicker");
133  const double tolerance = pickerConf.get<double>("<xmlattr>.tolerance", 0.0);
134 
135  if(m_pickers.count(id) == 0)
136  {
137  m_pickers[id] = vtkAbstractPropPicker::SafeDownCast(vtkInstantiator::CreateInstance(vtkclass.c_str()));
138  OSLM_ASSERT("'" << vtkclass.c_str() << "' instantiation failled.", m_pickers[id]);
139  m_pickers[id]->InitializePickList();
140  m_pickers[id]->PickFromListOn();
141  vtkPicker* picker = vtkPicker::SafeDownCast(m_pickers[id]);
142  if (picker)
143  {
144  picker->SetTolerance(tolerance);
145  }
146  }
147 }
148 
149 //-----------------------------------------------------------------------------
150 
151 void SRender::configureVtkObject( const ConfigType& vtkObjectConf )
152 {
153  const std::string id = vtkObjectConf.get<std::string>("<xmlattr>.id");
154  const std::string vtkClass = vtkObjectConf.get<std::string>("<xmlattr>.class");
155 
156  SLM_ASSERT("Empty 'id'.", !id.empty() );
157  SLM_ASSERT("Empty 'class'.", !vtkClass.empty() );
158 
159  if( m_vtkObjects.count(id) == 0 )
160  {
161  if ( vtkClass == "vtkTransform" )
162  {
163  m_vtkObjects[id] = createVtkTransform(vtkObjectConf);
164  }
165  else
166  {
167  m_vtkObjects[id] = vtkInstantiator::CreateInstance(vtkClass.c_str());
168  }
169  }
170 }
171 
172 //-----------------------------------------------------------------------------
173 
174 vtkTransform* SRender::createVtkTransform( const ConfigType& vtkObjectConf )
175 {
176  vtkTransform* newMat = vtkTransform::New();
177 
178  SLM_ASSERT("VTK transforms can contain at most one 'vtkTransform' sub-element.",
179  vtkObjectConf.count("vtkTransform") <= 1 );
180 
181  const ::boost::optional<const ConfigType&> vtkTransformConf = vtkObjectConf.get_child_optional("vtkTransform");
182 
183  if(vtkTransformConf.is_initialized())
184  {
185  BOOST_FOREACH(const ::boost::property_tree::ptree::value_type& v, vtkTransformConf.get())
186  {
187  SLM_ASSERT("Invalid markup '" + v.first + "', 'concatenate' must be used here.", v.first == "concatenate");
188 
189  const std::string& transformId = v.second.data();
190 
191  vtkTransform* mat = vtkTransform::SafeDownCast( getVtkObject(transformId) );
192 
193  SLM_ASSERT("No transform named '" + transformId + "'.", mat != nullptr);
194 
195  const std::string inverse = v.second.get<std::string>("<xmlattr>.inverse", "no");
196 
197  SLM_ASSERT("Inverse must be 'yes' or 'no'.", inverse == "yes" || inverse == "no");
198 
199  if(inverse == "yes")
200  {
201  newMat->Concatenate( mat->GetLinearInverse() );
202  }
203  else
204  {
205  newMat->Concatenate( mat );
206  }
207  }
208  }
209 
210  return newMat;
211 }
212 
213 //-----------------------------------------------------------------------------
214 
215 void SRender::addVtkObject( const VtkObjectIdType& _id, vtkObject* _vtkObj )
216 {
217  SLM_ASSERT( "vtkObject id is empty", !_id.empty() );
218  SLM_ASSERT( "vtkObject is NULL", _vtkObj );
219 
220  if( m_vtkObjects.count(_id) == 0 )
221  {
222  m_vtkObjects[_id] = _vtkObj;
223  }
224 }
225 
226 //-----------------------------------------------------------------------------
227 
228 void SRender::configuring()
229 {
230  const ConfigType& srvConf = this->getConfigTree();
231 
232  const size_t nbInouts = srvConf.count("inout");
233 
234  SLM_ASSERT("This service accepts at most one inout.", nbInouts <= 1);
235 
236  m_flip = (srvConf.get<std::string>("flip", "false") == "true");
237 
238  if(nbInouts == 1)
239  {
240  const std::string key = srvConf.get<std::string>("inout.<xmlattr>.key", "");
241  m_offScreen = (key == s_OFFSCREEN_INOUT);
242 
243  SLM_ASSERT("'" + key + "' is not a valid key. Only '" + s_OFFSCREEN_INOUT +"' is accepted.", m_offScreen);
244  }
245  else // no offscreen rendering.
246  {
247  SLM_WARN_IF("Flip tag is set to 'true' but no off screen render image is used.", m_flip);
248  this->initialize();
249  }
250 
251  m_sceneConf = srvConf.get_child("scene");
252 
253  const std::string renderMode = m_sceneConf.get("<xmlattr>.renderMode", "auto");
254 
255  if (renderMode == "auto")
256  {
257  m_renderMode = RenderMode::AUTO;
258  }
259  else if (renderMode == "timer")
260  {
261  m_renderMode = RenderMode::TIMER;
262  }
263  else if (renderMode == "sync")
264  {
265  m_renderMode = RenderMode::SYNC;
266  }
267  else if (renderMode == "none")
268  {
269  m_renderMode = RenderMode::NONE;
270  }
271  else
272  {
273  SLM_WARN_IF("renderMode '" + renderMode + " is unknown, setting renderMode to 'auto'.",
274  !renderMode.empty());
275  }
276 
277  m_width = m_sceneConf.get<unsigned int>("<xmlattr>.width", m_width);
278  m_height = m_sceneConf.get<unsigned int>("<xmlattr>.height", m_height);
279 
280  BOOST_FOREACH(const ::fwServices::IService::ConfigType::value_type& v, m_sceneConf.equal_range("adaptor"))
281  {
282  const std::string adaptorUid = v.second.get<std::string>("<xmlattr>.uid", "");
283 
284  SLM_FATAL_IF("Missing 'uid' attribute in adaptor configuration", adaptorUid == "");
285 
286  // register the <adaptor, scene> association
287  auto& registry = ::fwRenderVTK::registry::getAdaptorRegistry();
288  registry[adaptorUid] = this->getID();
289  }
290 
292  const unsigned int targetFrameRate = srvConf.get<unsigned int>("fps", 30);
293 
294  if(m_renderMode == RenderMode::TIMER)
295  {
296  unsigned int timeStep = static_cast<unsigned int>( 1000.f / targetFrameRate );
297  m_timer = m_associatedWorker->createTimer();
298 
299  ::fwThread::Timer::TimeDurationType duration = std::chrono::milliseconds(timeStep);
300  m_timer->setFunction( std::bind( &SRender::requestRender, this) );
301  m_timer->setDuration(duration);
302  }
303 }
304 
305 //-----------------------------------------------------------------------------
306 
307 void SRender::starting()
308 {
309  if (!m_offScreen)
310  {
311  this->create();
312  }
313 
314  this->startContext();
315 
316  // Instantiate vtk object, class...
317  BOOST_FOREACH(const ::fwServices::IService::ConfigType::value_type& v, m_sceneConf)
318  {
319  const std::string& subEltName = v.first;
320  if(subEltName == "renderer")
321  {
322  this->configureRenderer(v.second);
323  }
324  else if(subEltName == "picker")
325  {
326  this->configurePicker(v.second);
327  }
328  else if(subEltName == "vtkObject")
329  {
330  this->configureVtkObject(v.second);
331  }
332  else if(subEltName != "adaptor" && subEltName != "<xmlattr>")
333  {
334  SLM_FATAL("Unknown sub-element '" + subEltName + "'.");
335  }
336  }
337 
338  m_interactorManager->getInteractor()->GetRenderWindow()->SetNumberOfLayers(static_cast<int>(m_renderers.size()));
339  for( RenderersMapType::iterator iter = m_renderers.begin(); iter != m_renderers.end(); ++iter )
340  {
341  vtkRenderer* renderer = (*iter).second;
342  m_interactorManager->getInteractor()->GetRenderWindow()->AddRenderer(renderer);
343  }
344 
345  if(m_timer)
346  {
347  m_timer->start();
348  }
349 }
350 
351 //-----------------------------------------------------------------------------
352 
353 void SRender::stopping()
354 {
355  if(m_timer)
356  {
357  m_timer->stop();
358  }
359 
360  this->stopContext();
361 
362  if (!m_offScreen)
363  {
364  this->destroy();
365  }
366 }
367 
368 //-----------------------------------------------------------------------------
369 
370 void SRender::updating()
371 {
372 }
373 
374 //-----------------------------------------------------------------------------
375 
376 void SRender::render()
377 {
378  OSLM_ASSERT("Scene must be started", this->isStarted());
379  if (m_offScreen)
380  {
381  ::fwData::Image::sptr image = this->getInOut< ::fwData::Image >(s_OFFSCREEN_INOUT);
382  SLM_ASSERT("Offscreen image not found.", image);
383 
384  vtkSmartPointer<vtkRenderWindow> renderWindow = m_interactorManager->getInteractor()->GetRenderWindow();
385 
386  renderWindow->Render();
387 
388  vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
389  windowToImageFilter->SetInputBufferTypeToRGBA();
390  windowToImageFilter->SetInput( renderWindow );
391  windowToImageFilter->Update();
392 
393  {
394  ::fwData::mt::ObjectWriteLock lock(image);
395  if(m_flip)
396  {
397  vtkSmartPointer<vtkImageFlip> flipImage = vtkSmartPointer<vtkImageFlip>::New();
398  flipImage->SetFilteredAxes(1);
399  flipImage->SetInputData(windowToImageFilter->GetOutput());
400  flipImage->Update();
401 
402  ::fwVtkIO::fromVTKImage(flipImage->GetOutput(), image);
403  }
404  else
405  {
406  ::fwVtkIO::fromVTKImage(windowToImageFilter->GetOutput(), image);
407  }
408  }
409 
411  {
412  ::fwCom::Connection::Blocker block(sig->getConnection(m_slotUpdate));
413  sig->asyncEmit();
414  }
415  }
416  else
417  {
418  m_interactorManager->getInteractor()->Render();
419  }
420  this->setPendingRenderRequest(false);
421 }
422 
423 //-----------------------------------------------------------------------------
424 
425 bool SRender::isShownOnScreen()
426 {
427  if (!m_offScreen)
428  {
429  return this->getContainer()->isShownOnScreen();
430  }
431  else
432  {
433  return true;
434  }
435 }
436 
437 //-----------------------------------------------------------------------------
438 
439 void SRender::requestRender()
440 {
441  if ( this->isShownOnScreen() && !this->getPendingRenderRequest())
442  {
443  this->setPendingRenderRequest(true);
444  if(m_renderMode == RenderMode::SYNC)
445  {
446  OSLM_DEBUG("sync SRender: " << this->getID());
447  this->slot(SRender::s_RENDER_SLOT)->run();
448  }
449  else
450  {
451  this->slot(SRender::s_RENDER_SLOT)->asyncRun();
452  }
453  }
454 }
455 
456 //-----------------------------------------------------------------------------
457 
458 void SRender::toggleAutoRender()
459 {
460  if(m_renderMode == RenderMode::AUTO)
461  {
462  m_renderMode = RenderMode::NONE;
463  }
464  else if(m_renderMode == RenderMode::NONE)
465  {
466  m_renderMode = RenderMode::AUTO;
467  }
468 
469  auto interactor = m_interactorManager->getInteractor()->GetInteractorStyle();
470  auto interactorStyle = dynamic_cast< IInteractorStyle* >(interactor);
471  interactorStyle->setAutoRender(m_renderMode == RenderMode::AUTO);
472 }
473 
474 //-----------------------------------------------------------------------------
475 
476 void SRender::startContext()
477 {
478  if (!m_offScreen)
479  {
480  m_interactorManager = ::fwRenderVTK::IVtkRenderWindowInteractorManager::createManager();
481  m_interactorManager->installInteractor( this->getContainer() );
482  }
483  else
484  {
485  ::fwRenderVTK::OffScreenInteractorManager::sptr interactorManager =
486  ::fwRenderVTK::OffScreenInteractorManager::New();
487  interactorManager->installInteractor(m_width, m_height);
488  m_interactorManager = interactorManager;
489  }
490 
491  auto interactor = vtkSmartPointer<InteractorStyle3DForNegato>::New();
492  SLM_ASSERT("Can't instantiate interactor", interactor);
493  interactor->setAutoRender(m_renderMode == RenderMode::AUTO);
494  m_interactorManager->getInteractor()->SetInteractorStyle( interactor );
495 
496  m_interactorManager->setRenderService(this->getSptr());
497 
498 #ifndef __linux
499  m_interactorManager->getInteractor()->GetRenderWindow()->SetAlphaBitPlanes(1);
500  m_interactorManager->getInteractor()->GetRenderWindow()->SetMultiSamples(0);
501 #endif
502 
503 }
504 
505 //-----------------------------------------------------------------------------
506 
507 void SRender::stopContext()
508 {
509  for( RenderersMapType::iterator iter = m_renderers.begin(); iter != m_renderers.end(); ++iter )
510  {
511  vtkRenderer* renderer = iter->second;
512  renderer->InteractiveOff();
513  m_interactorManager->getInteractor()->GetRenderWindow()->RemoveRenderer(renderer);
514  renderer->Delete();
515  }
516 
517  m_renderers.clear();
518 
519  m_interactorManager->uninstallInteractor();
520  m_interactorManager.reset();
521 }
522 
523 //-----------------------------------------------------------------------------
524 
525 vtkRenderer* SRender::getRenderer(RendererIdType rendererId)
526 {
527  OSLM_ASSERT("Renderer not found : '" << rendererId << "'", m_renderers.count(rendererId) == 1);
528 
529  return m_renderers[rendererId];
530 }
531 
532 //-----------------------------------------------------------------------------
533 
534 vtkAbstractPropPicker* SRender::getPicker(PickerIdType pickerId)
535 {
536  PickersMapType::const_iterator iter = m_pickers.find(pickerId);
537  if ( iter == m_pickers.end())
538  {
539  SLM_DEBUG("Picker '" + pickerId + "' not found");
540  return nullptr;
541  }
542  return iter->second;
543 }
544 
545 //-----------------------------------------------------------------------------
546 
547 vtkObject* SRender::getVtkObject(const VtkObjectIdType& objectId) const
548 {
549  VtkObjectMapType::const_iterator iter = m_vtkObjects.find(objectId);
550  if ( iter == m_vtkObjects.end())
551  {
552  SLM_DEBUG("vtkObject '" + objectId + "' not found");
553  return nullptr;
554  }
555  return iter->second;
556 }
557 
558 //-----------------------------------------------------------------------------
559 
560 vtkTransform* SRender::getOrAddVtkTransform( const VtkObjectIdType& _id )
561 {
562  vtkTransform* t = vtkTransform::SafeDownCast(getVtkObject(_id));
563  if(t == 0)
564  {
565  t = vtkTransform::New();
566  this->addVtkObject(_id, t);
567  }
568  return t;
569 }
570 
571 //-----------------------------------------------------------------------------
572 
573 bool SRender::isOffScreen() const
574 {
575  return m_offScreen;
576 }
577 
578 //-----------------------------------------------------------------------------
579 
580 void SRender::setOffScreenRenderSize(unsigned int _width, unsigned int _height)
581 {
582  if(m_offScreen)
583  {
584  m_width = std::max(_width, 1u);
585  m_height = std::max(_height, 1u);
586 
587  // The MakeCurrent() is really essential otherwise this breaks the rendering of other rendering windows
588  m_interactorManager->getInteractor()->GetRenderWindow()->MakeCurrent();
589  m_interactorManager->getInteractor()->GetRenderWindow()->SetSize(static_cast<int>(m_width),
590  static_cast<int>(m_height));
591  }
592 }
593 
594 //-----------------------------------------------------------------------------
595 
596 } //namespace fwRenderVTK
Contains fwAtomsFilter::registry details.
IInteractorStyle is an interface dedicated to hold some flags for class inherited from vtkInteractorS...
#define OSLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:310
Class allowing to block a Connection.
Definition: Connection.hpp:20
Namespace fwServices is dedicated to (mimic) the dynamic affectation of methods to (pure data) object...
A helper to lock object on exclusive mode.
#define SLM_DEBUG(message)
Definition: spyLog.hpp:239
void setAutoRender(bool _autoRender)
Set the autorender flag.
#define SLM_FATAL(message)
Definition: spyLog.hpp:283
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
#define SLM_FATAL_IF(message, cond)
Definition: spyLog.hpp:287
static FWDATA_APIconst::fwCom::Signals::SignalKeyType s_MODIFIED_SIG
Key in m_signals map of signal m_sigModified.
The namespace fwRenderVTK contains classes for rendering with VTK.
#define SLM_WARN_IF(message, cond)
Definition: spyLog.hpp:265
#define OSLM_DEBUG(message)
Definition: spyLog.hpp:241