OpenWalnut  1.5.0dev
WMIsosurfaceRaytracer.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 <memory>
26 #include <string>
27 #include <utility>
28 
29 #include <osg/Geode>
30 #include <osg/Group>
31 #include <osg/Material>
32 #include <osg/ShapeDrawable>
33 #include <osg/StateAttribute>
34 
35 #include "WMIsosurfaceRaytracer.h"
36 #include "WMIsosurfaceRaytracer.xpm"
37 #include "core/common/WColor.h"
38 #include "core/common/WPropertyHelper.h"
39 #include "core/dataHandler/WDataSetScalar.h"
40 #include "core/dataHandler/WDataSetVector.h"
41 #include "core/dataHandler/WDataTexture3D.h"
42 #include "core/graphicsEngine/WGEColormapping.h"
43 #include "core/graphicsEngine/WGEGeodeUtils.h"
44 #include "core/graphicsEngine/WGEManagedGroupNode.h"
45 #include "core/graphicsEngine/WGERequirement.h"
46 #include "core/graphicsEngine/WGETextureUtils.h"
47 #include "core/graphicsEngine/WGEUtils.h"
48 #include "core/graphicsEngine/callbacks/WGENodeMaskCallback.h"
49 #include "core/graphicsEngine/postprocessing/WGEPostprocessingNode.h"
50 #include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
51 #include "core/graphicsEngine/shaders/WGEShader.h"
52 #include "core/graphicsEngine/shaders/WGEShaderDefineOptions.h"
53 #include "core/graphicsEngine/shaders/WGEShaderPropertyDefine.h"
54 #include "core/graphicsEngine/shaders/WGEShaderPropertyDefineOptions.h"
55 #include "core/kernel/WKernel.h"
56 
57 // This line is needed by the module loader to actually find your module.
58 W_LOADABLE_MODULE( WMIsosurfaceRaytracer )
59 
61  WModule()
62 {
63  // Initialize members
64 }
65 
67 {
68  // Cleanup!
69 }
70 
71 std::shared_ptr< WModule > WMIsosurfaceRaytracer::factory() const
72 {
73  return std::shared_ptr< WModule >( new WMIsosurfaceRaytracer() );
74 }
75 
77 {
78  return isosurfaceraytracer_xpm;
79 }
80 
81 const std::string WMIsosurfaceRaytracer::getName() const
82 {
83  // Specify your module name here. This name must be UNIQUE!
84  return "Isosurface Raytracer";
85 }
86 
87 const std::string WMIsosurfaceRaytracer::getDescription() const
88 {
89  // Specify your module description here. Be detailed. This text is read by the user.
90  return "This module shows a fast raytraced isosurface of the specified scalar dataset.";
91 }
92 
94 {
95  // DVR needs one input: the scalar dataset
96  m_input = std::shared_ptr< WModuleInputData < WDataSetScalar > >(
97  new WModuleInputData< WDataSetScalar >( shared_from_this(), "in", "The scalar dataset shown using isosurface." )
98  );
99 
100  // As properties, every connector needs to be added to the list of connectors.
102 
103  // Optional: the gradient field
104  m_gradients = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "gradients", "The gradient field of the dataset to display" );
105 
106  // call WModules initialization
108 }
109 
111 {
112  // Initialize the properties
113  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
114 
115  m_isoValue = m_properties->addProperty( "Isovalue", "The isovalue used whenever the isosurface Mode is turned on.",
116  128.0 );
117 
118  m_isoColor = m_properties->addProperty( "Iso color", "The color to blend the isosurface with.", WColor( 1.0, 1.0, 1.0, 1.0 ),
119  m_propCondition );
120 
121  m_stepCount = m_properties->addProperty( "Step count", "The number of steps to walk along the ray during raycasting. A low value "
122  "may cause artifacts whilst a high value slows down rendering.", 250 );
123  m_stepCount->setMin( 1 );
124  m_stepCount->setMax( 1000 );
125 
126  m_epsilon = m_properties->addProperty( "Epsilon", "The value defines the precision of iso-value checking. The lower the "
127  "value, the higher the precision.", 0.1 );
128  m_epsilon->setMin( 0.0 );
129  m_epsilon->setMax( 1.0 );
130 
131  m_alpha = m_properties->addProperty( "Opacity %", "The opacity in %. Transparency = 1 - Opacity.", 1.0 );
132  m_alpha->setMin( 0.0 );
133  m_alpha->setMax( 1.0 );
134 
135  m_colormapRatio = m_properties->addProperty( "Colormap ratio", "The intensity of the colormap in contrast to surface shading.", 0.5 );
136  m_colormapRatio->setMin( 0.0 );
137  m_colormapRatio->setMax( 1.0 );
138 
139  m_phongShading = m_properties->addProperty( "Phong shading", "If enabled, Phong shading gets applied on a per-pixel basis.", true );
140 
141  m_phongNoSpec = m_properties->addProperty( "No specular highlights", "Disables specular highlights on the surface. This is especially "
142  "useful when these highlights tamper with the colormap.", false );
143 
144  m_stochasticJitter = m_properties->addProperty( "Stochastic jitter", "Improves image quality at low sampling rates but introduces slight "
145  "noise effect.", true );
146 
147  m_borderClip = m_properties->addProperty( "Border clip", "If enabled, a certain area on the volume boundary can be clipped. This is useful "
148  "for noise and non-peeled data but will consume a lot of GPU power.", false );
149 
150  m_borderClipDistance = m_properties->addProperty( "Border clip distance", "The distance that should be ignored.", 0.05 );
151  m_borderClipDistance->setMin( 0.0 );
152  m_borderClipDistance->setMax( 1.0 );
153 
155 }
156 
158 {
159  m_requirements.push_back( new WGERequirement() );
160 }
161 
163 {
164  m_shader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMIsosurfaceRaytracer", m_localPath ) );
165  m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
166  new WGEShaderPropertyDefineOptions< WPropBool >( m_phongNoSpec, "PHONGSHADING_NOSPECULAR_DISABLED", "PHONGSHADING_NOSPECULAR_ENABLED" ) )
167  );
168  m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
169  new WGEShaderPropertyDefineOptions< WPropBool >( m_stochasticJitter, "STOCHASTICJITTER_DISABLED", "STOCHASTICJITTER_ENABLED" ) )
170  );
171  m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
172  new WGEShaderPropertyDefine< WPropDouble >( "ISO_EPSILON", m_epsilon ) )
173  );
174  m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
175  new WGEShaderPropertyDefineOptions< WPropBool >( m_phongShading, "PHONGSHADING_DISABLED", "PHONGSHADING_ENABLED" ) )
176  );
177  WGEShaderDefineSwitch::SPtr gradTexEnableDefine = m_shader->setDefine( "GRADIENTTEXTURE_ENABLED" );
178  m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
179  new WGEShaderPropertyDefineOptions< WPropBool >( m_borderClip, "BORDERCLIP_DISABLED", "BORDERCLIP_ENABLED" ) )
180  );
181 
182  // let the main loop awake if the data changes or the properties changed.
183  m_moduleState.setResetable( true, true );
184  m_moduleState.add( m_input->getDataChangedCondition() );
185  m_moduleState.add( m_gradients->getDataChangedCondition() );
187 
188  // Signal ready state.
189  ready();
190  debugLog() << "Module is now ready.";
191 
192  // create the root node containing the transformation and geometry
193  osg::ref_ptr< WGEGroupNode > rootNode = new WGEGroupNode();
194 
195  // create the post-processing node which actually does the nice stuff to the rendered image
196  osg::ref_ptr< WGEPostprocessingNode > postNode = new WGEPostprocessingNode(
197  WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera()
198  );
199  postNode->addUpdateCallback( new WGENodeMaskCallback( m_active ) ); // disable the postNode with m_active
200 
201  // provide the properties of the post-processor to the user
202  m_properties->addProperty( postNode->getProperties() );
203 
204  // insert it
205  postNode->insert( rootNode, m_shader );
206  bool postNodeInserted = false;
207 
208  // Normally, you will have a loop which runs as long as the module should not shutdown. In this loop you can react on changing data on input
209  // connectors or on changed in your properties.
210  debugLog() << "Entering main loop";
211  while( !m_shutdownFlag() )
212  {
213  // Now, the moduleState variable comes into play. The module can wait for the condition, which gets fired whenever the input receives data
214  // or an property changes. The main loop now waits until something happens.
215  debugLog() << "Waiting ...";
217 
218  // quit if requested
219  if( m_shutdownFlag() )
220  {
221  break;
222  }
223 
224  // was there an update?
225  bool dataUpdated = m_input->updated() || m_gradients->updated();
226  std::shared_ptr< WDataSetScalar > dataSet = m_input->getData();
227  bool dataValid = ( dataSet != NULL );
228 
229  // valid data available?
230  if( !dataValid )
231  {
232  // remove renderings if no data is available anymore
233  rootNode->clear();
234  }
235 
236  // m_isoColor or shading changed
237  if( m_isoColor->changed() )
238  {
239  // a new color requires the proxy geometry to be rebuild as we store it as color in this geometry
240  dataUpdated = true;
241  }
242 
243  // As the data has changed, we need to recreate the texture.
244  if( dataUpdated && dataValid )
245  {
246  debugLog() << "Data changed. Uploading new data as texture.";
247 
248  m_isoValue->setMin( dataSet->getTexture()->minimum()->get() );
249  m_isoValue->setMax( dataSet->getTexture()->scale()->get() + dataSet->getTexture()->minimum()->get() );
250  m_isoValue->setRecommendedValue( dataSet->getTexture()->minimum()->get() + ( 0.5 * dataSet->getTexture()->scale()->get() ) );
251 
252  // First, grab the grid
253  std::shared_ptr< WGridRegular3D > grid = std::dynamic_pointer_cast< WGridRegular3D >( dataSet->getGrid() );
254  if( !grid )
255  {
256  errorLog() << "The dataset does not provide a regular grid. Ignoring dataset.";
257  continue;
258  }
259 
260  // use the OSG Shapes, create unit cube
261  WBoundingBox bb( WPosition( 0.0, 0.0, 0.0 ),
262  WPosition( grid->getNbCoordsX() - 1, grid->getNbCoordsY() - 1, grid->getNbCoordsZ() - 1 ) );
263  osg::ref_ptr< osg::Node > cube = wge::generateSolidBoundingBoxNode( bb, m_isoColor->get( true ) );
264  cube->asTransform()->getChild( 0 )->setName( "_DVR Proxy Cube" ); // Be aware that this name is used in the pick handler.
265  // because of the underscore in front it won't be picked
266  // we also set the grid's transformation here
267  rootNode->setMatrix( static_cast< WMatrix4d >( grid->getTransform() ) );
268 
269  // bind the texture to the node
270  osg::StateSet* rootState = cube->getOrCreateStateSet();
271 
272  // enable transparency
273  wge::enableTransparency( cube );
274 
275  // bind the data texture
276  osg::ref_ptr< WGETexture3D > texture3D = dataSet->getTexture();
277  texture3D->bind( cube );
278 
279  ////////////////////////////////////////////////////////////////////////////////////////////////////
280  // setup all those uniforms and additional textures
281  ////////////////////////////////////////////////////////////////////////////////////////////////////
282 
283  rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_isovalue", m_isoValue ) );
284  rootState->addUniform( new WGEPropertyUniform< WPropInt >( "u_steps", m_stepCount ) );
285  rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_alpha", m_alpha ) );
286  rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_colormapRatio", m_colormapRatio ) );
287  rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_borderClipDistance", m_borderClipDistance ) );
288  // Stochastic jitter?
289  const size_t size = 64;
290  osg::ref_ptr< WGETexture2D > randTex = wge::genWhiteNoiseTexture( size, size, 1 );
291  wge::bindTexture( cube, randTex, 1 );
292 
293  // if there is a gradient field available -> apply as texture too
294  std::shared_ptr< WDataSetVector > gradients = m_gradients->getData();
295  if( gradients )
296  {
297  debugLog() << "Uploading specified gradient field.";
298 
299  // bind the texture to the node
300  osg::ref_ptr< WDataTexture3D > gradTexture3D = gradients->getTexture();
301  wge::bindTexture( cube, gradTexture3D, 2, "u_gradients" );
302  gradTexEnableDefine->setActive( true );
303  }
304  else
305  {
306  gradTexEnableDefine->setActive( false ); // disable gradient texture
307  }
308  WGEColormapping::apply( cube, grid->getTransformationMatrix(), m_shader, 3 );
309 
310  // update node
311  debugLog() << "Adding new rendering.";
312  rootNode->clear();
313  rootNode->insert( cube );
314  // insert root node if needed. This way, we ensure that the root node gets added only if the proxy cube has been added AND the bbox
315  // can be calculated properly by the OSG to ensure the proxy cube is centered in the scene if no other item has been added earlier.
316  if( !postNodeInserted )
317  {
318  postNodeInserted = true;
319  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( postNode );
320  }
321  }
322  }
323 
324  // At this point, the container managing this module signalled to shutdown. The main loop has ended and you should clean up. Always remove
325  // allocated memory and remove all OSG nodes.
326  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( postNode );
327 }
328 
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 callback is useful to en-/disable nodes using the node mask based on properties.
This class enables you to add arbitrary nodes that get post-processed in screen space.
Class implementing a uniform which can be controlled by a property instance.
This requirement ensures an up and running WGE.
std::shared_ptr< WGEShaderDefine< ValueType > > SPtr
Shared pointer for this class.
std::shared_ptr< WGEShaderPreprocessor > SPtr
Shared pointer for this class.
This is a WGEShaderDefineOptions class which additionally uses a property to automatically control th...
This class is able to provide arbitrary values as define statements in GLSL code.
Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader.
Definition: WGEShader.h:48
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
This module builds the base for fast raytracing of isosurfacesin OpenWalnut.
std::shared_ptr< WModuleInputData< WDataSetScalar > > m_input
An input connector used to get datasets from other modules.
osg::ref_ptr< WGEShader > m_shader
the DVR shader.
virtual void properties()
Initialize the properties for this module.
virtual ~WMIsosurfaceRaytracer()
Destructor.
WPropDouble m_epsilon
The numeric precision used for iso-checking.
virtual void requirements()
Initialize requirements for this module.
WPropDouble m_isoValue
The Isovalue used in the case m_isoSurface is true.
virtual const std::string getName() const
Gives back the name of this module.
WPropBool m_phongNoSpec
Some special coloring mode for disabling specular hightlights.
virtual const std::string getDescription() const
Gives back a description of this module.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
WPropColor m_isoColor
The color used when in isosurface mode for blending.
WPropBool m_stochasticJitter
If true, the ray-tracer uses stochastic jitter to improve image quality.
WPropDouble m_alpha
The alpha transparency used for the rendering.
WPropDouble m_borderClipDistance
The distance used for clipping.
std::shared_ptr< WModuleInputData< WDataSetVector > > m_gradients
The gradient field input.
WMIsosurfaceRaytracer()
Default constructor.
WPropBool m_phongShading
If true, per-pixel-phong shading is applied to the surface.
WPropInt m_stepCount
The number of steps to walk along the ray.
virtual void connectors()
Initialize the connectors this module is using.
WPropBool m_borderClip
If true, a certain border area can be clipped.
WPropDouble m_colormapRatio
The ratio between colormap and normal surface color.
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...
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
virtual void moduleMain()
Entry point after loading the module.
Class offering an instantiate-able data connection between modules.
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
Requirements m_requirements
The list of requirements.
Definition: WModule.h:754
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
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
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
wlog::WStreamedLogger errorLog() const
Logger instance for comfortable error logging.
Definition: WModule.cpp:570
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
This only is a 3d double vector.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
void enableTransparency(osg::ref_ptr< osg::Node > node)
Enable transparency for the given node.
Definition: WGEUtils.cpp:215
osg::ref_ptr< WGETexture< osg::Texture1D > > genWhiteNoiseTexture(size_t sizeX, size_t channels)
This generates an 1D texture only containing white noise in its channels.
void bindTexture(osg::ref_ptr< osg::Node > node, osg::ref_ptr< WDataTexture3D > texture, size_t unit=0, std::string prefix="")
Binds the specified texture to the specified unit.
osg::ref_ptr< osg::Node > generateSolidBoundingBoxNode(const WBoundingBox &bb, const WColor &color, bool threeDTexCoords=true)
Generates an OSG node for the specified bounding box.