WMNavigationSlices.cpp 17.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------

25 26 27
#include <algorithm>
#include <string>

28
#include <osg/LineWidth>
29
#include <osg/MatrixTransform>
30
#include <osg/Vec3>
31

32
#include "core/common/math/WMath.h"
33
#include "core/common/WPropertyHelper.h"
34 35 36 37 38 39 40 41
#include "core/graphicsEngine/callbacks/WGELinearTranslationCallback.h"
#include "core/graphicsEngine/callbacks/WGENodeMaskCallback.h"
#include "core/graphicsEngine/callbacks/WGEPropertyUniformCallback.h"
#include "core/graphicsEngine/callbacks/WGEShaderAnimationCallback.h"
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
#include "core/graphicsEngine/shaders/WGEShader.h"
#include "core/graphicsEngine/shaders/WGEShaderDefineOptions.h"
#include "core/graphicsEngine/shaders/WGEShaderPropertyDefineOptions.h"
42 43
#include "core/graphicsEngine/WGEColormapping.h"
#include "core/graphicsEngine/WGEGeodeUtils.h"
44
#include "core/graphicsEngine/WGraphicsEngine.h"
45
#include "core/graphicsEngine/WPickHandler.h"
46
#include "core/kernel/WKernel.h"
47
#include "core/kernel/WSelectionManager.h"
48 49 50 51 52 53 54
#include "WMNavigationSlices.h"
#include "WMNavigationSlices.xpm"

// This line is needed by the module loader to actually find your module. You need to add this to your module too. Do NOT add a ";" here.
W_LOADABLE_MODULE( WMNavigationSlices )

WMNavigationSlices::WMNavigationSlices():
55 56
    WModule(),
    m_first( true )
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
{
}

WMNavigationSlices::~WMNavigationSlices()
{
    // Cleanup!
}

boost::shared_ptr< WModule > WMNavigationSlices::factory() const
{
    return boost::shared_ptr< WModule >( new WMNavigationSlices() );
}

const char** WMNavigationSlices::getXPMIcon() const
{
    return WMNavigationSlices_xpm;
}

const std::string WMNavigationSlices::getName() const
{
77
    return "Navigation Slices";
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
}

const std::string WMNavigationSlices::getDescription() const
{
    return "This module provides slices, orthogonally oriented in the OpenWalnut coordinate system. They allow the fast and easy exploration of "
           "three-dimensional datasets.";
}

void WMNavigationSlices::connectors()
{
    // call WModule's initialization
    WModule::connectors();
}

void WMNavigationSlices::properties()
{
94
    m_noTransparency  = m_properties->addProperty( "No transparency", "If checked, transparency is not used. This will show the complete slices.",
95 96
                                                   false );

97 98 99 100
    m_sliceGroup      = m_properties->addPropertyGroup( "Slices",  "Slice Options." );

    // enable slices
    // Flags denoting whether the glyphs should be shown on the specific slice
101 102 103
    m_showonX = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropSagittalShow() );
    m_showonY = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropCoronalShow() );
    m_showonZ = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropAxialShow() );
104 105

    // The slice positions.
106 107 108
    m_xPos    = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropSagittalPos() );
    m_yPos    = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropCoronalPos() );
    m_zPos    = m_sliceGroup->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropAxialPos() );
109

110 111 112 113 114 115 116 117
    // call WModule's initialization
    WModule::properties();
}

