//--------------------------------------------------------------------------- // // 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 . // //--------------------------------------------------------------------------- #include #include #include #include #include #include "core/common/math/WMath.h" #include "core/common/WPropertyHelper.h" #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" #include "core/graphicsEngine/WGEColormapping.h" #include "core/graphicsEngine/WGEGeodeUtils.h" #include "core/graphicsEngine/WGraphicsEngine.h" #include "core/graphicsEngine/WPickHandler.h" #include "core/kernel/WKernel.h" #include "core/kernel/WSelectionManager.h" #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(): WModule(), m_first( true ) { } 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 { return "Navigation Slices"; } 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() { m_noTransparency = m_properties->addProperty( "No transparency", "If checked, transparency is not used. This will show the complete slices.", false ); m_sliceGroup = m_properties->addPropertyGroup( "Slices", "Slice Options." ); // enable slices // Flags denoting whether the glyphs should be shown on the specific slice 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() ); // The slice positions. 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() ); // call WModule's initialization WModule::properties(); } void WMNavigationSlices::initOSG() { // remove the old slices m_output->clear(); m_axialOutput->clear(); m_sagittalOutput->clear(); m_coronalOutput->clear(); /////////////////////////////////////////////////////////////////////////////////////////////// // Property Setup /////////////////////////////////////////////////////////////////////////////////////////////// // no colormaps -> no slices bool empty = !WGEColormapping::instance()->size(); if( empty ) { // hide the slider properties. m_xPos->setHidden(); m_yPos->setHidden(); m_zPos->setHidden(); return; } // grab the current bounding box WBoundingBox bb = WGEColormapping::instance()->getBoundingBox(); WVector3d minV = bb.getMin(); WVector3d maxV = bb.getMax(); WVector3d sizes = ( maxV - minV ); WVector3d midBB = minV + ( sizes * 0.5 ); // 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 if( m_first ) { m_first = false; m_xPos->set( midBB[0] ); m_yPos->set( midBB[1] ); m_zPos->set( midBB[2] ); } /////////////////////////////////////////////////////////////////////////////////////////////// // 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 ) ); /////////////////////////////////////////////////////////////////////////////////////////////// // Slice Setup /////////////////////////////////////////////////////////////////////////////////////////////// // create a new geode containing the slices // X Slice 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] ) ); xSlice->setName( "Sagittal Slice" ); osg::Uniform* xSliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() ); xSlice->getOrCreateStateSet()->addUniform( xSliceUniform ); // Y Slice 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] ) ); ySlice->setName( "Coronal Slice" ); osg::Uniform* ySliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() ); ySlice->getOrCreateStateSet()->addUniform( ySliceUniform ); // Z Slice 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 ) ); zSlice->setName( "Axial Slice" ); osg::Uniform* zSliceUniform = new osg::Uniform( "u_WorldTransform", osg::Matrixf::identity() ); 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 ); // each slice is child of an transformation node 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 ); /////////////////////////////////////////////////////////////////////////////////////////////// // 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 ) ); // 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 ) ); // transparency property osg::ref_ptr< osg::Uniform > transparencyUniform = new osg::Uniform( "u_noTransparency", false ); transparencyUniform->setUpdateCallback( new WGEPropertyUniformCallback< WPropBool >( m_noTransparency ) ); /////////////////////////////////////////////////////////////////////////////////////////////// // Done /////////////////////////////////////////////////////////////////////////////////////////////// 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() ); // transparency property state->addUniform( transparencyUniform ); m_axialOutput->getOrCreateStateSet()->addUniform( transparencyUniform ); m_sagittalOutput->getOrCreateStateSet()->addUniform( transparencyUniform ); m_coronalOutput->getOrCreateStateSet()->addUniform( transparencyUniform ); // add the transformation nodes to the output group m_output->insert( mX ); m_output->insert( mY ); m_output->insert( mZ ); m_output->dirtyBound(); m_axialOutput->insert( mZ ); m_sagittalOutput->insert( mX ); m_coronalOutput->insert( mY ); } WMNavigationSlices::PickCallback::PickCallback( osg::ref_ptr< osg::Node > node, WPropDouble property, bool negateDirection ): m_node( node ), m_property( property ), m_pickUniform( new osg::Uniform( "u_picked", 0.0f ) ), m_dir( negateDirection ? -1.0 : 1.0 ) { boost::shared_ptr< WGraphicsEngine > ge = WGraphicsEngine::getGraphicsEngine(); boost::shared_ptr< WGEViewer > viewer = ge->getViewerByName( "Main View" ); m_camera = viewer->getCamera(); m_pickConnection = viewer->getPickHandler()->getPickSignal()->connect( boost::bind( &WMNavigationSlices::PickCallback::pick, this, _1 ) ); node->getOrCreateStateSet()->addUniform( m_pickUniform ); } void WMNavigationSlices::PickCallback::pick( WPickInfo pickInfo ) { if( pickInfo.getName() == m_node->getName() ) { WVector3d normal = pickInfo.getPickNormal(); WVector2d newPixelPos = pickInfo.getPickPixel(); // dragging was initialized earlier if( m_isPicked ) { osg::Vec3 startPosScreen( m_oldPixelPosition[ 0 ], m_oldPixelPosition[ 1 ], 0.0 ); osg::Vec3 endPosScreen( newPixelPos[ 0 ], newPixelPos[ 1 ], 0.0 ); osg::Vec3 startPosWorld = wge::unprojectFromScreen( startPosScreen, m_camera ); osg::Vec3 endPosWorld = wge::unprojectFromScreen( endPosScreen, m_camera ); osg::Vec3 moveDirWorld = endPosWorld - startPosWorld; float diff = moveDirWorld * static_cast< osg::Vec3 >( normal ); 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; m_pickUniform->set( 1.0f ); } else // someone else got picked. { m_isPicked = false; m_pickUniform->set( 0.0f ); } } void WMNavigationSlices::moduleMain() { // get notified about data changes m_moduleState.setResetable( true, true ); // done. ready(); ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Slices Setup ///////////////////////////////////////////////////////////////////////////////////////////////////////// // create the root node for all the geometry m_output = osg::ref_ptr< WGEManagedGroupNode > ( new WGEManagedGroupNode( m_active ) ); // 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() ); WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_output ); // 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 ); } // 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 ) ); 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 ///////////////////////////////////////////////////////////////////////////////////////////////////////// // Main loop ///////////////////////////////////////////////////////////////////////////////////////////////////////// // we need to be informed if the bounding box of the volume containing all the data changes. m_moduleState.add( WGEColormapping::instance()->getChangeCondition() ); // main loop while( !m_shutdownFlag() ) { // woke up since the module is requested to finish? if( m_shutdownFlag() ) { break; } // create the slices. This loop is only entered if WGEColormapper was fired or shutdown. 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 ); 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 ); } }