Commit f9538d6a authored by Alexander Otte, Markus Jankowski, Michael Hamburger's avatar Alexander Otte, Markus Jankowski, Michael Hamburger
Browse files

[ADD] new module that can draw DVR or MIP projections on planes of the

bounding box of a scalar dataset
parent 183b0fd7
......@@ -2,6 +2,7 @@ Alois Schloegl
Andreas Schwarzkopf
André Reichenbach
Anne Berres
Alexander Otte
Christian Heine
Christof Pieloth
Cornelius Müller
......@@ -11,8 +12,10 @@ Gerald Sommer
Jan Forberg
Marcus Stuber
Mario Hlawitschka
Markus Jankowski
Matthias Berndt
Matthias Klose
Michael Hamburger
Patrick Oesterling
Robert Frohl
Robin Ledig
......
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2013 OpenWalnut Community, BSV-Leipzig and CNCF-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/>.
//
//---------------------------------------------------------------------------
#include <string>
#include <osg/Geometry>
#include <osg/ShapeDrawable>
#include <osg/Group>
#include <osg/Geode>
#include <osg/Material>
#include <osg/StateAttribute>
#include "core/dataHandler/WDataSetScalar.h"
#include "core/graphicsEngine/callbacks/WGELinearTranslationCallback.h"
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
#include "core/graphicsEngine/shaders/WGEShader.h"
#include "core/graphicsEngine/WGEGeodeUtils.h"
#include "core/graphicsEngine/WGEManagedGroupNode.h"
#include "core/kernel/WKernel.h"
#include "core/kernel/WModuleInputData.h"
#include "core/graphicsEngine/WGEUtils.h"
#include "core/graphicsEngine/WGEColormapping.h"
#include "WMProject.xpm"
#include "WMProject.h"
// This line is needed by the module loader to actually find your module.
W_LOADABLE_MODULE( WMProject )
WMProject::WMProject():
WModule(),
m_propCondition( new WCondition() )
{
}
WMProject::~WMProject()
{
}
boost::shared_ptr< WModule > WMProject::factory() const
{
return boost::shared_ptr< WModule >( new WMProject() );
}
const char** WMProject::getXPMIcon() const
{
return WMProject_xpm;
}
const std::string WMProject::getName() const
{
return "[EDU] Project";
}
const std::string WMProject::getDescription() const
{
return "This module draws projections by using different algorithms on a bounding box (Shader Deluxxe Edition).";
}
void WMProject::connectors()
{
m_scalarIC = WModuleInputData< WDataSetScalar >::createAndAdd( shared_from_this(), "scalarData", "Scalar Data." );
m_transferFunction = WModuleInputData< WDataSetSingle >::createAndAdd( shared_from_this(), "transfer function", "1D Transfer Function." );
WModule::connectors();
}
void WMProject::properties()
{
m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
m_possibleSelectionsUsingTypes = WItemSelection::SPtr( new WItemSelection() );
m_possibleSelectionsUsingTypes->addItem(
MyItemType::create(
NONE,
"None",
"Do not show this plane."
)
);
m_possibleSelectionsUsingTypes->addItem(
MyItemType::create(
MIP,
"MIP Algorithm",
"Show this plane using the MIP Algorithm."
)
);
m_possibleSelectionsUsingTypes->addItem(
MyItemType::create(
COMPOSITING_F2B,
"Compositing (Front-to-Back) Algorithm",
"Show this plane using the Compositing Algorithm (F2B)."
)
);
m_possibleSelectionsUsingTypes->addItem(
MyItemType::create(
COMPOSITING_B2F,
"Compositing (Back-to-Front) Algorithm",
"Show this plane using the Compositing Algorithm (B2F)."
)
);
// add items to dropdown menu
m_singleSelectionAxialTop = m_properties->addProperty( "Axial Top", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
m_singleSelectionAxialBottom = m_properties->addProperty( "Axial Bottom", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
m_singleSelectionCoronalTop = m_properties->addProperty( "Coronal Top", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
m_singleSelectionCoronalBottom = m_properties->addProperty( "Coronal Bottom", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
m_singleSelectionSagittalTop = m_properties->addProperty( "Sagittal Top", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
m_singleSelectionSagittalBottom = m_properties->addProperty( "Sagittal Bottom", "Choose action for this Plane.", m_possibleSelectionsUsingTypes->getSelector(0), m_propCondition );
// specify single selection
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionAxialTop );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionAxialTop );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionAxialBottom );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionAxialBottom );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionCoronalTop );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionCoronalTop );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionCoronalBottom );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionCoronalBottom );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionSagittalTop );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionSagittalTop );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_singleSelectionSagittalBottom );
WPropertyHelper::PC_NOTEMPTY::addTo( m_singleSelectionSagittalBottom );
m_earlyRayTerminationAlpha = m_properties->addProperty(
"Early Ray Termination",
"Used by Compositing Front-to-Back.",
1.0,
m_propCondition );
m_earlyRayTerminationAlpha->setMin(0.0);
m_earlyRayTerminationAlpha->setMax(1.0);
WModule::properties();
}
void WMProject::requirements()
{
}
void WMProject::moduleMain()
{
infoLog() << "Thrsholding example main routine started";
// get notified about data changes
m_moduleState.setResetable( true, true );
m_moduleState.add( m_scalarIC->getDataChangedCondition() );
m_moduleState.add( m_transferFunction->getDataChangedCondition() );
m_moduleState.add( m_propCondition );
// Information output
m_maxCoordsPosition = m_infoProperties->addProperty("Max Coords", "", WPosition(0,0,0));
m_normalOut = m_infoProperties->addProperty("Normal", "", WPosition(0,0,0));
m_planeTypes[0] = m_infoProperties->addProperty("Type Axial Top","",0);
m_planeTypes[1] = m_infoProperties->addProperty("Type Axial Bottom","",0);
m_planeTypes[2] = m_infoProperties->addProperty("Type Coronal Top","",0);
m_planeTypes[3] = m_infoProperties->addProperty("Type Coronal Bottom","",0);
m_planeTypes[4] = m_infoProperties->addProperty("Type Sagittal Top","",0);
m_planeTypes[5] = m_infoProperties->addProperty("Type Sagittal Bottom","",0);
ready();
// graphics setup
m_rootNode = osg::ref_ptr< WGEManagedGroupNode >( new WGEManagedGroupNode( m_active ) );
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode );
m_geode = new osg::Geode();
// create shader
m_shader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMProject", m_localPath ) );
m_geode->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
m_geode->getOrCreateStateSet()->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
// Apply colormapping
WGEColormapping::apply( m_geode, m_shader );
// add geode to rootnode so that the graphics will be used/drawn in OpenWalnut
m_rootNode->insert( m_geode );
// initialize a "list" to save the plane selection
std::vector<osg::ref_ptr<osg::Geometry> > planeList;
for(int i = 0; i < 6; i++)
planeList.push_back(NULL);
// for compositing
osg::ref_ptr< osg::Image > tfImage = new osg::Image();
osg::ref_ptr< osg::Texture1D > tfTexture = new osg::Texture1D();
tfTexture->setDataVariance( osg::Object::DYNAMIC );
tfTexture->setImage( tfImage );
// bind this texture so that we can use it in our shader
wge::bindTexture( m_geode, tfTexture, 3, "u_transferFunction" );
// main loop
while( !m_shutdownFlag() )
{
infoLog() << "Waiting ...";
m_moduleState.wait();
// woke up since the module is requested to finish?
if( m_shutdownFlag() )
{
break;
}
// save data behind connectors since it might change during processing
scalarData = m_scalarIC->getData();
if( !scalarData )
{
continue;
}
// extract size of grid - needed for step size in shader (sampling rate)
WDataSetSingle::SPtr dsSingle = boost::dynamic_pointer_cast< WDataSetSingle >( scalarData );
WGridRegular3D::SPtr regGrid = boost::dynamic_pointer_cast< WGridRegular3D >( dsSingle->getGrid() );
int X,Y,Z;
X = regGrid->getNbCoordsX();
Y = regGrid->getNbCoordsY();
Z = regGrid->getNbCoordsZ();
m_maxCoordsPosition->set(WPosition(X,Y,Z));
// save selection of each dropdown box
int selection[6];
selection[0] = (int)m_singleSelectionAxialTop->get().at(0)->getAs<MyItemType>()->getValue();
selection[1] = (int)m_singleSelectionAxialBottom->get().at(0)->getAs<MyItemType>()->getValue();
selection[2] = (int)m_singleSelectionCoronalTop->get().at(0)->getAs<MyItemType>()->getValue();
selection[3] = (int)m_singleSelectionCoronalBottom->get().at(0)->getAs<MyItemType>()->getValue();
selection[4] = (int)m_singleSelectionSagittalTop->get().at(0)->getAs<MyItemType>()->getValue();
selection[5] = (int)m_singleSelectionSagittalBottom->get().at(0)->getAs<MyItemType>()->getValue();
for(int i = 0; i < 6; i++)
m_planeTypes[i]->set(selection[i]);
// bind some uniforms with properties
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropPosition >( "maxCoords", m_maxCoordsPosition ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "axialTop", m_planeTypes[0] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "axialBottom", m_planeTypes[1] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "coronalTop", m_planeTypes[2] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "coronalBottom", m_planeTypes[3] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "sagittalTop", m_planeTypes[4] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropInt >( "sagittalBottom", m_planeTypes[5] ) );
m_geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "earlyRayTerminationAlpha", m_earlyRayTerminationAlpha ) );
// remove all selected planes
for(int i = 0; i < 6; i++)
m_geode->removeDrawable(planeList[i]);
osg::ref_ptr<osg::Geometry> planeGeometry = NULL;
for(int i = 0; i < 6; i++)
{
planeList[i] = NULL;
if(selection[i] != 0)
{
planeList[i] = osg::ref_ptr<osg::Geometry>( new osg::Geometry() );
planeGeometry = planeList[i];
osg::Vec3Array* planeVertices = new osg::Vec3Array;
osg::Vec3Array* planeNormales = new osg::Vec3Array;
WPosition normal(0,0,1);
switch((int)floor(i/2.0f))
{
case 0:
{
WPosition tempV0(0-10,(i%2==0)?0-10:Y+10,0-10);
WPosition tempV1(X+10,(i%2==0)?0-10:Y+10,0-10);
WPosition tempV2(X+10,(i%2==0)?0-10:Y+10,Z+10);
WPosition tempV3(0-10,(i%2==0)?0-10:Y+10,Z+10);
planeVertices->push_back( tempV0 );
planeVertices->push_back( tempV1 );
planeVertices->push_back( tempV2 );
planeVertices->push_back( tempV3 );
normal[0] = normal[2] = 0;
normal[1] = 1;
if(i%2!=0)
normal[1] = -1;
}
break; // Axial
case 1:
{
WPosition tempV0(0-10,0-10,(i%2==0)?0-10:Z+10);
WPosition tempV1(X+10,0-10,(i%2==0)?0-10:Z+10);
WPosition tempV2(X+10,Y+10,(i%2==0)?0-10:Z+10);
WPosition tempV3(0-10,Y+10,(i%2==0)?0-10:Z+10);
planeVertices->push_back( tempV0 );
planeVertices->push_back( tempV1 );
planeVertices->push_back( tempV2 );
planeVertices->push_back( tempV3 );
normal[0] = normal[1] = 0;
normal[2] = 1;
if(i%2!=0)
normal[2] = -1;
}
break; // Coronal
case 2:
{
WPosition tempV0((i%2==0)?0-10:X+10,0-10,0-10);
WPosition tempV1((i%2==0)?0-10:X+10,0-10,Z+10);
WPosition tempV2((i%2==0)?0-10:X+10,Y+10,Z+10);
WPosition tempV3((i%2==0)?0-10:X+10,Y+10,0-10);
planeVertices->push_back( tempV0 );
planeVertices->push_back( tempV1 );
planeVertices->push_back( tempV2 );
planeVertices->push_back( tempV3 );
normal[1] = normal[2] = 0;
normal[0] = 1;
if(i%2!=0)
normal[0] = -1;
}
break; // Sagittal
default:
break; // error occured
}
// add 4 normals to this plane. One for each vertex because of BIND_PER_VERTEX
planeNormales->push_back(normal);
planeNormales->push_back(normal);
planeNormales->push_back(normal);
planeNormales->push_back(normal);
planeGeometry->setVertexArray( planeVertices );
planeGeometry->setNormalArray(planeNormales);
planeGeometry->setNormalBinding((osg::Geometry::AttributeBinding)4);
// add plane to geode
osg::DrawElementsUInt* quad = new osg::DrawElementsUInt( osg::PrimitiveSet::QUADS, 0 );
quad->push_back( 3 );
quad->push_back( 2 );
quad->push_back( 1 );
quad->push_back( 0 );
planeGeometry->addPrimitiveSet( quad );
m_geode->addDrawable( planeGeometry );
}
}
if(m_transferFunction->updated())
{
wge::bindTexture( m_geode, tfTexture, 3, "u_transferFunction" );
boost::shared_ptr< WDataSetSingle > dataSet = m_transferFunction->getData();
if( !dataSet )
{
debugLog() << "no data set?";
}
else
{
WAssert( dataSet, "data set" );
boost::shared_ptr< WValueSetBase > valueSet = dataSet->getValueSet();
WAssert( valueSet, "value set" );
boost::shared_ptr< WValueSet< unsigned char > > cvalueSet(
boost::dynamic_pointer_cast<WValueSet< unsigned char> >( valueSet )
);
if( !cvalueSet )
{
debugLog() << "invalid type";
}
else
{
size_t tfsize = cvalueSet->rawSize();
// create image and copy the TF
tfImage->allocateImage( tfsize/4, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE );
tfImage->setInternalTextureFormat( GL_RGBA );
unsigned char* data = reinterpret_cast< unsigned char* >( tfImage->data() );
std::copy( cvalueSet->rawData(), &cvalueSet->rawData()[ tfsize ], data );
// force OpenGl to use the new texture
tfTexture->dirtyTextureObject();
}
}
}
}
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode );
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2013 OpenWalnut Community, BSV-Leipzig and CNCF-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/>.
//
//---------------------------------------------------------------------------
#ifndef WMProject_H
#define WMProject_H
#include <string>
#include "core/kernel/WModule.h"
#include "core/common/WItemSelection.h"
#include "core/common/WItemSelectionItem.h"
#include "core/common/WItemSelectionItemTyped.h"
#include "core/common/WItemSelector.h"
#include "core/common/WPropertyHelper.h"
#include <osg/Geode>
#include "core/kernel/WModule.h"
#include "core/kernel/WModuleInputData.h"
#include "core/kernel/WModuleOutputData.h"
enum PlaneType
{
NONE, MIP, COMPOSITING_F2B, COMPOSITING_B2F
};
// forward declarations to reduce compile dependencies
template< class T > class WModuleInputData;
class WDataSetScalar;
class WGEManagedGroupNode;
/**
* Computes contour lines (aka isolines) for the given data and render them on a 2D plane.
* \ingroup modules
*/
class WMProject: public WModule
{
public:
/**
* Creates the module for drawing contour lines.
*/
WMProject();
/**
* Destroys this module.
*/
virtual ~WMProject();
/**
* Gives back the name of this module.
* \return the module's name.
*/
virtual const std::string getName() const;
/**
* Gives back a description of this module.
* \return description to module.
*/
virtual const std::string getDescription() const;
/**
* Due to the prototype design pattern used to build modules, this method returns a new instance of this method. NOTE: it
* should never be initialized or modified in some other way. A simple new instance is required.
*
* \return the prototype used to create every module in OpenWalnut.
*/
virtual boost::shared_ptr< WModule > factory() const;
/**
* Get the icon for this module in XPM format.
*
* \return The icon.
*/
virtual const char** getXPMIcon() const;
protected:
/**
* Entry point after loading the module. Runs in separate thread.
*/
virtual void moduleMain();
/**
* Initialize the connectors this module is using.
*/
virtual void connectors();
/**
* Initialize the properties for this module.
*/
virtual void properties();
/**
* Initialize requirements for this module.
*/
virtual void requirements();
osg::ref_ptr< osg::Geode > m_geode;
private:
/**
* Input connector for scalar data.
*/
boost::shared_ptr< WModuleInputData< WDataSetScalar > > m_scalarIC;
/**
* The transfer function as an input data set
*/
boost::shared_ptr< WModuleInputData< WDataSetSingle > > m_transferFunction;
/**
* The OSG root node for this module. All other geodes or OSG nodes will be attached on this single node.
*/
osg::ref_ptr< WGEManagedGroupNode > m_rootNode;
/**
* Needed for recreating the geometry, incase when resolution changes.
*/
boost::shared_ptr< WCondition > m_propCondition;
typedef WItemSelectionItemTyped< PlaneType > MyItemType;
boost::shared_ptr< WItemSelection > m_possibleSelectionsUsingTypes;
WPropSelection m_singleSelectionAxialTop, m_singleSelectionAxialBottom,
m_singleSelectionCoronalTop, m_singleSelectionCoronalBottom,
m_singleSelectionSagittalTop, m_singleSelectionSagittalBottom;
WPropDouble m_earlyRayTerminationAlpha;
// Output
WPropPosition m_maxCoordsPosition;
WPropPosition m_normalOut;
WPropInt m_planeTypes[6];
osg::ref_ptr< WGEShader > m_shader;
boost::shared_ptr< WDataSetScalar > scalarData;
};
#endif // WMProject_H
/* XPM */
static const char * WMProject_xpm[] = {
"32 32 312 2",
" c None",
". c #FCAF60",
"+ c #F8A859",
"@ c #F6A555",
"# c #F5A351",
"$ c #F4A14E",
"% c #F29F4C",
"& c #F19E4B",
"* c #F09C48",