//---------------------------------------------------------------------------
//
// 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
#include "../../common/WColor.h"
#include "../../common/WLogger.h"
#include "../../common/WPathHelper.h"
#include "../../dataHandler/WDataHandler.h"
#include "../../dataHandler/WDataTexture3D.h"
#include "../../dataHandler/WSubject.h"
#include "../../graphicsEngine/WGEUtils.h"
#include "../../kernel/WKernel.h"
#include "fiberdisplay2.xpm"
#include "WMFiberDisplay.h"
W_LOADABLE_MODULE( WMFiberDisplay )
WMFiberDisplay::WMFiberDisplay()
: WModule(),
m_noData( new WCondition, true ),
m_osgNode( osg::ref_ptr< osg::Group >() )
{
m_shaderTubes = osg::ref_ptr< WShader > ( new WShader( "WMFiberDisplay-FakeTubes" ) );
m_shaderTexturedFibers = osg::ref_ptr< WShader > ( new WShader( "WMFiberDisplay-Textured" ) );
m_textureChanged = true;
}
WMFiberDisplay::~WMFiberDisplay()
{
}
boost::shared_ptr< WModule > WMFiberDisplay::factory() const
{
return boost::shared_ptr< WModule >( new WMFiberDisplay() );
}
const char** WMFiberDisplay::getXPMIcon() const
{
return fiberdisplay2_xpm;
}
void WMFiberDisplay::connectors()
{
using boost::shared_ptr;
typedef WModuleInputData< const WDataSetFibers > FiberInputData; // just an alias
m_fiberInput = shared_ptr< FiberInputData >( new FiberInputData( shared_from_this(), "fiberInput", "A loaded fiber dataset." ) );
addConnector( m_fiberInput );
WModule::connectors(); // call WModules initialization
}
void WMFiberDisplay::activate()
{
if( m_osgNode )
{
if( m_active->get() )
{
m_osgNode->setNodeMask( 0xFFFFFFFF );
}
else
{
m_osgNode->setNodeMask( 0x0 );
}
}
WModule::activate();
}
void WMFiberDisplay::initUniforms( osg::StateSet* rootState )
{
m_uniformUseTexture = osg::ref_ptr( new osg::Uniform( "useTexture", false ) );
m_uniformSampler = osg::ref_ptr( new osg::Uniform( "tex", 0 ) );
m_uniformType = osg::ref_ptr( new osg::Uniform( "type", 0 ) );
m_uniformThreshold = osg::ref_ptr( new osg::Uniform( "threshold", 0.0f ) );
m_uniformsColorMap = osg::ref_ptr( new osg::Uniform( "cMap", 0 ) );
m_uniformDimX = osg::ref_ptr( new osg::Uniform( "dimX", 1 ) );
m_uniformDimY = osg::ref_ptr( new osg::Uniform( "dimY", 1 ) );
m_uniformDimZ = osg::ref_ptr( new osg::Uniform( "dimZ", 1 ) );
rootState->addUniform( m_uniformUseTexture );
rootState->addUniform( m_uniformSampler );
rootState->addUniform( m_uniformType );
rootState->addUniform( m_uniformThreshold );
rootState->addUniform( m_uniformsColorMap );
rootState->addUniform( m_uniformDimX );
rootState->addUniform( m_uniformDimY );
rootState->addUniform( m_uniformDimZ );
m_uniformTubeThickness = osg::ref_ptr( new osg::Uniform( "u_thickness", static_cast( m_tubeThickness->get() ) ) );
rootState->addUniform( m_uniformTubeThickness );
// cull box info
float xMin = m_cullBox->getMinPos()[0];
float yMin = m_cullBox->getMinPos()[1];
float zMin = m_cullBox->getMinPos()[2];
float xMax = m_cullBox->getMaxPos()[0];
float yMax = m_cullBox->getMaxPos()[1];
float zMax = m_cullBox->getMaxPos()[2];
m_uniformUseCullBox = osg::ref_ptr( new osg::Uniform( "useCullBox", false ) );
m_uniformInsideCullBox = osg::ref_ptr( new osg::Uniform( "insideCullBox", false ) );
m_uniformCullBoxLBX = osg::ref_ptr( new osg::Uniform( "cullBoxLBX", static_cast( xMin ) ) );
m_uniformCullBoxLBY = osg::ref_ptr( new osg::Uniform( "cullBoxLBY", static_cast( yMin ) ) );
m_uniformCullBoxLBZ = osg::ref_ptr( new osg::Uniform( "cullBoxLBZ", static_cast( zMin ) ) );
m_uniformCullBoxUBX = osg::ref_ptr( new osg::Uniform( "cullBoxUBX", static_cast( xMax ) ) );
m_uniformCullBoxUBY = osg::ref_ptr( new osg::Uniform( "cullBoxUBY", static_cast( yMax ) ) );
m_uniformCullBoxUBZ = osg::ref_ptr( new osg::Uniform( "cullBoxUBZ", static_cast( zMax ) ) );
rootState->addUniform( m_uniformUseCullBox );
rootState->addUniform( m_uniformInsideCullBox );
rootState->addUniform( m_uniformCullBoxLBX );
rootState->addUniform( m_uniformCullBoxLBY );
rootState->addUniform( m_uniformCullBoxLBZ );
rootState->addUniform( m_uniformCullBoxUBX );
rootState->addUniform( m_uniformCullBoxUBY );
rootState->addUniform( m_uniformCullBoxUBZ );
}
void WMFiberDisplay::initCullBox()
{
wmath::WPosition crossHairPos = WKernel::getRunningKernel()->getSelectionManager()->getCrosshair()->getPosition();
wmath::WPosition minROIPos = crossHairPos - wmath::WPosition( 10., 10., 10. );
wmath::WPosition maxROIPos = crossHairPos + wmath::WPosition( 10., 10., 10. );
m_cullBox = osg::ref_ptr< WROIBox >( new WROIBox( minROIPos, maxROIPos ) );
m_cullBox->setColor( osg::Vec4( 1.0, 0., 1.0, 0.4 ) );
}
void WMFiberDisplay::properties()
{
m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
m_useTubesProp = m_properties->addProperty( "Use tubes", "Draw fiber tracts as fake tubes.", false, m_propCondition );
m_useTextureProp = m_properties->addProperty( "Use texture", "Texture fibers with the texture on top of the list.", false, m_propCondition );
m_tubeThickness = m_properties->addProperty( "Tube thickness", "Adjusts the thickness of the tubes.", 50., m_propCondition );
m_tubeThickness->setMin( 0 );
m_tubeThickness->setMax( 300 );
m_save = m_properties->addProperty( "Save", "Saves the selected fiber bundles.", false, m_propCondition );
m_saveFileName = m_properties->addProperty( "File name", "", WPathHelper::getAppPath() );
m_cullBoxGroup = m_properties->addPropertyGroup( "Box Culling", "Properties only related to the box culling.", m_propCondition );
m_activateCullBox = m_cullBoxGroup->addProperty( "Activate", "Activates the cull box", false, m_propCondition );
m_showCullBox = m_cullBoxGroup->addProperty( "Show cull box", "Shows/hides the cull box", false, m_propCondition );
m_insideCullBox = m_cullBoxGroup->addProperty( "Inside - outside", "Show fibers inside or outside the cull box", true, m_propCondition );
}
void WMFiberDisplay::moduleMain()
{
// additional fire-condition: "data changed" flag
m_moduleState.setResetable( true, true );
m_moduleState.add( m_fiberInput->getDataChangedCondition() );
m_moduleState.add( m_propCondition );
m_moduleState.add( m_active->getUpdateCondition() );
// now, to watch changing/new textures use WSubject's change condition
boost::signals2::connection con = WDataHandler::getDefaultSubject()->getChangeCondition()->subscribeSignal(
boost::bind( &WMFiberDisplay::notifyTextureChange, this ) );
initCullBox();
m_cullBox->hide();
ready();
while ( !m_shutdownFlag() ) // loop until the module container requests the module to quit
{
m_moduleState.wait(); // waits for firing of m_moduleState ( dataChanged, shutdown, etc. )
if ( m_shutdownFlag() )
{
break;
}
// data changed?
if ( m_dataset != m_fiberInput->getData() )
{
inputUpdated();
}
if ( m_showCullBox->changed() )
{
if( m_showCullBox->get() )
{
m_cullBox->unhide();
}
else
{
m_cullBox->hide();
}
}
}
con.disconnect();
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_osgNode );
}
void WMFiberDisplay::inputUpdated()
{
// data has changed
// -> recalculate
debugLog() << "Data changed on " << m_fiberInput->getCanonicalName();
m_dataset = m_fiberInput->getData();
// ensure the data is valid (not NULL)
if ( !m_fiberInput->getData().get() ) // ok, the output has been reset, so we can ignore the "data change"
{
m_noData.set( true );
debugLog() << "Data reset on " << m_fiberInput->getCanonicalName() << ". Ignoring.";
return;
}
infoLog() << "Fiber dataset for display with: " << m_dataset->size() << " fibers loaded.";
if( m_dataset->size() != 0 ) // incase of an empty fiber dataset nothing is to display
{
boost::shared_ptr< WProgress > progress = boost::shared_ptr< WProgress >( new WProgress( "Fiber Display", 2 ) );
m_progress->addSubProgress( progress );
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->removeChild( m_osgNode.get() );
++*progress;
m_fiberSelector = boost::shared_ptr( new WFiberSelector( m_dataset ) );
++*progress;
create();
progress->finish();
m_noData.set( false );
}
else
{
warnLog() << "Nothing to display for an empty fiber dataset";
}
}
void WMFiberDisplay::create()
{
// create new node
osg::ref_ptr< osg::Group > osgNodeNew = osg::ref_ptr< osg::Group >( new osg::Group );
m_fiberDrawable = osg::ref_ptr< WFiberDrawable >( new WFiberDrawable );
m_fiberDrawable->setBoundingBox( osg::BoundingBox( m_dataset->getBoundingBox().first[0],
m_dataset->getBoundingBox().first[1],
m_dataset->getBoundingBox().first[2],
m_dataset->getBoundingBox().second[0],
m_dataset->getBoundingBox().second[1],
m_dataset->getBoundingBox().second[2] ) );
m_fiberDrawable->setStartIndexes( m_dataset->getLineStartIndexes() );
m_fiberDrawable->setPointsPerLine( m_dataset->getLineLengths() );
m_fiberDrawable->setVerts( m_dataset->getVertices() );
m_fiberDrawable->setTangents( m_dataset->getTangents() );
m_fiberDrawable->setColor( m_dataset->getGlobalColors() );
m_fiberDrawable->setBitfield( m_fiberSelector->getBitfield() );
m_fiberDrawable->setUseDisplayList( false );
m_fiberDrawable->setDataVariance( osg::Object::DYNAMIC );
osg::ref_ptr< osg::Geode > geode = osg::ref_ptr< osg::Geode >( new osg::Geode );
geode->addDrawable( m_fiberDrawable );
osgNodeNew->addChild( geode );
osgNodeNew->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
osgNodeNew->addUpdateCallback( new WGEFunctorCallback< osg::Node >( boost::bind( &WMFiberDisplay::updateCallback, this ) ) );
// remove previous nodes if there are any
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->removeChild( m_osgNode.get() );
m_osgNode = osgNodeNew;
activate();
osg::StateSet* rootState = m_osgNode->getOrCreateStateSet();
initUniforms( rootState );
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->addChild( m_osgNode.get() );
}
void WMFiberDisplay::update()
{
if( m_osgNode && m_noData.changed() )
{
if ( m_noData.get( true ) )
{
m_osgNode->setNodeMask( 0x0 );
}
else
{
m_osgNode->setNodeMask( 0xFFFFFFFF );
}
}
float xMin = m_cullBox->getMinPos()[0];
float yMin = m_cullBox->getMinPos()[1];
float zMin = m_cullBox->getMinPos()[2];
float xMax = m_cullBox->getMaxPos()[0];
float yMax = m_cullBox->getMaxPos()[1];
float zMax = m_cullBox->getMaxPos()[2];
m_uniformUseCullBox->set( m_activateCullBox->get() );
m_uniformInsideCullBox->set( m_insideCullBox->get() );
m_uniformCullBoxLBX->set( static_cast( xMin ) );
m_uniformCullBoxLBY->set( static_cast( yMin ) );
m_uniformCullBoxLBZ->set( static_cast( zMin ) );
m_uniformCullBoxUBX->set( static_cast( xMax ) );
m_uniformCullBoxUBY->set( static_cast( yMax ) );
m_uniformCullBoxUBZ->set( static_cast( zMax ) );
}
void WMFiberDisplay::updateRenderModes()
{
osg::StateSet* rootState = m_osgNode->getOrCreateStateSet();
if ( m_textureChanged )
{
m_textureChanged = false;
updateTexture();
}
if( m_useTubesProp->changed() || m_useTextureProp->changed() || m_activateCullBox->changed() )
{
if ( m_useTubesProp->get( true ) )
{
updateTexture();
m_fiberDrawable->setUseTubes( true );
m_shaderTubes->apply( m_osgNode );
m_uniformUseTexture->set( m_useTextureProp->get( true ) );
}
else if ( ( m_useTextureProp->get( true ) && !m_useTubesProp->get() ) || m_activateCullBox->get( true) )
{
m_fiberDrawable->setUseTubes( false );
updateTexture();
m_shaderTubes->deactivate( m_osgNode );
m_shaderTexturedFibers->apply( m_osgNode );
m_uniformUseTexture->set( m_useTextureProp->get() );
}
else
{
m_fiberDrawable->setUseTubes( false );
m_shaderTubes->deactivate( m_osgNode );
m_shaderTexturedFibers->deactivate( m_osgNode );
}
}
if ( !m_useTextureProp->get() && !m_useTubesProp->get() )
{
rootState->setTextureMode( 0, GL_TEXTURE_3D, osg::StateAttribute::OFF );
}
}
void WMFiberDisplay::saveSelected()
{
boost::shared_ptr< std::vector< bool > > active = m_fiberSelector->getBitfield();
m_dataset->saveSelected( m_saveFileName->getAsString(), active );
}
void WMFiberDisplay::updateTexture()
{
osg::StateSet* rootState = m_osgNode->getOrCreateStateSet();
// grab a list of data textures
std::vector< boost::shared_ptr< WDataTexture3D > > tex = WDataHandler::getDefaultSubject()->getDataTextures();
if ( tex.size() > 0 )
{
osg::ref_ptr texture3D = tex[0]->getTexture();
if ( tex[0]->isInterpolated() )
{
texture3D->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR );
texture3D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
}
else
{
texture3D->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST );
texture3D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::NEAREST );
}
rootState->setTextureAttributeAndModes( 0, texture3D, osg::StateAttribute::ON );
m_uniformType->set( tex[0]->getDataType() );
float minValue = tex[0]->getMinValue();
float maxValue = tex[0]->getMaxValue();
float thresh = ( tex[0]->getThreshold() - minValue ) / ( maxValue - minValue ); // rescale to [0,1]
m_uniformThreshold->set( thresh );
m_uniformsColorMap->set( tex[0]->getSelectedColormap() );
m_uniformDimX->set( static_cast( tex[0]->getGrid()->getNbCoordsX() ) );
m_uniformDimY->set( static_cast( tex[0]->getGrid()->getNbCoordsY() ) );
m_uniformDimZ->set( static_cast( tex[0]->getGrid()->getNbCoordsZ() ) );
}
}
void WMFiberDisplay::notifyTextureChange()
{
m_textureChanged = true;
}
void WMFiberDisplay::updateCallback()
{
update();
m_fiberDrawable->setColor( m_dataset->getColorScheme()->getColor() );
if ( m_tubeThickness->changed() && m_useTubesProp->get() )
{
m_uniformTubeThickness->set( static_cast( m_tubeThickness->get( true ) ) );
}
updateRenderModes();
}