1
//---------------------------------------------------------------------------
3
// Project: OpenWalnut ( http://www.openwalnut.org )
5
// Copyright 2009 OpenWalnut Community, BSV-Leipzig and CNCF-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
//---------------------------------------------------------------------------
29
#include <osg/Geometry>
30
#include <osg/StateSet>
31
#include <osg/StateAttribute>
32
#include <osg/PolygonMode>
33
#include <osg/LightModel>
35
#include "core/kernel/WKernel.h"
37
#include "core/graphicsEngine/WGEUtils.h"
38
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
40
#include "WMSuperquadricGlyphs.h"
41
#include "WMSuperquadricGlyphs.xpm"
43
// This line is needed by the module loader to actually find your module.
44
W_LOADABLE_MODULE( WMSuperquadricGlyphs )
46
WMSuperquadricGlyphs::WMSuperquadricGlyphs():
52
WMSuperquadricGlyphs::~WMSuperquadricGlyphs()
58
boost::shared_ptr< WModule > WMSuperquadricGlyphs::factory() const
61
return boost::shared_ptr< WModule >( new WMSuperquadricGlyphs() );
64
const char** WMSuperquadricGlyphs::getXPMIcon() const
66
return superquadricglyphs_xpm;
69
const std::string WMSuperquadricGlyphs::getName() const
71
return "Superquadric Glyphs";
74
const std::string WMSuperquadricGlyphs::getDescription() const
76
return "GPU based ray-tracing of second order, superquadric tensor glyphs.";
79
void WMSuperquadricGlyphs::connectors()
81
// The input second order tensor dataset
82
m_input = boost::shared_ptr< WModuleInputData< WDataSetDTI > >( new WModuleInputData< WDataSetDTI >( shared_from_this(),
83
"tensor input", "An input set of 2nd-order tensors on a regular 3D-grid." )
85
addConnector( m_input );
87
// call WModule's initialization
88
WModule::connectors();
91
void WMSuperquadricGlyphs::properties()
93
// The condition fires on property updates
94
m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
96
// The slice positions. These get update externally
97
m_xPos = m_properties->addProperty( "Sagittal position", "Slice X position.", 0, m_propCondition );
98
m_yPos = m_properties->addProperty( "Coronal position", "Slice Y position.", 0, m_propCondition );
99
m_zPos = m_properties->addProperty( "Axial position", "Slice Z position.", 0, m_propCondition );
107
// Flags denoting whether the glyphs should be shown on the specific slice
108
// NOTE: the showon* properties do not need to notify m_propCondition as they get handled by the slice's osg node.
109
m_showonX = m_properties->addProperty( "Show sagittal", "Show vectors on sagittal slice.", true );
110
m_showonY = m_properties->addProperty( "Show coronal", "Show vectors on coronal slice.", true );
111
m_showonZ = m_properties->addProperty( "Show axial", "Show vectors on axial slice.", true );
113
// Thresholding for filtering glyphs
114
m_evThreshold = m_properties->addProperty( "Eigenvalue threshold",
115
"Clip Glyphs whose smallest eigenvalue is below the given threshold.", 0.01 );
116
m_evThreshold->setMin( 0.0 );
117
m_evThreshold->setMax( 1.0 );
118
m_faThreshold = m_properties->addProperty( "FA threshold",
119
"Clip Glyphs whose fractional anisotropy is below the given threshold.", 0.01 );
120
m_faThreshold->setMin( 0.0 );
121
m_faThreshold->setMax( 1.0 );
123
m_gamma = m_properties->addProperty( "Gamma", "Sharpness Parameter of the SuperQuadrics.", 10.0 );
124
m_gamma->setMin( 0.0 );
125
m_gamma->setMax( 10.0 );
127
m_scaling = m_properties->addProperty( "Scaling", "Scaling of Glyphs.", 0.5 );
128
m_scaling->setMin( 0.0 );
129
m_scaling->setMax( 2.0 );
132
inline void WMSuperquadricGlyphs::addGlyph( osg::Vec3 position, osg::ref_ptr< osg::Vec3Array > vertices, osg::ref_ptr< osg::Vec3Array > orientation )
135
vertices->push_back( position );
136
orientation->push_back( osg::Vec3( -1.0, -1.0, -1.0 ) );
137
vertices->push_back( position );
138
orientation->push_back( osg::Vec3( 1.0, -1.0, -1.0 ) );
139
vertices->push_back( position );
140
orientation->push_back( osg::Vec3( 1.0, 1.0, -1.0 ) );
141
vertices->push_back( position );
142
orientation->push_back( osg::Vec3( -1.0, 1.0, -1.0 ) );
145
vertices->push_back( position );
146
orientation->push_back( osg::Vec3( -1.0, -1.0, 1.0 ) );
147
vertices->push_back( position );
148
orientation->push_back( osg::Vec3( 1.0, -1.0, 1.0 ) );
149
vertices->push_back( position );
150
orientation->push_back( osg::Vec3( 1.0, 1.0, 1.0 ) );
151
vertices->push_back( position );
152
orientation->push_back( osg::Vec3( -1.0, 1.0, 1.0 ) );
155
vertices->push_back( position );
156
orientation->push_back( osg::Vec3( -1.0, 1.0, -1.0 ) );
157
vertices->push_back( position );
158
orientation->push_back( osg::Vec3( 1.0, 1.0, -1.0 ) );
159
vertices->push_back( position );
160
orientation->push_back( osg::Vec3( 1.0, 1.0, 1.0 ) );
161
vertices->push_back( position );
162
orientation->push_back( osg::Vec3( -1.0, 1.0, 1.0 ) );
165
vertices->push_back( position );
166
orientation->push_back( osg::Vec3( -1.0, -1.0, -1.0 ) );
167
vertices->push_back( position );
168
orientation->push_back( osg::Vec3( 1.0, -1.0, -1.0 ) );
169
vertices->push_back( position );
170
orientation->push_back( osg::Vec3( 1.0, -1.0, 1.0 ) );
171
vertices->push_back( position );
172
orientation->push_back( osg::Vec3( -1.0, -1.0, 1.0 ) );
175
vertices->push_back( position );
176
orientation->push_back( osg::Vec3( -1.0, -1.0, -1.0 ) );
177
vertices->push_back( position );
178
orientation->push_back( osg::Vec3( -1.0, 1.0, -1.0 ) );
179
vertices->push_back( position );
180
orientation->push_back( osg::Vec3( -1.0, 1.0, 1.0 ) );
181
vertices->push_back( position );
182
orientation->push_back( osg::Vec3( -1.0, -1.0, 1.0 ) );
185
vertices->push_back( position );
186
orientation->push_back( osg::Vec3( 1.0, -1.0, -1.0 ) );
187
vertices->push_back( position );
188
orientation->push_back( osg::Vec3( 1.0, 1.0, -1.0 ) );
189
vertices->push_back( position );
190
orientation->push_back( osg::Vec3( 1.0, 1.0, 1.0 ) );
191
vertices->push_back( position );
192
orientation->push_back( osg::Vec3( 1.0, -1.0, 1.0 ) );
195
void WMSuperquadricGlyphs::addTensor( size_t idx, osg::Vec3Array* diag, osg::Vec3Array* offdiag )
197
osg::Vec3 d = osg::Vec3( m_dataSetValueSet->getScalarDouble( idx * 6 + 0 ),
198
m_dataSetValueSet->getScalarDouble( idx * 6 + 3 ),
199
m_dataSetValueSet->getScalarDouble( idx * 6 + 5 ) );
200
osg::Vec3 o = osg::Vec3( m_dataSetValueSet->getScalarDouble( idx * 6 + 1 ),
201
m_dataSetValueSet->getScalarDouble( idx * 6 + 2 ),
202
m_dataSetValueSet->getScalarDouble( idx * 6 + 4 ) );
204
// we need to add it for every vertex per glyph!
205
for( size_t c = 0; c < 6 * 4; ++c )
207
diag->push_back( d );
208
offdiag->push_back( o );
212
void WMSuperquadricGlyphs::initOSG()
214
// The Idea behind all this is to have fast updates when the navigation slices move. By presetting glyph vertex and tex coords -> fast change
215
// of diag and offdiag tex arrays possible. The slices are moved using the transform nodes
217
debugLog() << "Grid Size: " << m_maxX << "x" << m_maxY << "x" << m_maxZ;
218
debugLog() << "Creating " << m_nbGlyphsX + m_nbGlyphsY + m_nbGlyphsZ << " glyphs.";
220
// remove the old slices
221
m_output->remove( m_xSlice );
222
m_output->remove( m_ySlice );
223
m_output->remove( m_zSlice );
225
// create all the transformation nodes
226
m_xSlice = new WGEManagedGroupNode( m_showonX );
227
m_xSlice->setMatrix( osg::Matrixd::identity() );
228
m_ySlice = new WGEManagedGroupNode( m_showonY );
229
m_ySlice->setMatrix( osg::Matrixd::identity() );
230
m_zSlice = new WGEManagedGroupNode( m_showonZ );
231
m_zSlice->setMatrix( osg::Matrixd::identity() );
233
// init the vertex arrays
234
osg::ref_ptr< osg::Geode > geode;
235
osg::ref_ptr< osg::Geometry > geometry;
236
osg::ref_ptr< osg::DrawArrays > da;
238
// NOTE: it would be nice to use one vertex and tex array for all the three but this disallows us to replace the tensor diag/offdiag arrays
239
// for each slide separately.
240
osg::ref_ptr< osg::Vec3Array > vertices;
241
osg::ref_ptr< osg::Vec3Array > texcoords0;
246
// fill the geode with glyph proxy geometry for a slice
247
vertices = new osg::Vec3Array;
248
vertices->reserve( m_nbGlyphsX * 6 * 4 );
249
texcoords0 = new osg::Vec3Array;
250
texcoords0->reserve( m_nbGlyphsX * 6 * 4 );
251
geometry = new osg::Geometry();
252
geometry->setDataVariance( osg::Object::DYNAMIC );
253
geometry->setVertexArray( vertices );
254
geometry->setUseVertexBufferObjects( true );
255
geometry->setTexCoordArray( 0, texcoords0 );
256
da = new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, m_nbGlyphsX * 6 * 4 );
257
geometry->addPrimitiveSet( da );
259
// create a new geode containing the glyphs proxy geometry
260
geode = new osg::Geode();
261
geode->addDrawable( geometry );
263
// add a glyph at every position
264
for( size_t z = 0; z < m_maxZ; ++z )
266
for( size_t y = 0; y < m_maxY; ++y )
268
addGlyph( osg::Vec3d( 0.0, y, z ), vertices, texcoords0 );
272
// set some callbacks
273
m_xSliceGlyphCallback = new GlyphGeometryNodeCallback( geometry );
274
geometry->setUpdateCallback( m_xSliceGlyphCallback );
275
m_xSlice->addUpdateCallback( new WGELinearTranslationCallback< WPropInt >( osg::Vec3( 1.0, 0.0, 0.0 ), m_xPos ) );
276
m_xSlice->addChild( geode );
279
// Y Slice, sorry for code duplication.
281
// fill the geode with glyph proxy geometry for a slice
282
vertices = new osg::Vec3Array;
283
vertices->reserve( m_nbGlyphsX * 6 * 4 );
284
texcoords0 = new osg::Vec3Array;
285
texcoords0->reserve( m_nbGlyphsX * 6 * 4 );
286
geometry = new osg::Geometry();
287
geometry->setDataVariance( osg::Object::DYNAMIC );
288
geometry->setVertexArray( vertices );
289
geometry->setUseVertexBufferObjects( true );
290
geometry->setTexCoordArray( 0, texcoords0 );
291
da = new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, m_nbGlyphsY * 6 * 4 );
292
geometry->addPrimitiveSet( da );
294
// create a new geode containing the glyphs proxy geometry
295
geode = new osg::Geode();
296
geode->addDrawable( geometry );
297
// add a glyph at every position
298
for( size_t z = 0; z < m_maxZ; ++z )
300
for( size_t x = 0; x < m_maxX; ++x )
302
addGlyph( osg::Vec3d( x, 0.0, z ), vertices, texcoords0 );
306
// set some callbacks
307
m_ySliceGlyphCallback = new GlyphGeometryNodeCallback( geometry );
308
geometry->setUpdateCallback( m_ySliceGlyphCallback );
309
m_ySlice->addUpdateCallback( new WGELinearTranslationCallback< WPropInt >( osg::Vec3( 0.0, 1.0, 0.0 ), m_yPos ) );
310
m_ySlice->addChild( geode );
313
// Z Slice, sorry for code duplication.
315
// fill the geode with glyph proxy geometry for a slice
316
vertices = new osg::Vec3Array;
317
vertices->reserve( m_nbGlyphsX * 6 * 4 );
318
texcoords0 = new osg::Vec3Array;
319
texcoords0->reserve( m_nbGlyphsX * 6 * 4 );
320
geometry = new osg::Geometry();
321
geometry->setDataVariance( osg::Object::DYNAMIC );
322
geometry->setVertexArray( vertices );
323
geometry->setUseVertexBufferObjects( true );
324
geometry->setTexCoordArray( 0, texcoords0 );
325
da = new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, m_nbGlyphsZ * 6 * 4 );
326
geometry->addPrimitiveSet( da );
328
// create a new geode containing the glyphs proxy geometry
329
geode = new osg::Geode();
330
geode->addDrawable( geometry );
331
// add a glyph at every position
332
for( size_t y = 0; y < m_maxY; ++y )
334
for( size_t x = 0; x < m_maxX; ++x )
336
addGlyph( osg::Vec3d( x, y, 0.0 ), vertices, texcoords0 );
340
// set some callbacks
341
m_zSliceGlyphCallback = new GlyphGeometryNodeCallback( geometry );
342
geometry->setUpdateCallback( m_zSliceGlyphCallback );
343
m_zSlice->addUpdateCallback( new WGELinearTranslationCallback< WPropInt >( osg::Vec3( 0.0, 0.0, 1.0 ), m_zPos ) );
344
m_zSlice->addChild( geode );
346
// add the transformation nodes to the output group
347
m_output->insert( m_xSlice );
348
m_output->insert( m_ySlice );
349
m_output->insert( m_zSlice );
352
void WMSuperquadricGlyphs::GlyphGeometryNodeCallback::update( osg::NodeVisitor* /*nv*/, osg::Drawable* /*d*/ )
358
// update the tex array
359
m_geometry->setTexCoordArray( 1, m_tensorDiag );
360
m_geometry->setTexCoordArray( 2, m_tensorOffDiag );
364
void WMSuperquadricGlyphs::GlyphGeometryNodeCallback::setNewTensorData( osg::ref_ptr< osg::Vec3Array > diag, osg::ref_ptr< osg::Vec3Array > offdiag )
367
m_tensorOffDiag = offdiag;
372
void WMSuperquadricGlyphs::moduleMain()
374
// use the m_input "data changed" flag
375
m_moduleState.setResetable( true, true );
376
m_moduleState.add( m_input->getDataChangedCondition() );
377
// use property condition to wake up on property changes
378
m_moduleState.add( m_propCondition );
380
// signal ready state
383
// create all these geodes we need
384
m_output = osg::ref_ptr< WGEManagedGroupNode > ( new WGEManagedGroupNode( m_active ) );
385
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_output );
386
osg::ref_ptr< osg::StateSet > sset = m_output->getOrCreateStateSet();
387
sset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
390
m_shader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMSuperquadricGlyphs", m_localPath ) );
391
m_shader->apply( m_output );
393
// set uniform callbacks and uniforms
394
osg::ref_ptr< osg::Uniform > evThreshold = new WGEPropertyUniform< WPropDouble >( "u_evThreshold", m_evThreshold );
395
osg::ref_ptr< osg::Uniform > faThreshold = new WGEPropertyUniform< WPropDouble >( "u_faThreshold", m_faThreshold );
396
osg::ref_ptr< osg::Uniform > scaling = new WGEPropertyUniform< WPropDouble >( "u_scaling", m_scaling );
397
osg::ref_ptr< osg::Uniform > gamma = new WGEPropertyUniform< WPropDouble >( "u_gamma", m_gamma );
399
sset->addUniform( evThreshold );
400
sset->addUniform( faThreshold );
401
sset->addUniform( scaling );
402
sset->addUniform( gamma );
404
bool initialTensorUpload = true;
406
// loop until the module container requests the module to quit
407
while( !m_shutdownFlag() )
409
m_moduleState.wait();
411
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
412
// After waking up, the module has to check whether the shutdownFlag fired. If yes, simply quit the module.
414
// woke up since the module is requested to finish
415
if( m_shutdownFlag() )
420
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
423
// has the data changes? And even more important: is it valid?
424
boost::shared_ptr< WDataSetDTI > newDataSet = m_input->getData();
425
bool dataChanged = ( m_dataSet != newDataSet );
426
bool dataValid = ( newDataSet );
428
// if data is invalid, remove rendering
431
debugLog() << "Resetting.";
437
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
440
if( dataChanged && dataValid )
442
// The data is different. Copy it to our internal data variable:
443
debugLog() << "Received Data.";
444
initialTensorUpload = true;
446
// also provide progress information
447
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Building Glyph Geometry" ) );
448
m_progress->addSubProgress( progress1 );
450
// get pointers for the new data
451
m_dataSet = newDataSet;
452
m_dataSetGrid = boost::shared_dynamic_cast< WGridRegular3D >( m_dataSet->getGrid() );
453
WAssert( m_dataSetGrid, "Dataset does not have a regular 3D grid." );
454
m_maxX = m_dataSetGrid->getNbCoordsX();
455
m_maxY = m_dataSetGrid->getNbCoordsY();
456
m_maxZ = m_dataSetGrid->getNbCoordsZ();
457
m_dataSetValueSet = m_dataSet->getValueSet();
458
m_nbGlyphsX = m_maxY * m_maxZ;
459
m_nbGlyphsY = m_maxX * m_maxZ;
460
m_nbGlyphsZ = m_maxX * m_maxY;
463
m_xPos->setMax( m_maxX );
464
m_xPos->set( m_maxX / 2 );
465
m_yPos->setMax( m_maxY );
466
m_yPos->set( m_maxY / 2 );
467
m_zPos->setMax( m_maxZ );
468
m_zPos->set( m_maxZ / 2 );
470
m_output->setMatrix( static_cast< WMatrix4d >( m_dataSetGrid->getTransform() ) );
472
// new data -> update OSG Stuff
477
if( dataValid && ( m_xPos->changed() || initialTensorUpload ) )
479
// also provide progress information
480
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Building Glyph Geometry" ) );
481
m_progress->addSubProgress( progress1 );
484
osg::ref_ptr< osg::Vec3Array > diag = new osg::Vec3Array;
485
diag->reserve( m_nbGlyphsX * 6 * 4 );
486
osg::ref_ptr< osg::Vec3Array > offdiag = new osg::Vec3Array;
487
offdiag->reserve( m_nbGlyphsX * 6 * 4 );
489
// this updates the diag/offdiag texture coordinate arrays of the xSlice
490
// x = const -> handle xPos property
491
size_t fixedX = m_xPos->get( true );
492
for( size_t z = 0; z < m_maxZ; ++z )
494
// Calculate the current position along the z direction
495
size_t zPre = fixedX + z * m_maxX * m_maxY;
497
for( size_t y = 0; y < m_maxY; ++y )
500
addTensor( zPre + y * m_maxX, diag, offdiag );
504
m_xSliceGlyphCallback->setNewTensorData( diag, offdiag );
508
if( dataValid && ( m_yPos->changed() || initialTensorUpload ) )
510
// also provide progress information
511
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Building Glyph Geometry" ) );
512
m_progress->addSubProgress( progress1 );
515
osg::ref_ptr< osg::Vec3Array > diag = new osg::Vec3Array;
516
diag->reserve( m_nbGlyphsX * 6 * 4 );
517
osg::ref_ptr< osg::Vec3Array > offdiag = new osg::Vec3Array;
518
offdiag->reserve( m_nbGlyphsX * 6 * 4 );
520
// y = const -> handle yPos property
521
size_t fixedY = m_yPos->get( true );
522
size_t fixedYOffset = fixedY * m_maxX;
523
for( size_t z = 0; z < m_maxZ; ++z )
525
// Calculate current offset for value index
526
size_t zPre = fixedYOffset + ( z * m_maxX * m_maxY );
528
for( size_t x = 0; x < m_maxX; ++x )
531
addTensor( zPre + x, diag, offdiag );
535
m_ySliceGlyphCallback->setNewTensorData( diag, offdiag );
539
if( dataValid && ( m_zPos->changed() || initialTensorUpload ) )
541
// also provide progress information
542
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Building Glyph Geometry" ) );
543
m_progress->addSubProgress( progress1 );
546
osg::ref_ptr< osg::Vec3Array > diag = new osg::Vec3Array;
547
diag->reserve( m_nbGlyphsX * 6 * 4 );
548
osg::ref_ptr< osg::Vec3Array > offdiag = new osg::Vec3Array;
549
offdiag->reserve( m_nbGlyphsX * 6 * 4 );
551
// z = const -> handle zPos property
552
size_t fixedZ = static_cast< size_t >( m_zPos->get( true ) );
553
size_t fixedZOffset = fixedZ * m_maxX * m_maxY;
554
for( size_t y = 0; y < m_maxY; ++y )
556
// Calculate current offset for value index
557
size_t yPre = fixedZOffset + ( y * m_maxX );
559
for( size_t x = 0; x < m_maxX; ++x )
562
addTensor( yPre + x, diag, offdiag );
566
m_zSliceGlyphCallback->setNewTensorData( diag, offdiag );
570
initialTensorUpload = false;
573
// At this point, the container managing this module signalled to shutdown. The main loop has ended and you should clean up. Always remove
574
// allocated memory and remove all OSG nodes.
575
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_output );