OpenWalnut  1.5.0dev
WMImageSpaceTensorLIC.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <cmath>
26 #include <cstdlib>
27 #include <memory>
28 #include <string>
29 #include <vector>
30 
31 #include <osg/BoundingSphere>
32 #include <osg/Drawable>
33 #include <osg/Geode>
34 #include <osg/Geometry>
35 #include <osg/Vec3>
36 
37 #include "WMImageSpaceTensorLIC.h"
38 #include "WTuringTextureCreator.h"
39 #include "core/common/WPropertyHelper.h"
40 #include "core/common/math/WMath.h"
41 #include "core/dataHandler/WDataHandler.h"
42 #include "core/dataHandler/WDataTexture3D.h"
43 #include "core/dataHandler/WGridRegular3D.h"
44 #include "core/graphicsEngine/WGEColormapping.h"
45 #include "core/graphicsEngine/WGEGeodeUtils.h"
46 #include "core/graphicsEngine/WGETextureUtils.h"
47 #include "core/graphicsEngine/WGEUtils.h"
48 #include "core/graphicsEngine/callbacks/WGELinearTranslationCallback.h"
49 #include "core/graphicsEngine/callbacks/WGENodeMaskCallback.h"
50 #include "core/graphicsEngine/callbacks/WGEShaderAnimationCallback.h"
51 #include "core/graphicsEngine/offscreen/WGEOffscreenRenderNode.h"
52 #include "core/graphicsEngine/offscreen/WGEOffscreenRenderPass.h"
53 #include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
54 #include "core/graphicsEngine/shaders/WGEShader.h"
55 #include "core/graphicsEngine/shaders/WGEShaderDefineOptions.h"
56 #include "core/graphicsEngine/shaders/WGEShaderPropertyDefineOptions.h"
57 #include "core/kernel/WKernel.h"
58 
59 
60  /**
61  * @brief Calculates a reaction diffusion texture using turings method.
62  *
63  * @param target the memory to store the texture to
64  * @param tileWidth the width of one tile
65  * @param tileHeight the height of one tile
66  * @param width the width of the target mem
67  * @param height the height of the target mem. Not used.
68  * @param iterations the number of iterations to use
69  * @param spotSize the size of the spots - [0,1]
70  * @param spotIrregularity value specifying irregularity of the spots - [0,1]
71  */
72 void genReactionDiffusion( unsigned char* target,
73  unsigned int tileWidth, unsigned int tileHeight,
74  unsigned int width, unsigned int /* height */,
75  unsigned int iterations,
76  float spotSize,
77  float spotIrregularity )
78 {
79  /////////////////////////////////////////////////////////////////////////////////////////////
80  // 1: get memory
81  /////////////////////////////////////////////////////////////////////////////////////////////
82 
83  // at first create some mem
84  float grid1Min = 3.0;
85  float grid1Max = 5.0;
86  float grid1Base = 4.0;
87  std::vector< std::vector< float > > grid1( tileWidth,
88  std::vector< float >( tileHeight, grid1Base ) );
89 
90  float grid2Base = 4.0;
91  std::vector< std::vector< float > > grid2( tileWidth,
92  std::vector< float >( tileHeight, grid2Base ) );
93 
94  std::vector< std::vector< float > > delta1( tileWidth,
95  std::vector< float >( tileHeight, 0.0f ) );
96  std::vector< std::vector< float > > delta2( tileWidth,
97  std::vector< float >( tileHeight, 0.0f ) );
98 
99  std::vector< std::vector< float > > noise( tileWidth,
100  std::vector< float >( tileHeight, 0.0f ) );
101 
102  float noiseRangeMin = 0.1;
103  float noiseRangeMax = 5.0;
104 
105  float noiseRange =
106  noiseRangeMin + ( ( noiseRangeMax - noiseRangeMin ) *
107  spotIrregularity ); // the highter the more irregular "spots"
108  float noiseBase = 12.0;
109 
110  /////////////////////////////////////////////////////////////////////////////////////////////
111  // 2: init the grids and create random seed used during turing iteration
112  /////////////////////////////////////////////////////////////////////////////////////////////
113 
114  // init grids
115  srand48( time( 0 ) );
116  for( unsigned int y = 0; y < tileHeight; y++ )
117  {
118  for( unsigned int x = 0; x < tileWidth; x++ )
119  {
120  // grid1[ x ][ y ] = grid1Base;
121  // grid2[ x ][ y ] = grid2Base;
122  noise[ x ][ y ] = noiseBase + ( -noiseRange + ( drand48() * 2.0 * noiseRange ) );
123  // delta1[ x ][ y ] = 0.0;
124  // delta2[ x ][ y ] = 0.0;
125  }
126  }
127 
128  /////////////////////////////////////////////////////////////////////////////////////////////
129  // 3: turing iteration
130  /////////////////////////////////////////////////////////////////////////////////////////////
131 
132  float kaMin = 0.02;
133  float kaMax = 0.6;
134  float ka = ( kaMin + ( ( kaMax - kaMin ) * ( 1.0 - spotSize ) ) ) / 15.0; // size of spots
135  float d1= 0.25 / 2.0;
136  float d2= 0.0625 / 2.0;
137  float speed = 1.0;
138 
139  for( unsigned int iteration = 0; iteration < iterations; iteration++ )
140  {
141  // go through each cell in grid
142  for( unsigned int i = 0; i < tileWidth; i++ )
143  {
144  // this ensures we have an "endless" plane -> creates seamless textures
145  unsigned int iPrev = ( i + tileWidth - 1 ) % tileWidth;
146  unsigned int iNext = ( i + 1 ) % tileWidth;
147 
148  for( unsigned int j = 0; j < tileHeight; j++ )
149  {
150  // this ensures we have an "endless" plane -> creates seamless textures
151  unsigned int jPrev = ( j + tileHeight - 1 ) % tileHeight;
152  unsigned int jNext = ( j + 1 ) % tileHeight;
153 
154  /*
155  // try the other laplacian filter
156  float laplacian1= grid1[iPrev][jPrev] + grid1[i][jPrev] + grid1[iNext][jPrev]
157  + grid1[iPrev][j] - (8.0*grid1[i][j]) + grid1[iNext][j] +
158  grid1[iPrev][jNext] + grid1[i][jNext] +
159  grid1[iNext][jNext];
160 
161  float laplacian2= grid2[iPrev][jPrev] + grid2[i][jPrev] + grid2[iNext][jPrev]
162  + grid2[iPrev][j] - (8.0*grid2[i][j]) + grid2[iNext][j] +
163  grid2[iPrev][jNext] + grid2[i][jNext] +
164  grid2[iNext][jNext];
165  */
166 
167  // apply laplace filter around current grid point
168  float laplacian1 = grid1[ i ][ jPrev ] + grid1[ i ][ jNext ] + grid1[ iPrev ][ j ] +
169  grid1[ iNext ][ j ] - 4.0 * grid1[ i ][ j ];
170  float laplacian2 = grid2[ i ][ jPrev ] + grid2[ i ][ jNext ] + grid2[ iPrev ][ j ] +
171  grid2[ iNext ][ j ] - 4.0 * grid2[ i ][ j ];
172 
173  // diffusion reaction
174  delta1[ i ][ j ] =
175  ka * ( 16 - grid1[ i ][ j ] * grid2[ i ][ j ] ) + d1 * laplacian1;
176  delta2[ i ][ j ] =
177  ka * ( grid1[ i ][ j ] * grid2[ i ][ j ] - grid2[ i ][ j ] - noise[ i ][ j ] ) +
178  d2 * laplacian2;
179  }
180  }
181 
182  // apply delta and find min and max value
183  grid1Min = 1e20;
184  grid1Max = -1e20;
185 
186  for( unsigned int i = 0; i < tileWidth; i++ )
187  {
188  for( unsigned int j = 0; j < tileHeight; j++ )
189  {
190  grid1[ i ][ j ] += ( speed * delta1[ i ][ j ] );
191  grid2[ i ][ j ] += ( speed * delta2[ i ][ j ] );
192  if( grid2[ i ][ j ] < 0 )
193  grid2[ i ][ j ] = 0;
194 
195  if( grid1[ i ][ j ] < grid1Min )
196  grid1Min = grid1[ i ][ j ];
197  if( grid1[ i ][ j ] > grid1Max )
198  grid1Max = grid1[ i ][ j ];
199  }
200  }
201  }
202 
203  /////////////////////////////////////////////////////////////////////////////////////////////
204  // 4: scale grid and copy to target
205  /////////////////////////////////////////////////////////////////////////////////////////////
206 
207  for( unsigned int x = 0; x < tileWidth; x++ )
208  {
209  for( unsigned int y = 0; y < tileHeight; y++ )
210  {
211  target[ ( y * width ) + x ] =
212  255.0 * ( grid1[ x ][ y ] - grid1Min ) / ( grid1Max - grid1Min );
213  }
214  }
215 }
216 
218  WModule()
219 {
220 }
221 
223 {
224  // Cleanup!
225 }
226 
227 std::shared_ptr< WModule > WMImageSpaceTensorLIC::factory() const
228 {
229  return std::shared_ptr< WModule >( new WMImageSpaceTensorLIC() );
230 }
231 
232 const std::string WMImageSpaceTensorLIC::getName() const
233 {
234  return "Image Space Tensor LIC";
235 }
236 
237 const std::string WMImageSpaceTensorLIC::getDescription() const
238 {
239  return "";
240 }
241 
243 {
244  // DTI input
245  m_evec1In = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "evec1", "The first Eigenvectors dataset." );
246  m_evec2In = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "evec2", "The second Eigenvectors dataset." );
247  m_evalsIn = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "evals", "The Eigenvalues dataset." );
248 
249  // mesh input
250  m_meshIn = WModuleInputData< WTriangleMesh >::createAndAdd( shared_from_this(), "surface", "The optional surface to use." );
251 
252  // call WModule's initialization
254 }
255 
257 {
258  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
259 
260  m_geometryGroup = m_properties->addPropertyGroup( "Geometry", "Selection of used geometry to apply LIC to." );
261 
262  m_useSlices = m_geometryGroup->addProperty( "Use Slices", "Show vectors on slices.", true, m_propCondition );
263 
264  m_sliceGroup = m_geometryGroup->addPropertyGroup( "Slices", "Slice based LIC." );
265 
266  // enable slices
267  // Flags denoting whether the glyphs should be shown on the specific slice
268  m_showonX = m_sliceGroup->addProperty( "Show Sagittal", "Show vectors on sagittal slice.", true );
269  m_showonY = m_sliceGroup->addProperty( "Show Coronal", "Show vectors on coronal slice.", true );
270  m_showonZ = m_sliceGroup->addProperty( "Show Axial", "Show vectors on axial slice.", true );
271 
272  // The slice positions. These get update externally.
273  // TODO(all): this should somehow be connected to the nav slices.
274  m_xPos = m_sliceGroup->addProperty( "Sagittal Position", "Slice X position.", 80 );
275  m_yPos = m_sliceGroup->addProperty( "Coronal Position", "Slice Y position.", 100 );
276  m_zPos = m_sliceGroup->addProperty( "Axial Position", "Slice Z position.", 80 );
277  m_xPos->setMin( 0 );
278  m_xPos->setMax( 159 );
279  m_yPos->setMin( 0 );
280  m_yPos->setMax( 199 );
281  m_zPos->setMin( 0 );
282  m_zPos->setMax( 159 );
283 
284  m_licGroup = m_properties->addPropertyGroup( "LIC", "LIC properties." );
285 
286  m_useLight = m_licGroup->addProperty( "Use Light", "Check to enable lightning using the Phong model.", false );
287  m_lightIntensity = m_licGroup->addProperty( "Light Intensity", "Define how intense the light should be.", 1.0 );
288  m_lightIntensity->setMin( 0.0 );
289  m_lightIntensity->setMax( 10.0 );
290 
291  m_useEdges = m_licGroup->addProperty( "Edges", "Check to enable blending in edges.", true );
292  m_useEdgesColor = m_licGroup->addProperty( "Edge Color", "Define the color of the edges.", defaultColor::WHITE );
293  m_useEdgesStep = m_licGroup->addProperty( "Edge Step", "Define the steepness of the blend function between color and edge color.", 1.0 );
294  m_useEdgesStep->setMin( 0.0 );
295  m_useEdgesStep->setMax( 10.0 );
296 
297  m_cmapRatio = m_licGroup->addProperty( "Ratio Colormap to LIC", "Blending ratio between LIC and colormap.", 0.5 );
298  m_cmapRatio->setMin( 0.0 );
299  m_cmapRatio->setMax( 1.0 );
300 
301  m_advancedLicGroup = m_properties->addPropertyGroup( "Advanced", "More advanced LIC properties." );
302  // show hud?
303  m_showHUD = m_advancedLicGroup->addProperty( "Show HUD", "Check to enable the debugging texture HUD.", false );
304  m_noiseRes = m_advancedLicGroup->addProperty( "Noise Resolution", "The noise is of 128^3 pixels size. This scaler allows "
305  "modification of this size.", 1.5 );
306  m_noiseRes->setMin( 0 );
307  m_noiseRes->setMax( 100 );
308 
309  m_numIters = m_advancedLicGroup->addProperty( "Number of Iterations", "How much iterations along a streamline should be done per frame.",
310  30 );
311  m_numIters->setMin( 1 );
312  m_numIters->setMax( 1000 );
313 
314  m_projectionAngleThreshold = m_advancedLicGroup->addProperty( "Projection Angle Threshold", "This defines the threshold of the angle between "
315  "tangential plane of the surface and the vector which is going to be projected. You can adjust how steep a vector can be before it is "
316  "clipped and NOT projected. Note: all vectors with an angle below this threshold are projected but linearly reduced in influence "
317  "depending on the angle.", 90.0 );
318  m_projectionAngleThreshold->setMin( 0.0 );
319  m_projectionAngleThreshold->setMax( 180.0 );
320 
321  m_faClip = m_advancedLicGroup->addProperty( "Clip FA", "Clip by FA", 0.1 );
322  m_faClip->setMin( 0.0 );
323  m_faClip->setMax( 1.0 );
324 
325  // call WModule's initialization
327 }
328 
329 void WMImageSpaceTensorLIC::initOSG( std::shared_ptr< WGridRegular3D > grid, std::shared_ptr< WTriangleMesh > mesh )
330 {
331  // remove the old slices
332  m_output->clear();
333 
334  if( mesh && !m_useSlices->get( true ) )
335  {
336  // we have a mesh and want to use it
337  // create geometry and geode
338  osg::Geometry* surfaceGeometry = new osg::Geometry();
339  osg::ref_ptr< osg::Geode > surfaceGeode = osg::ref_ptr< osg::Geode >( new osg::Geode );
340 
341  surfaceGeometry->setVertexArray( mesh->getVertexArray() );
342  osg::DrawElementsUInt* surfaceElement;
343  surfaceElement = new osg::DrawElementsUInt( osg::PrimitiveSet::TRIANGLES, 0 );
344  std::vector< size_t > tris = mesh->getTriangles();
345  surfaceElement->reserve( tris.size() );
346  for( unsigned int vertId = 0; vertId < tris.size(); ++vertId )
347  {
348  surfaceElement->push_back( tris[vertId] );
349  }
350  surfaceGeometry->addPrimitiveSet( surfaceElement );
351 
352  // normals
353  surfaceGeometry->setNormalArray( mesh->getVertexNormalArray() );
354  surfaceGeometry->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
355 
356  // texture coordinates
357  surfaceGeometry->setTexCoordArray( 0, mesh->getTextureCoordinateArray() );
358 
359  // render
360  surfaceGeode->addDrawable( surfaceGeometry );
361  m_output->insert( surfaceGeode );
362  }
363  else if( !mesh && !m_useSlices->get( true ) )
364  {
365  warnLog() << "No surface connected to input but surface render mode enabled. Nothing rendered.";
366  }
367  else
368  {
369  // create a new geode containing the slices
370  osg::ref_ptr< osg::Node > xSlice = wge::genFinitePlane( grid->getOrigin(), grid->getNbCoordsY() * grid->getDirectionY(),
371  grid->getNbCoordsZ() * grid->getDirectionZ() );
372 
373  osg::ref_ptr< osg::Node > ySlice = wge::genFinitePlane( grid->getOrigin(), grid->getNbCoordsX() * grid->getDirectionX(),
374  grid->getNbCoordsZ() * grid->getDirectionZ() );
375 
376  osg::ref_ptr< osg::Node > zSlice = wge::genFinitePlane( grid->getOrigin(), grid->getNbCoordsX() * grid->getDirectionX(),
377  grid->getNbCoordsY() * grid->getDirectionY() );
378 
379  // disable picking
380  xSlice->setName( "_X SLice" );
381  ySlice->setName( "_Y SLice" );
382  zSlice->setName( "_Z SLice" );
383 
384  // The movement of the slice is done in the shader. An alternative would be WGELinearTranslationCallback but there, the needed matrix is
385  // not available in the shader
386  osg::StateSet* ss = xSlice->getOrCreateStateSet();
387  ss->addUniform( new WGEPropertyUniform< WPropInt >( "u_vertexShift", m_xPos ) );
388  ss->addUniform( new osg::Uniform( "u_vertexShiftDirection", grid->getDirectionX().as< osg::Vec3f >() ) ); // the axis to move along
389  ss = ySlice->getOrCreateStateSet();
390  ss->addUniform( new WGEPropertyUniform< WPropInt >( "u_vertexShift", m_yPos ) );
391  ss->addUniform( new osg::Uniform( "u_vertexShiftDirection", grid->getDirectionY().as< osg::Vec3f >() ) ); // the axis to move along
392  ss = zSlice->getOrCreateStateSet();
393  ss->addUniform( new WGEPropertyUniform< WPropInt >( "u_vertexShift", m_zPos ) );
394  ss->addUniform( new osg::Uniform( "u_vertexShiftDirection", grid->getDirectionZ().as< osg::Vec3f >() ) ); // the axis to move along
395 
396  // set callbacks for en-/disabling the nodes
397  xSlice->addUpdateCallback( new WGENodeMaskCallback( m_showonX ) );
398  ySlice->addUpdateCallback( new WGENodeMaskCallback( m_showonY ) );
399  zSlice->addUpdateCallback( new WGENodeMaskCallback( m_showonZ ) );
400 
401  // disable culling.
402  xSlice->setCullingActive( false );
403  ySlice->setCullingActive( false );
404  zSlice->setCullingActive( false );
405 
406  // add the transformation nodes to the output group
407  m_output->insert( xSlice );
408  m_output->insert( ySlice );
409  m_output->insert( zSlice );
410  m_output->dirtyBound();
411  }
412 }
413 
415 {
416  // get notified about data changes
417  m_moduleState.setResetable( true, true );
418  m_moduleState.add( m_evec1In->getDataChangedCondition() );
419  m_moduleState.add( m_evec2In->getDataChangedCondition() );
420  m_moduleState.add( m_evalsIn->getDataChangedCondition() );
421  m_moduleState.add( m_meshIn->getDataChangedCondition() );
422  // Remember the condition provided to some properties in properties()? The condition can now be used with this condition set.
424 
425  /////////////////////////////////////////////////////////////////////////////////////////////////////////
426  // Preparation 1: create noise texture
427  /////////////////////////////////////////////////////////////////////////////////////////////////////////
428 
429  // we need a noise texture with a sufficient resolution. Create it.
430  const size_t resX = 128;
431  float rdIrr = 0.0;
432  float rdSpot = 0.689;
433  unsigned int rdIter = 4000;
434  //unsigned char* rdField = new unsigned char[ resX * resX ];
435 
436  osg::ref_ptr< osg::Image > ima = new osg::Image;
437  ima->allocateImage( resX, resX, 1, GL_LUMINANCE, GL_UNSIGNED_BYTE );
438  genReactionDiffusion( ima->data(), resX, resX, resX, resX, rdIter, rdSpot, rdIrr );
439 
440  WGETexture2D::SPtr randTexture( new WGETexture2D( ima ) );
441  randTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT );
442  randTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT );
443 
444  // done.
445  ready();
446 
447  /////////////////////////////////////////////////////////////////////////////////////////////////////////
448  // Preparation 2: initialize offscreen renderer and hardwire it
449  /////////////////////////////////////////////////////////////////////////////////////////////////////////
450 
451  // create the root node for all the geometry
452  m_root = osg::ref_ptr< WGEManagedGroupNode > ( new WGEManagedGroupNode( m_active ) );
453 
454  // root geometry node for the offscreen path
455  m_output = osg::ref_ptr< WGEGroupNode > ( new WGEGroupNode() );
456 
457  // the WGEOffscreenRenderNode manages each of the render-passes for us
458  osg::ref_ptr< WGEOffscreenRenderNode > offscreen = new WGEOffscreenRenderNode(
459  WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera()
460  );
461 
462  // allow en-/disabling the HUD:
463  offscreen->getTextureHUD()->addUpdateCallback( new WGENodeMaskCallback( m_showHUD ) );
464 
465  // setup all the passes needed for image space advection
466  osg::ref_ptr< WGEShader > transformationShader = new WGEShader( "WMImageSpaceTensorLIC-Transformation", m_localPath );
467 
468  // This should not be needed. But somehow, the u_vertexShift uniforms does not get removed by OSG when switching to mesh.
469  transformationShader->addPreprocessor( WGEShaderPreprocessor::SPtr(
470  new WGEShaderPropertyDefineOptions< WPropBool >( m_useSlices, "VERTEXSHIFT_DISABLED", "VERTEXSHIFT_ENABLED" )
471  ) );
472 
473  osg::ref_ptr< WGEOffscreenRenderPass > transformation = offscreen->addGeometryRenderPass(
474  m_output,
475  transformationShader,
476  "Transformation"
477  );
478  transformation->bind( randTexture, 3 );
479  // apply colormapping to transformation
480  WGEColormapping::apply( transformation, transformationShader, 4 );
481 
482  osg::ref_ptr< WGEShader > edgeShader = new WGEShader( "WMImageSpaceTensorLIC-Edge", m_localPath );
483  osg::ref_ptr< WGEOffscreenRenderPass > edgeDetection = offscreen->addTextureProcessingPass(
484  edgeShader,
485  "Edge Detection"
486  );
487 
488  // we use two advection passes per frame as the input A of the first produces the output B whereas the second pass uses B as input and
489  // produces A as output. This way we can use A as input for the next step (clipping and blending).
490  osg::ref_ptr< WGEOffscreenRenderPass > advection = offscreen->addTextureProcessingPass(
491  new WGEShader( "WMImageSpaceTensorLIC-Advection", m_localPath ),
492  "Advection"
493  );
494 
495  // finally, put it back on screen, clip it, color it and apply depth buffer to on-screen buffer
496  osg::ref_ptr< WGEOffscreenRenderPass > clipBlend = offscreen->addTextureProcessingPass(
497  new WGEShader( "WMImageSpaceTensorLIC-ClipBlend", m_localPath ),
498  "Clip & Blend"
499  );
500 
501  // finally, put it back on screen, clip it, color it and apply depth buffer to on-screen buffer
502  osg::ref_ptr< WGEOffscreenRenderPass > postprocessing = offscreen->addFinalOnScreenPass(
503  new WGEShader( "WMImageSpaceTensorLIC-Postprocessing", m_localPath ),
504  "Postprocessing"
505  );
506 
507  // hardwire the textures to use for each pass:
508 
509  // Transformation Pass, needs Geometry
510  // * Creates 2D projected Vectors in RG and BA
511  // * Lighting and projected noise in out2.rg, depth in b
512  // * Depth
513  osg::ref_ptr< osg::Texture2D > transformationOut1 = transformation->attach( WGECamera::COLOR_BUFFER0 );
514  osg::ref_ptr< osg::Texture2D > transformationOut2 = transformation->attach( WGECamera::COLOR_BUFFER1 );
515  osg::ref_ptr< osg::Texture2D > transformationOut3 = transformation->attach( WGECamera::COLOR_BUFFER2 );
516  osg::ref_ptr< osg::Texture2D > transformationColormapped = transformation->attach( WGECamera::COLOR_BUFFER3 );
517  osg::ref_ptr< osg::Texture2D > transformationDepth = transformation->attach( WGECamera::DEPTH_BUFFER );
518  // and some uniforms
519  transformation->addUniform( new WGEPropertyUniform< WPropDouble >( "u_noiseResoultuion", m_noiseRes ) );
520  transformation->addUniform( new WGEPropertyUniform< WPropDouble >( "u_projectionAngleThreshold", m_projectionAngleThreshold ) );
521  transformation->addUniform( new WGEPropertyUniform< WPropDouble >( "u_clipFA", m_faClip ) );
522 
523  // Edge Detection Pass, needs Depth as input
524  // * Edges in R, noise in G
525  osg::ref_ptr< osg::Texture2D > edgeDetectionOut1 = edgeDetection->attach( WGECamera::COLOR_BUFFER0 );
526  edgeDetection->bind( transformationDepth, 0 );
527  edgeDetection->bind( transformationOut2, 1 );
528 
529  // Advection Pass, needs edges and projected vectors as well as noise texture
530  // * Advected noise in luminance channel
531  osg::ref_ptr< osg::Texture2D > advectionOutA = advection->attach( WGECamera::COLOR_BUFFER0, GL_RGBA );
532  advection->bind( transformationOut1, 0 );
533  advection->bind( edgeDetectionOut1, 1 );
534 
535  // advection needs some uniforms controlled by properties
536  osg::ref_ptr< osg::Uniform > numIters = new WGEPropertyUniform< WPropInt >( "u_numIter", m_numIters );
537  advection->addUniform( numIters );
538 
539  osg::Uniform* animationTime( new osg::Uniform( "u_animation", 0 ) );
540  animationTime->setUpdateCallback( new WGEShaderAnimationCallback() );
541  advection->addUniform( animationTime );
542 
543  // provide the Gbuffer input, with several mipmap levels
544  advectionOutA->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
545  edgeDetectionOut1->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
546  transformationColormapped->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR );
547 
548  // Final clipping and blending phase, needs Advected Noise, Edges, Depth and Light
549  osg::ref_ptr< osg::Texture2D > merged = clipBlend->attach( WGECamera::COLOR_BUFFER0, GL_RGBA );
550  clipBlend->bind( advectionOutA, 0 );
551  clipBlend->bind( edgeDetectionOut1, 1 );
552  clipBlend->bind( transformationColormapped, 2 );
553  clipBlend->bind( transformationOut2, 3 );
554  clipBlend->bind( transformationOut3, 4 );
555 
556  // final pass needs some uniforms controlled by properties
557  clipBlend->addUniform( new WGEPropertyUniform< WPropBool >( "u_useEdges", m_useEdges ) );
558  clipBlend->addUniform( new WGEPropertyUniform< WPropColor >( "u_useEdgesColor", m_useEdgesColor ) );
559  clipBlend->addUniform( new WGEPropertyUniform< WPropDouble >( "u_useEdgesStep", m_useEdgesStep ) );
560  clipBlend->addUniform( new WGEPropertyUniform< WPropBool >( "u_useLight", m_useLight ) );
561  clipBlend->addUniform( new WGEPropertyUniform< WPropDouble >( "u_lightIntensity", m_lightIntensity ) );
562  clipBlend->addUniform( new WGEPropertyUniform< WPropDouble >( "u_cmapRatio", m_cmapRatio ) );
563 
564  // The final pass should also blend properly:
565  postprocessing->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
566 
567  // Final clipping and blending phase, needs Advected Noise, Edges, Depth and Light
568  postprocessing->bind( advectionOutA, 0 );
569  postprocessing->bind( edgeDetectionOut1, 1 );
570  postprocessing->bind( transformationColormapped, 2 );
571  postprocessing->bind( transformationOut2, 3 );
572  postprocessing->bind( transformationOut3, 4 );
573  postprocessing->bind( merged, 5 );
574 
575  // final pass needs some uniforms controlled by properties
576  postprocessing->addUniform( new WGEPropertyUniform< WPropBool >( "u_useEdges", m_useEdges ) );
577  postprocessing->addUniform( new WGEPropertyUniform< WPropColor >( "u_useEdgesColor", m_useEdgesColor ) );
578  postprocessing->addUniform( new WGEPropertyUniform< WPropDouble >( "u_useEdgesStep", m_useEdgesStep ) );
579  postprocessing->addUniform( new WGEPropertyUniform< WPropBool >( "u_useLight", m_useLight ) );
580  postprocessing->addUniform( new WGEPropertyUniform< WPropDouble >( "u_lightIntensity", m_lightIntensity ) );
581  postprocessing->addUniform( new WGEPropertyUniform< WPropDouble >( "u_cmapRatio", m_cmapRatio ) );
582 
583  // add everything to root node
584  m_root->insert( offscreen );
585 
586  // Cull proxy. Updated on dataset change
587  osg::ref_ptr< osg::Node > cullProxy;
588 
589  // register scene
590  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_root );
591 
592  /////////////////////////////////////////////////////////////////////////////////////////////////////////
593  // Main loop
594  /////////////////////////////////////////////////////////////////////////////////////////////////////////
595 
596  // main loop
597  while( !m_shutdownFlag() )
598  {
599  debugLog() << "Waiting ...";
601 
602  // woke up since the module is requested to finish?
603  if( m_shutdownFlag() )
604  {
605  break;
606  }
607 
608  // To query whether an input was updated, simply ask the input:
609  bool dataUpdated = m_evec1In->handledUpdate() || m_evec2In->handledUpdate() || m_meshIn->handledUpdate();
610  bool propertyUpdated = m_useSlices->changed();
611  std::shared_ptr< WDataSetVector > dataSetEvec1 = m_evec1In->getData();
612  std::shared_ptr< WDataSetVector > dataSetEvec2 = m_evec2In->getData();
613  std::shared_ptr< WDataSetVector > dataSetEvals = m_evalsIn->getData();
614  std::shared_ptr< WTriangleMesh > mesh = m_meshIn->getData();
615 
616  bool dataValid = ( dataSetEvals && dataSetEvec1 && dataSetEvec2 );
617 
618  // is data valid? If not, remove graphics
619  if( !dataValid )
620  {
621  debugLog() << "Resetting.";
622  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( offscreen );
623  continue;
624  }
625 
626  // something interesting for us?
627  if( dataValid && !dataUpdated && !propertyUpdated )
628  {
629  continue;
630  }
631 
632  // prefer vector dataset if existing
633  std::shared_ptr< WGridRegular3D > grid;
634  // get grid and prepare OSG
635  grid = std::dynamic_pointer_cast< WGridRegular3D >( dataSetEvec1->getGrid() );
636  m_xPos->setMax( grid->getNbCoordsX() - 1 );
637  m_yPos->setMax( grid->getNbCoordsY() - 1 );
638  m_zPos->setMax( grid->getNbCoordsZ() - 1 );
639  initOSG( grid, mesh );
640 
641  // prepare offscreen render chain
642  transformation->bind( dataSetEvec1->getTexture(), 0 );
643  transformation->bind( dataSetEvec2->getTexture(), 1 );
644  transformation->bind( dataSetEvals->getTexture(), 2 );
645 
646  // Update CullProxy when we get new data
647  // add a cull-proxy as we modify the geometry on the GPU
648  WBoundingBox bbox = grid->getVoxelBoundingBox();
649  m_root->remove( cullProxy );
650  cullProxy = wge::generateCullProxy( bbox );
651  m_root->insert( cullProxy );
652  debugLog() << "Done";
653  }
654 
655  // clean up
656  m_root->clear();
657  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_root );
658 }
659 
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
static void apply(osg::ref_ptr< osg::Node > node, WMatrix4d preTransform=WMatrix4d::identity(), osg::ref_ptr< WGEShader > shader=osg::ref_ptr< WGEShader >(), size_t startTexUnit=0)
Apply the colormapping to the specified node.
Class to wrap around the osg Group node and providing a thread safe add/removal mechanism.
Definition: WGEGroupNode.h:48
This class adds some convenience methods to WGEGroupNode.
This callback is useful to en-/disable nodes using the node mask based on properties.
This type of node basically is a convenience class for managing and creating offscreen renderings.
Class implementing a uniform which can be controlled by a property instance.
This is a uniform callback setting the uniform to the current time in milliseconds,...
std::shared_ptr< WGEShaderPreprocessor > SPtr
Shared pointer for this class.
This is a WGEShaderDefineOptions class which additionally uses a property to automatically control th...
Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader.
Definition: WGEShader.h:48
This calls serves a simple purpose: have a texture and its scaling information together which allows ...
Definition: WGETexture.h:53
osg::ref_ptr< WGETexture< TextureType > > SPtr
Convenience type for OSG reference pointer on WGETextures.
Definition: WGETexture.h:63
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
std::shared_ptr< WGraphicsEngine > getGraphicsEngine() const
Returns pointer to currently running instance of graphics engine.
Definition: WKernel.cpp:122
WPropBool m_useLight
indicates whether to use Phong
virtual void connectors()
Initialize the connectors this module is using.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
WPropDouble m_lightIntensity
light intensity
WPropDouble m_cmapRatio
the ratio between colormap and LIC
WPropGroup m_geometryGroup
the group contains several input geometry parameters
WMImageSpaceTensorLIC()
Default constructor.
WPropDouble m_faClip
Clipp according to FA.
WPropInt m_yPos
y position of the slice
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
WPropBool m_showonX
indicates whether the vector should be shown on slice X
virtual void moduleMain()
Entry point after loading the module.
WPropInt m_xPos
x position of the slice
std::shared_ptr< WModuleInputData< WDataSetVector > > m_evec2In
The input connector containing the DTI field whose derived field is used for LIC.
WPropBool m_showonZ
indicates whether the vector should be shown on slice Z
void initOSG(std::shared_ptr< WGridRegular3D > grid, std::shared_ptr< WTriangleMesh > mesh)
Initializes the needed geodes, transformations and vertex arrays.
WPropDouble m_useEdgesStep
define the steepness of the step function used to blend in the edge color.
WPropGroup m_advancedLicGroup
The group for more advanced LIC features.
virtual ~WMImageSpaceTensorLIC()
Destructor.
WPropDouble m_noiseRes
The resolution scaling for the noise.
std::shared_ptr< WModuleInputData< WDataSetVector > > m_evalsIn
The input connector containing the DTI field whose derived field is used for LIC.
virtual const std::string getDescription() const
Gives back a description of this module.
osg::ref_ptr< WGEGroupNode > m_output
The Geode containing all the slices and the mesh.
osg::ref_ptr< WGEManagedGroupNode > m_root
Scene root node.
std::shared_ptr< WModuleInputData< WDataSetVector > > m_evec1In
The input connector containing the DTI field whose derived field is used for LIC.
WPropInt m_numIters
the number of iterations done per frame
WPropBool m_useSlices
indicates whether the vector should be shown on slices or input geometry
WPropBool m_showonY
indicates whether the vector should be shown on slice Y
WPropBool m_useEdges
indicates whether to show the edges
WPropColor m_useEdgesColor
indicated whether the edges (if enabled) should be black or white or green or red or ....
virtual const std::string getName() const
Gives back the name of this module.
std::shared_ptr< WModuleInputData< WTriangleMesh > > m_meshIn
The input containing the surface on which the LIC should be applied on.
WPropGroup m_sliceGroup
the group contains several slice properties
WPropDouble m_projectionAngleThreshold
the angle threshold between surface and vector before clipping the vector.
WPropBool m_showHUD
indicates whether to show the texture HUD
WPropInt m_zPos
z position of the slice
WPropGroup m_licGroup
the group contains several LIC properties
virtual void properties()
Initialize the properties for this module.
static PtrType createAndAdd(std::shared_ptr< WModule > module, std::string name="", std::string description="")
Convenience method to create a new instance of this in data connector with proper type and add it to ...
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
boost::filesystem::path m_localPath
The path where the module binary resides in.
Definition: WModule.h:734
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
wlog::WStreamedLogger warnLog() const
Logger instance for comfortable warning- logs.
Definition: WModule.cpp:580
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
WPropBool m_active
True whenever the module should be active.
Definition: WModule.h:723
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
osg::ref_ptr< osg::Node > generateCullProxy(const WBoundingBox &bbox)
Generate a proxy cube, which ensures OSG does proper near-far plane calculation and culling.
Definition: WGEUtils.cpp:236
osg::ref_ptr< osg::Geode > genFinitePlane(double xSize, double ySize, const WPlane &p, const WColor &color=WColor(0.0, 0.7, 0.7, 1.0), bool border=false)
Generates a geode out of a Plane with a fixed size in direction of the vectors which span that plane.