void WMNavigationSlices::initOSG()
{
    // remove the old slices
    m_output->clear();
118 119 120
    m_axialOutput->clear();
    m_sagittalOutput->clear();
    m_coronalOutput->clear();
121

122 123 124 125 126 127
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Property Setup
    ///////////////////////////////////////////////////////////////////////////////////////////////

    // no colormaps -> no slices
    bool empty = !WGEColormapping::instance()->size();
128
    if( empty )
129 130 131 132 133 134 135 136 137 138
    {
        // hide the slider properties.
        m_xPos->setHidden();
        m_yPos->setHidden();
        m_zPos->setHidden();
        return;
    }

    // grab the current bounding box
    WBoundingBox bb = WGEColormapping::instance()->getBoundingBox();
139 140
    WVector3d minV = bb.getMin();
    WVector3d maxV = bb.getMax();
141 142
    WVector3d sizes = ( maxV - minV );
    WVector3d midBB = minV + ( sizes * 0.5 );
143 144 145 146 147 148 149 150 151 152 153 154 155 156

    // update the properties
    m_xPos->setMin( minV[0] );
    m_xPos->setMax( maxV[0] );
    m_yPos->setMin( minV[1] );
    m_yPos->setMax( maxV[1] );
    m_zPos->setMin( minV[2] );
    m_zPos->setMax( maxV[2] );
    // un-hide the slider properties.
    m_xPos->setHidden( false );
    m_yPos->setHidden( false );
    m_zPos->setHidden( false );

    // if this is done the first time, set the slices to the center of the dataset
157
    if( m_first )
158 159
    {
        m_first = false;
160 161 162
        m_xPos->set( midBB[0] );
        m_yPos->set( midBB[1] );
        m_zPos->set( midBB[2] );
163 164
    }

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Navigation View Setup
    ///////////////////////////////////////////////////////////////////////////////////////////////

    double maxSizeInv = 1.0 / std::max( sizes[0], std::max( sizes[1], sizes[2] ) );
    m_axialOutput->setMatrix(
        osg::Matrixd::translate( -midBB[0], -midBB[1], -midBB[2] ) *
        osg::Matrixd::scale( maxSizeInv, maxSizeInv, maxSizeInv ) *
        osg::Matrixd::translate( 0.0, 0.0, -0.5 )
    );

    m_coronalOutput->setMatrix(
        osg::Matrixd::translate( -midBB[0], -midBB[1], -midBB[2] ) *
        osg::Matrixd::scale( maxSizeInv, maxSizeInv, maxSizeInv ) *
        osg::Matrixd::rotate( -0.5 * piDouble, 1.0, 0.0 , 0.0 ) *
        osg::Matrixd::translate( 0.0, 0.0, -0.5 )
    );

    m_sagittalOutput->setMatrix(
        osg::Matrixd::translate( -midBB[0], -midBB[1], -midBB[2] ) *
        osg::Matrixd::scale( maxSizeInv, maxSizeInv, maxSizeInv ) *
        osg::Matrixd::rotate( -0.5 * piDouble, 1.0, 0.0 , 0.0 ) *
        osg::Matrixd::rotate( 0.5 * piDouble, 0.0, 1.0 , 0.0 ) *
        osg::Matrixd::translate( 0.0, 0.0, -0.5 )
    );

191 192 193 194
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Slice Setup
    ///////////////////////////////////////////////////////////////////////////////////////////////

195
    // create a new geode containing the slices
196 197

    // X Slice
198 199
    osg::ref_ptr< osg::Node > xSlice = wge::genFinitePlane( minV, osg::Vec3( 0.0, sizes[1], 0.0 ),
                                                                  osg::Vec3( 0.0, 0.0, sizes[2] ) );
200
    xSlice->setName( "Sagittal Slice" );
201
    osg::Uniform* xSliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() );
202 203 204
    xSlice->getOrCreateStateSet()->addUniform( xSliceUniform );

    // Y Slice
205 206
    osg::ref_ptr< osg::Node > ySlice = wge::genFinitePlane( minV, osg::Vec3( sizes[0], 0.0, 0.0 ),
                                                                  osg::Vec3( 0.0, 0.0, sizes[2] ) );
207
    ySlice->setName( "Coronal Slice" );
208
    osg::Uniform* ySliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() );
209
    ySlice->getOrCreateStateSet()->addUniform( ySliceUniform );
210

211
    // Z Slice
212 213
    osg::ref_ptr< osg::Node > zSlice = wge::genFinitePlane( minV, osg::Vec3( sizes[0], 0.0, 0.0 ),
                                                                  osg::Vec3( 0.0, sizes[1], 0.0 ) );
214
    zSlice->setName( "Axial Slice" );
215
    osg::Uniform* zSliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() );
216 217 218 219 220 221 222
    zSlice->getOrCreateStateSet()->addUniform( zSliceUniform );

    // disable culling.
    // NOTE: Somehow, this is ignore by OSG. If you know why: tell me please
    xSlice->setCullingActive( false );
    ySlice->setCullingActive( false );
    zSlice->setCullingActive( false );
223

224
    // each slice is child of an transformation node
225 226 227 228 229 230 231
    osg::ref_ptr< osg::MatrixTransform > mX = new osg::MatrixTransform();
    mX->addChild( xSlice );
    osg::ref_ptr< osg::MatrixTransform > mY = new osg::MatrixTransform();
    mY->addChild( ySlice );
    osg::ref_ptr< osg::MatrixTransform > mZ = new osg::MatrixTransform();
    mZ->addChild( zSlice );

