1
//---------------------------------------------------------------------------
3
// Project: OpenWalnut ( http://www.openwalnut.org )
5
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6
// For more information see http://www.openwalnut.org/copying
8
// This file is part of OpenWalnut.
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.
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.
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/>.
23
//---------------------------------------------------------------------------
30
#include <osg/Material>
31
#include <osg/ShapeDrawable>
32
#include <osg/StateAttribute>
34
#include "core/common/WColor.h"
35
#include "core/common/WPropertyHelper.h"
36
#include "core/dataHandler/WDataSetScalar.h"
37
#include "core/dataHandler/WDataSetVector.h"
38
#include "core/dataHandler/WDataTexture3D.h"
39
#include "core/graphicsEngine/WGEColormapping.h"
40
#include "core/graphicsEngine/WGEGeodeUtils.h"
41
#include "core/graphicsEngine/WGEManagedGroupNode.h"
42
#include "core/graphicsEngine/WGEUtils.h"
43
#include "core/graphicsEngine/WGETextureUtils.h"
44
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
45
#include "core/graphicsEngine/shaders/WGEShader.h"
46
#include "core/graphicsEngine/shaders/WGEShaderDefineOptions.h"
47
#include "core/graphicsEngine/shaders/WGEShaderPropertyDefineOptions.h"
48
#include "core/graphicsEngine/shaders/WGEShaderPropertyDefine.h"
49
#include "core/graphicsEngine/postprocessing/WGEPostprocessingNode.h"
50
#include "core/graphicsEngine/WGERequirement.h"
51
#include "core/graphicsEngine/callbacks/WGENodeMaskCallback.h"
52
#include "core/kernel/WKernel.h"
53
#include "WMIsosurfaceRaytracer.xpm"
54
#include "WMIsosurfaceRaytracer.h"
56
// This line is needed by the module loader to actually find your module.
57
W_LOADABLE_MODULE( WMIsosurfaceRaytracer )
59
WMIsosurfaceRaytracer::WMIsosurfaceRaytracer():
65
WMIsosurfaceRaytracer::~WMIsosurfaceRaytracer()
70
boost::shared_ptr< WModule > WMIsosurfaceRaytracer::factory() const
72
return boost::shared_ptr< WModule >( new WMIsosurfaceRaytracer() );
75
const char** WMIsosurfaceRaytracer::getXPMIcon() const
77
return isosurfaceraytracer_xpm;
80
const std::string WMIsosurfaceRaytracer::getName() const
82
// Specify your module name here. This name must be UNIQUE!
83
return "Isosurface Raytracer";
86
const std::string WMIsosurfaceRaytracer::getDescription() const
88
// Specify your module description here. Be detailed. This text is read by the user.
89
return "This module shows a fast raytraced isosurface of the specified scalar dataset.";
92
void WMIsosurfaceRaytracer::connectors()
94
// DVR needs one input: the scalar dataset
95
m_input = boost::shared_ptr< WModuleInputData < WDataSetScalar > >(
96
new WModuleInputData< WDataSetScalar >( shared_from_this(), "in", "The scalar dataset shown using isosurface." )
99
// As properties, every connector needs to be added to the list of connectors.
100
addConnector( m_input );
102
// Optional: the gradient field
103
m_gradients = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "gradients", "The gradient field of the dataset to display" );
105
// call WModules initialization
106
WModule::connectors();
109
void WMIsosurfaceRaytracer::properties()
111
// Initialize the properties
112
m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
114
m_isoValue = m_properties->addProperty( "Isovalue", "The isovalue used whenever the isosurface Mode is turned on.",
117
m_isoColor = m_properties->addProperty( "Iso color", "The color to blend the isosurface with.", WColor( 1.0, 1.0, 1.0, 1.0 ),
120
m_stepCount = m_properties->addProperty( "Step count", "The number of steps to walk along the ray during raycasting. A low value "
121
"may cause artifacts whilst a high value slows down rendering.", 250 );
122
m_stepCount->setMin( 1 );
123
m_stepCount->setMax( 1000 );
125
m_epsilon = m_properties->addProperty( "Epsilon", "The value defines the precision of iso-value checking. The lower the "
126
"value, the higher the precision.", 0.1 );
127
m_epsilon->setMin( 0.0 );
128
m_epsilon->setMax( 1.0 );
130
m_alpha = m_properties->addProperty( "Opacity %", "The opacity in %. Transparency = 1 - Opacity.", 1.0 );
131
m_alpha->setMin( 0.0 );
132
m_alpha->setMax( 1.0 );
134
m_colormapRatio = m_properties->addProperty( "Colormap ratio", "The intensity of the colormap in contrast to surface shading.", 0.5 );
135
m_colormapRatio->setMin( 0.0 );
136
m_colormapRatio->setMax( 1.0 );
138
m_phongShading = m_properties->addProperty( "Phong shading", "If enabled, Phong shading gets applied on a per-pixel basis.", true );
140
m_cortexMode = m_properties->addProperty( "Emphasize cortex", "Emphasize the cortex while inner parts ar not that well lighten.", false );
142
m_stochasticJitter = m_properties->addProperty( "Stochastic jitter", "Improves image quality at low sampling rates but introduces slight "
143
"noise effect.", true );
145
m_borderClip = m_properties->addProperty( "Border clip", "If enabled, a certain area on the volume boundary can be clipped. This is useful "
146
"for noise and non-peeled data but will consume a lot of GPU power.", false );
148
m_borderClipDistance = m_properties->addProperty( "Border clip distance", "The distance that should be ignored.", 0.05 );
149
m_borderClipDistance->setMin( 0.0 );
150
m_borderClipDistance->setMax( 0.1 );
152
WModule::properties();
155
void WMIsosurfaceRaytracer::requirements()
157
m_requirements.push_back( new WGERequirement() );
160
void WMIsosurfaceRaytracer::moduleMain()
162
m_shader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMIsosurfaceRaytracer", m_localPath ) );
163
m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
164
new WGEShaderPropertyDefineOptions< WPropBool >( m_cortexMode, "CORTEXMODE_DISABLED", "CORTEXMODE_ENABLED" ) )
166
m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
167
new WGEShaderPropertyDefineOptions< WPropBool >( m_stochasticJitter, "STOCHASTICJITTER_DISABLED", "STOCHASTICJITTER_ENABLED" ) )
169
m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
170
new WGEShaderPropertyDefine< WPropDouble >( "ISO_EPSILON", m_epsilon ) )
172
m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
173
new WGEShaderPropertyDefineOptions< WPropBool >( m_phongShading, "PHONGSHADING_DISABLED", "PHONGSHADING_ENABLED" ) )
175
WGEShaderDefineSwitch::SPtr gradTexEnableDefine = m_shader->setDefine( "GRADIENTTEXTURE_ENABLED" );
176
m_shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
177
new WGEShaderPropertyDefineOptions< WPropBool >( m_borderClip, "BORDERCLIP_DISABLED", "BORDERCLIP_ENABLED" ) )
180
// let the main loop awake if the data changes or the properties changed.
181
m_moduleState.setResetable( true, true );
182
m_moduleState.add( m_input->getDataChangedCondition() );
183
m_moduleState.add( m_gradients->getDataChangedCondition() );
184
m_moduleState.add( m_propCondition );
186
// Signal ready state.
188
debugLog() << "Module is now ready.";
190
// create the root node containing the transformation and geometry
191
osg::ref_ptr< WGEGroupNode > rootNode = new WGEGroupNode();
193
// create the post-processing node which actually does the nice stuff to the rendered image
194
osg::ref_ptr< WGEPostprocessingNode > postNode = new WGEPostprocessingNode(
195
WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getCamera()
197
postNode->addUpdateCallback( new WGENodeMaskCallback( m_active ) ); // disable the postNode with m_active
199
// provide the properties of the post-processor to the user
200
m_properties->addProperty( postNode->getProperties() );
203
postNode->insert( rootNode, m_shader );
204
bool postNodeInserted = false;
206
// 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
207
// connectors or on changed in your properties.
208
debugLog() << "Entering main loop";
209
while( !m_shutdownFlag() )
211
// Now, the moduleState variable comes into play. The module can wait for the condition, which gets fired whenever the input receives data
212
// or an property changes. The main loop now waits until something happens.
213
debugLog() << "Waiting ...";
214
m_moduleState.wait();
217
if( m_shutdownFlag() )
222
// was there an update?
223
bool dataUpdated = m_input->updated() || m_gradients->updated();
224
boost::shared_ptr< WDataSetScalar > dataSet = m_input->getData();
225
bool dataValid = ( dataSet );
227
// valid data available?
230
// remove renderings if no data is available anymore
234
// m_isoColor or shading changed
235
if( m_isoColor->changed() )
237
// a new color requires the proxy geometry to be rebuild as we store it as color in this geometry
241
// As the data has changed, we need to recreate the texture.
242
if( dataUpdated && dataValid )
244
debugLog() << "Data changed. Uploading new data as texture.";
246
m_isoValue->setMin( dataSet->getTexture()->minimum()->get() );
247
m_isoValue->setMax( dataSet->getTexture()->scale()->get() + dataSet->getTexture()->minimum()->get() );
248
m_isoValue->setRecommendedValue( dataSet->getTexture()->minimum()->get() + ( 0.5 * dataSet->getTexture()->scale()->get() ) );
250
// First, grab the grid
251
boost::shared_ptr< WGridRegular3D > grid = boost::shared_dynamic_cast< WGridRegular3D >( dataSet->getGrid() );
254
errorLog() << "The dataset does not provide a regular grid. Ignoring dataset.";
258
// use the OSG Shapes, create unit cube
259
WBoundingBox bb( WPosition( 0.0, 0.0, 0.0 ),
260
WPosition( grid->getNbCoordsX() - 1, grid->getNbCoordsY() - 1, grid->getNbCoordsZ() - 1 ) );
261
osg::ref_ptr< osg::Node > cube = wge::generateSolidBoundingBoxNode( bb, m_isoColor->get( true ) );
262
cube->asTransform()->getChild( 0 )->setName( "_DVR Proxy Cube" ); // Be aware that this name is used in the pick handler.
263
// because of the underscore in front it won't be picked
264
// we also set the grid's transformation here
265
rootNode->setMatrix( static_cast< WMatrix4d >( grid->getTransform() ) );
267
// bind the texture to the node
268
osg::StateSet* rootState = cube->getOrCreateStateSet();
270
// enable transparency
271
wge::enableTransparency( cube );
273
// bind the data texture
274
osg::ref_ptr< WGETexture3D > texture3D = dataSet->getTexture();
275
texture3D->bind( cube );
277
////////////////////////////////////////////////////////////////////////////////////////////////////
278
// setup all those uniforms and additional textures
279
////////////////////////////////////////////////////////////////////////////////////////////////////
281
rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_isovalue", m_isoValue ) );
282
rootState->addUniform( new WGEPropertyUniform< WPropInt >( "u_steps", m_stepCount ) );
283
rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_alpha", m_alpha ) );
284
rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_colormapRatio", m_colormapRatio ) );
285
rootState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_borderClipDistance", m_borderClipDistance ) );
286
// Stochastic jitter?
287
const size_t size = 64;
288
osg::ref_ptr< WGETexture2D > randTex = wge::genWhiteNoiseTexture( size, size, 1 );
289
wge::bindTexture( cube, randTex, 1 );
291
// if there is a gradient field available -> apply as texture too
292
boost::shared_ptr< WDataSetVector > gradients = m_gradients->getData();
295
debugLog() << "Uploading specified gradient field.";
297
// bind the texture to the node
298
osg::ref_ptr< WDataTexture3D > gradTexture3D = gradients->getTexture();
299
wge::bindTexture( cube, gradTexture3D, 2, "u_gradients" );
300
gradTexEnableDefine->setActive( true );
304
gradTexEnableDefine->setActive( false ); // disable gradient texture
306
WGEColormapping::apply( cube, grid->getTransformationMatrix(), m_shader, 3 );
309
debugLog() << "Adding new rendering.";
311
rootNode->insert( cube );
312
// 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
313
// 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.
314
if( !postNodeInserted )
316
postNodeInserted = true;
317
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( postNode );
322
// At this point, the container managing this module signalled to shutdown. The main loop has ended and you should clean up. Always remove
323
// allocated memory and remove all OSG nodes.
324
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( postNode );