232 233 234 235 236 237 238 239 240 241
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Callback Setup
    ///////////////////////////////////////////////////////////////////////////////////////////////

    // Control transformation node by properties. We use an additional uniform here to provide the shader the transformation matrix used to
    // translate the slice.
    mX->addUpdateCallback( new WGELinearTranslationCallback< WPropDouble >( osg::Vec3( 1.0, 0.0, 0.0 ), m_xPos, xSliceUniform ) );
    mY->addUpdateCallback( new WGELinearTranslationCallback< WPropDouble >( osg::Vec3( 0.0, 1.0, 0.0 ), m_yPos, ySliceUniform ) );
    mZ->addUpdateCallback( new WGELinearTranslationCallback< WPropDouble >( osg::Vec3( 0.0, 0.0, 1.0 ), m_zPos, zSliceUniform ) );

242 243 244 245 246 247 248 249 250 251
    // set callbacks for en-/disabling the nodes
    xSlice->addUpdateCallback( new WGENodeMaskCallback( m_showonX ) );
    ySlice->addUpdateCallback( new WGENodeMaskCallback( m_showonY ) );
    zSlice->addUpdateCallback( new WGENodeMaskCallback( m_showonZ ) );

    // set the pick callbacks for each slice
    m_xSlicePicker = PickCallback::SPtr( new PickCallback( xSlice, m_xPos ) );
    m_ySlicePicker = PickCallback::SPtr( new PickCallback( ySlice, m_yPos, true ) );
    m_zSlicePicker = PickCallback::SPtr( new PickCallback( zSlice, m_zPos ) );

252 253 254 255
    // transparency property
    osg::ref_ptr< osg::Uniform > transparencyUniform = new osg::Uniform( "u_noTransparency", false );
    transparencyUniform->setUpdateCallback( new WGEPropertyUniformCallback< WPropBool >( m_noTransparency ) );

256 257 258
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Done
    ///////////////////////////////////////////////////////////////////////////////////////////////
259

260 261 262 263 264 265 266 267
    osg::ref_ptr< osg::StateSet > state = m_output->getOrCreateStateSet();
    state->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );

    // we want some nice animations: add timer
    osg::ref_ptr< osg::Uniform > animationUniform = new osg::Uniform( "u_timer", 0 );
    state->addUniform( animationUniform );
    animationUniform->setUpdateCallback( new WGEShaderAnimationCallback() );

268 269
    // transparency property
    state->addUniform( transparencyUniform );
270 271 272
    m_axialOutput->getOrCreateStateSet()->addUniform( transparencyUniform );
    m_sagittalOutput->getOrCreateStateSet()->addUniform( transparencyUniform );
    m_coronalOutput->getOrCreateStateSet()->addUniform( transparencyUniform );
273

274 275 276 277 278
    // add the transformation nodes to the output group
    m_output->insert( mX );
    m_output->insert( mY );
    m_output->insert( mZ );
    m_output->dirtyBound();
279

280 281 282
    m_axialOutput->insert( mZ );
    m_sagittalOutput->insert( mX );
    m_coronalOutput->insert( mY );
283 284 285 286 287
}

WMNavigationSlices::PickCallback::PickCallback( osg::ref_ptr< osg::Node > node, WPropDouble property, bool negateDirection ):
    m_node( node ),
    m_property( property ),
288
    m_pickUniform( new osg::Uniform( "u_picked", 0.0f ) ),
289 290 291
    m_dir( negateDirection ? -1.0 : 1.0 )
{
    boost::shared_ptr< WGraphicsEngine > ge = WGraphicsEngine::getGraphicsEngine();
292
    boost::shared_ptr< WGEViewer > viewer = ge->getViewerByName( "Main View" );
293 294
    m_camera = viewer->getCamera();
    m_pickConnection = viewer->getPickHandler()->getPickSignal()->connect( boost::bind( &WMNavigationSlices::PickCallback::pick, this, _1 ) );
295
    node->getOrCreateStateSet()->addUniform( m_pickUniform );
296 297 298 299
}

void WMNavigationSlices::PickCallback::pick( WPickInfo pickInfo )
{
300
    if( pickInfo.getName() == m_node->getName() )
301
    {
302 303
        WVector3d normal = pickInfo.getPickNormal();
        WVector2d newPixelPos = pickInfo.getPickPixel();
304
        // dragging was initialized earlier
305
        if( m_isPicked )
306
        {
307 308
            osg::Vec3 startPosScreen( m_oldPixelPosition[ 0 ], m_oldPixelPosition[ 1 ], 0.0 );
            osg::Vec3 endPosScreen( newPixelPos[ 0 ], newPixelPos[ 1 ], 0.0 );
309 310 311 312 313

            osg::Vec3 startPosWorld = wge::unprojectFromScreen( startPosScreen, m_camera );
            osg::Vec3 endPosWorld = wge::unprojectFromScreen( endPosScreen, m_camera );

            osg::Vec3 moveDirWorld = endPosWorld - startPosWorld;
314
            float diff = moveDirWorld * static_cast< osg::Vec3 >( normal );
315 316 317 318 319 320

            m_property->set( m_property->get() + m_dir * diff );
        }
        // this might have initialized dragging. Keep track of old position
        m_oldPixelPosition = newPixelPos;
        m_isPicked = true;
321
        m_pickUniform->set( 1.0f );
322 323 324 325
    }
    else    // someone else got picked.
    {
        m_isPicked = false;
326
        m_pickUniform->set( 0.0f );
327 328 329 330 331 332 333 334 335 336 337
    }
}

void WMNavigationSlices::moduleMain()
{
    // get notified about data changes
    m_moduleState.setResetable( true, true );

    // done.
    ready();

338 339 340 341
    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Slices Setup
    /////////////////////////////////////////////////////////////////////////////////////////////////////////

342 343
    // create the root node for all the geometry
    m_output = osg::ref_ptr< WGEManagedGroupNode > ( new WGEManagedGroupNode( m_active ) );
344 345 346 347 348 349

    // create the roots for the nav-views
    m_sagittalOutput = osg::ref_ptr< WGEGroupNode > ( new WGEGroupNode() );
    m_coronalOutput = osg::ref_ptr< WGEGroupNode > ( new WGEGroupNode() );
    m_axialOutput = osg::ref_ptr< WGEGroupNode > ( new WGEGroupNode() );

350
    WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_output );
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

    // add for side-views
    boost::shared_ptr< WGEViewer > v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Axial View" );
    if( v )
    {
        v->getScene()->insert( m_axialOutput );
    }
    v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Coronal View" );
    if( v )
    {
        v->getScene()->insert( m_coronalOutput );
    }
    v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Sagittal View" );
    if( v )
    {
        v->getScene()->insert( m_sagittalOutput );
    }
368

369 370 371 372 373 374
    // disable the pick-coloring for the side views
    m_axialOutput->getOrCreateStateSet()->addUniform( new osg::Uniform( "u_pickColorEnabled", 0.0f ) );
    m_sagittalOutput->getOrCreateStateSet()->addUniform( new osg::Uniform( "u_pickColorEnabled", 0.0f ) );
    m_coronalOutput->getOrCreateStateSet()->addUniform( new osg::Uniform( "u_pickColorEnabled", 0.0f ) );
    m_output->getOrCreateStateSet()->addUniform( new osg::Uniform( "u_pickColorEnabled", 1.0f ) );

375 376 377 378 379 380 381 382 383 384 385 386 387 388
    m_output->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
    m_axialOutput->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
    m_sagittalOutput->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
    m_coronalOutput->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );

    // apply colormapping to all the nodes
    osg::ref_ptr< WGEShader > shader = new WGEShader( "WMNavigationSlices", m_localPath );
    WGEColormapping::NodeList nodes;
    nodes.push_back( m_output );
    nodes.push_back( m_axialOutput );
    nodes.push_back( m_sagittalOutput );
    nodes.push_back( m_coronalOutput );
    WGEColormapping::apply( nodes, shader ); // this automatically applies the shader

389 390 391 392
    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Main loop
    /////////////////////////////////////////////////////////////////////////////////////////////////////////

393 394 395
    // we need to be informed if the bounding box of the volume containing all the data changes.
    m_moduleState.add( WGEColormapping::instance()->getChangeCondition() );

396
    // main loop
397
    while( !m_shutdownFlag() )
398 399
    {
        // woke up since the module is requested to finish?
400
        if( m_shutdownFlag() )
401 402 403 404
        {
            break;
        }

405
        // create the slices. This loop is only entered if WGEColormapper was fired or shutdown.
406 407 408 409 410 411 412 413 414 415 416 417 418
        initOSG();

        // Thats it. Nothing more to do. Slide movement and colormapping is done by the pick handler and WGEColormapper
        debugLog() << "Waiting ...";
        m_moduleState.wait();
    }

    // clean up
    m_xSlicePicker.reset();
    m_ySlicePicker.reset();
    m_zSlicePicker.reset();

    WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_output );
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Axial View" );
    if( v )
    {
        v->getScene()->remove( m_axialOutput );
    }
    v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Coronal View" );
    if( v )
    {
        v->getScene()->remove( m_coronalOutput );
    }
    v = WKernel::getRunningKernel()->getGraphicsEngine()->getViewerByName( "Sagittal View" );
    if( v )
    {
        v->getScene()->remove( m_sagittalOutput );
    }
435 436
}