WMMarchingCubes.cpp 30.2 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 25
//---------------------------------------------------------------------------
//
// 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/>.
//
//---------------------------------------------------------------------------

#include <iostream>
26
#include <fstream>
Alexander Wiebel's avatar
Alexander Wiebel committed
27
#include <string>
28
#include <vector>
29
#include <limits>
30

31
#include "WMMarchingCubes.xpm"
32
#include "../../common/WLimits.h"
33
#include "../../common/WAssert.h"
34

35 36
#include <osg/Geode>
#include <osg/Geometry>
37
#include <osg/Material>
38 39 40
#include <osg/StateSet>
#include <osg/StateAttribute>
#include <osg/PolygonMode>
41 42
#include <osg/LightModel>
#include <osgDB/WriteFile>
43

44
#include "../../common/WPathHelper.h"
Alexander Wiebel's avatar
Alexander Wiebel committed
45
#include "../../common/WProgress.h"
46
#include "../../common/WPreferences.h"
47
#include "../../common/math/WVector3D.h"
48
#include "../../common/math/WLinearAlgebraFunctions.h"
49
#include "../../common/math/WMath.h"
50
#include "../../dataHandler/WDataHandler.h"
51
#include "../../dataHandler/WSubject.h"
52
#include "../../dataHandler/WDataTexture3D.h"
53
#include "../../graphicsEngine/WGEUtils.h"
54
#include "../../kernel/WKernel.h"
55

56
#include "../../graphicsEngine/algorithms/WMarchingCubesAlgorithm.h"
57 58
#include "WMMarchingCubes.h"

59 60
// This line is needed by the module loader to actually find your module.
W_LOADABLE_MODULE( WMMarchingCubes )
61

62
WMMarchingCubes::WMMarchingCubes():
63
    WModule(),
64
    m_recompute( boost::shared_ptr< WCondition >( new WCondition() ) ),
65
    m_dataSet(),
66
    m_shaderUseLighting( false ),
67
    m_shaderUseTransparency( false ),
68
    m_moduleNodeInserted( false ),
69
    m_surfaceGeode( 0 )
70 71 72 73 74
{
    // WARNING: initializing connectors inside the constructor will lead to an exception.
    // Implement WModule::initializeConnectors instead.
}

75
WMMarchingCubes::~WMMarchingCubes()
76 77 78 79 80
{
    // cleanup
    removeConnectors();
}

81 82 83 84 85
boost::shared_ptr< WModule > WMMarchingCubes::factory() const
{
    return boost::shared_ptr< WModule >( new WMMarchingCubes() );
}

86 87 88 89 90
const char** WMMarchingCubes::getXPMIcon() const
{
    return iso_surface_xpm;
}

91
const std::string WMMarchingCubes::getName() const
92
{
93
    return "Isosurface";
94 95
}

96
const std::string WMMarchingCubes::getDescription() const
97
{
98
    return "This module implements the marching cubes"
99
" algorithm with a consistent triangulation. It allows to compute isosurfaces"
100
" for a given isovalue on data given on a grid only consisting of cubes. It yields"
Alexander Wiebel's avatar
Alexander Wiebel committed
101
" the surface as triangle soup.";
102 103
}

104
void WMMarchingCubes::moduleMain()
105
{
106
    // use the m_input "data changed" flag
107
    m_moduleState.setResetable( true, true );
108
    m_moduleState.add( m_input->getDataChangedCondition() );
109
    m_moduleState.add( m_recompute );
110

111 112 113
    // signal ready state
    ready();

114 115
    m_moduleNode = new WGEManagedGroupNode( m_active );

116 117 118 119 120
    // now, to watch changing/new textures use WSubject's change condition
    boost::signals2::connection con = WDataHandler::getDefaultSubject()->getChangeCondition()->subscribeSignal(
            boost::bind( &WMMarchingCubes::notifyTextureChange, this )
    );

121 122
    // loop until the module container requests the module to quit
    while ( !m_shutdownFlag() )
123
    {
124
        if ( !m_input->getData().get() )
125
        {
126
            // OK, the output has not yet sent data
127
            // NOTE: see comment at the end of this while loop for m_moduleState
128
            debugLog() << "Waiting for data ...";
129 130 131
            m_moduleState.wait();
            continue;
        }
132

133 134 135 136 137
        if( m_dataSet != m_input->getData() )
        {
            // acquire data from the input connector
            m_dataSet = m_input->getData();

138 139 140 141
            // set appropriate constraints for properties
            m_isoValueProp->setMin( m_dataSet->getMin() );
            m_isoValueProp->setMax( m_dataSet->getMax() );

142
            if( m_isoValueProp->get() >= m_dataSet->getMax() || m_isoValueProp->get() <= m_dataSet->getMin() )
143 144 145
            {
                m_isoValueProp->set( 0.5 * ( m_dataSet->getMax() +  m_dataSet->getMin() ), true );
            }
146
        }
147

148
        // update isosurface
149
        debugLog() << "Computing surface ...";
150

151 152 153
        boost::shared_ptr< WProgress > progress = boost::shared_ptr< WProgress >( new WProgress( "Marching Cubes", 2 ) );
        m_progress->addSubProgress( progress );

154
        generateSurfacePre( m_isoValueProp->get() );
155

156
        ++*progress;
157
        debugLog() << "Rendering surface ...";
158

159 160
        // settings for normal isosurface
        m_shaderUseLighting = true;
161
        m_shaderUseTransparency = true;
Alexander Wiebel's avatar
Alexander Wiebel committed
162

163 164 165
        renderMesh();
        m_output->updateData( m_triMesh );

166
        debugLog() << "Done!";
167
        progress->finish();
Alexander Wiebel's avatar
Alexander Wiebel committed
168

169 170 171 172
        // this waits for m_moduleState to fire. By default, this is only the m_shutdownFlag condition.
        // NOTE: you can add your own conditions to m_moduleState using m_moduleState.add( ... )
        m_moduleState.wait();
    }
173 174

    WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getScene()->remove( m_moduleNode );
175 176
}

177
void WMMarchingCubes::connectors()
178 179
{
    // initialize connectors
180
    m_input = boost::shared_ptr< WModuleInputData < WDataSetScalar > >(
181
        new WModuleInputData< WDataSetScalar >( shared_from_this(),
182
                                                               "values", "Dataset to compute isosurface for." )
183 184 185 186 187
        );

    // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
    addConnector( m_input );

188 189
    m_output = boost::shared_ptr< WModuleOutputData< WTriangleMesh > >(
            new WModuleOutputData< WTriangleMesh >( shared_from_this(), "surface mesh", "The mesh representing the isosurface." ) );
190 191 192

    addConnector( m_output );

193 194 195 196
    // call WModules initialization
    WModule::connectors();
}

197
void WMMarchingCubes::properties()
198
{
199 200 201 202 203 204
    m_nbTriangles = m_infoProperties->addProperty( "Triangles", "The number of triangles in the produced mesh.", 0 );
    m_nbTriangles->setMax( std::numeric_limits< int >::max() );

    m_nbVertices = m_infoProperties->addProperty( "Vertices", "The number of vertices in the produced mesh.", 0 );
    m_nbVertices->setMax( std::numeric_limits< int >::max() );

205
    m_isoValueProp = m_properties->addProperty( "Iso value", "The surface will show the area that has this value.", 100., m_recompute );
206 207
    m_isoValueProp->setMin( wlimits::MIN_DOUBLE );
    m_isoValueProp->setMax( wlimits::MAX_DOUBLE );
208 209 210 211 212 213 214 215 216
    {
        // If set in config file use standard isovalue from config file
        double tmpIsoValue;
        if( WPreferences::getPreference( "modules.MC.isoValue", &tmpIsoValue ) )
        {
            m_isoValueProp->set( tmpIsoValue );
        }
    }

217
    m_opacityProp = m_properties->addProperty( "Opacity %", "Opaqueness of surface.", 100 );
218 219
    m_opacityProp->setMin( 0 );
    m_opacityProp->setMax( 100 );
220

221
    m_useTextureProp = m_properties->addProperty( "Use texture", "Use texturing of the surface?", false );
222

223
    m_surfaceColor = m_properties->addProperty( "Surface color", "Description.", WColor( 0.5, 0.5, 0.5, 1.0 ) );
224 225 226 227 228 229

    m_savePropGroup = m_properties->addPropertyGroup( "Save Surface",  "" );
    m_saveTriggerProp = m_savePropGroup->addProperty( "Do Save",  "Press!",
                                                  WPVBaseTypes::PV_TRIGGER_READY );
    m_saveTriggerProp->getCondition()->subscribeSignal( boost::bind( &WMMarchingCubes::save, this ) );

230
    m_meshFile = m_savePropGroup->addProperty( "Mesh File", "", WPathHelper::getAppPath() );
231 232

    WModule::properties();
233
}
234

235 236
void WMMarchingCubes::generateSurfacePre( double isoValue )
{
237
    debugLog() << "Isovalue: " << isoValue;
238
    WAssert( ( *m_dataSet ).getValueSet()->order() == 0, "This module only works on scalars." );
239 240 241 242 243 244

    WMarchingCubesAlgorithm mcAlgo;
    boost::shared_ptr< WGridRegular3D > gridRegular3D = boost::shared_dynamic_cast< WGridRegular3D >( ( *m_dataSet ).getGrid() );
    WAssert( gridRegular3D, "Grid is not of type WGridRegular3D." );
    m_grid = gridRegular3D;

245 246 247 248 249 250
    switch( (*m_dataSet).getValueSet()->getDataType() )
    {
        case W_DT_UNSIGNED_CHAR:
        {
            boost::shared_ptr< WValueSet< unsigned char > > vals;
            vals =  boost::shared_dynamic_cast< WValueSet< unsigned char > >( ( *m_dataSet ).getValueSet() );
251
            WAssert( vals, "Data type and data type indicator must fit." );
252 253 254 255 256
            m_triMesh = mcAlgo.generateSurface( m_grid->getNbCoordsX(), m_grid->getNbCoordsY(), m_grid->getNbCoordsZ(),
                                                m_grid->getTransformationMatrix(),
                                                vals->rawDataVectorPointer(),
                                                isoValue,
                                                m_progress );
257 258 259 260 261 262
            break;
        }
        case W_DT_INT16:
        {
            boost::shared_ptr< WValueSet< int16_t > > vals;
            vals =  boost::shared_dynamic_cast< WValueSet< int16_t > >( ( *m_dataSet ).getValueSet() );
263
            WAssert( vals, "Data type and data type indicator must fit." );
264 265 266 267 268
            m_triMesh = mcAlgo.generateSurface( m_grid->getNbCoordsX(), m_grid->getNbCoordsY(), m_grid->getNbCoordsZ(),
                                                m_grid->getTransformationMatrix(),
                                                vals->rawDataVectorPointer(),
                                                isoValue,
                                                m_progress );
269 270
            break;
        }
271 272 273 274
        case W_DT_SIGNED_INT:
        {
            boost::shared_ptr< WValueSet< int32_t > > vals;
            vals =  boost::shared_dynamic_cast< WValueSet< int32_t > >( ( *m_dataSet ).getValueSet() );
275
            WAssert( vals, "Data type and data type indicator must fit." );
276 277 278 279 280
            m_triMesh = mcAlgo.generateSurface( m_grid->getNbCoordsX(), m_grid->getNbCoordsY(), m_grid->getNbCoordsZ(),
                                                m_grid->getTransformationMatrix(),
                                                vals->rawDataVectorPointer(),
                                                isoValue,
                                                m_progress );
281 282
            break;
        }
283 284 285 286
        case W_DT_FLOAT:
        {
            boost::shared_ptr< WValueSet< float > > vals;
            vals =  boost::shared_dynamic_cast< WValueSet< float > >( ( *m_dataSet ).getValueSet() );
287
            WAssert( vals, "Data type and data type indicator must fit." );
288 289 290 291 292
            m_triMesh = mcAlgo.generateSurface( m_grid->getNbCoordsX(), m_grid->getNbCoordsY(), m_grid->getNbCoordsZ(),
                                                m_grid->getTransformationMatrix(),
                                                vals->rawDataVectorPointer(),
                                                isoValue,
                                                m_progress );
293 294
            break;
        }
295 296 297 298
        case W_DT_DOUBLE:
        {
            boost::shared_ptr< WValueSet< double > > vals;
            vals =  boost::shared_dynamic_cast< WValueSet< double > >( ( *m_dataSet ).getValueSet() );
299
            WAssert( vals, "Data type and data type indicator must fit." );
300 301 302 303 304
            m_triMesh = mcAlgo.generateSurface( m_grid->getNbCoordsX(), m_grid->getNbCoordsY(), m_grid->getNbCoordsZ(),
                                                m_grid->getTransformationMatrix(),
                                                vals->rawDataVectorPointer(),
                                                isoValue,
                                                m_progress );
305 306
            break;
        }
307
        default:
308
            WAssert( false, "Unknow data type in MarchingCubes module" );
309
    }
310 311 312 313

    // Set the info properties
    m_nbTriangles->set( m_triMesh->triangleSize() );
    m_nbVertices->set( m_triMesh->vertSize() );
314 315
}

316
void WMMarchingCubes::renderMesh()
317
{
318 319 320 321 322 323 324 325 326 327
    {
        // Remove the previous node in a thread save way.
        boost::unique_lock< boost::shared_mutex > lock;
        lock = boost::unique_lock< boost::shared_mutex >( m_updateLock );

        m_moduleNode->remove( m_surfaceGeode );

        lock.unlock();
    }

328
    osg::Geometry* surfaceGeometry = new osg::Geometry();
329
    m_surfaceGeode = osg::ref_ptr< osg::Geode >( new osg::Geode );
330

331 332
    m_surfaceGeode->setName( "iso surface" );

333
    surfaceGeometry->setVertexArray( m_triMesh->getVertexArray() );
334 335 336
    osg::DrawElementsUInt* surfaceElement;

    surfaceElement = new osg::DrawElementsUInt( osg::PrimitiveSet::TRIANGLES, 0 );
337

338
    std::vector< size_t >tris = m_triMesh->getTriangles();
339 340 341
    surfaceElement->reserve( tris.size() );

    for( unsigned int vertId = 0; vertId < tris.size(); ++vertId )
342
    {
343
        surfaceElement->push_back( tris[vertId] );
344 345 346
    }
    surfaceGeometry->addPrimitiveSet( surfaceElement );

347
    // ------------------------------------------------
348
    // normals
349
    surfaceGeometry->setNormalArray( m_triMesh->getVertexNormalArray() );
350 351
    surfaceGeometry->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );

352 353
    m_surfaceGeode->addDrawable( surfaceGeometry );
    osg::StateSet* state = m_surfaceGeode->getOrCreateStateSet();
354

355 356
    // ------------------------------------------------
    // colors
357
    osg::Vec4Array* colors = new osg::Vec4Array;
358

359 360
    WColor c = m_surfaceColor->get( true );
    colors->push_back( osg::Vec4( c.getRed(), c.getGreen(), c.getBlue(), 1.0f ) );
361 362 363
    surfaceGeometry->setColorArray( colors );
    surfaceGeometry->setColorBinding( osg::Geometry::BIND_OVERALL );

364 365 366
    osg::ref_ptr<osg::LightModel> lightModel = new osg::LightModel();
    lightModel->setTwoSided( true );
    state->setAttributeAndModes( lightModel.get(), osg::StateAttribute::ON );
367 368 369 370 371 372 373 374 375 376
    {
        osg::ref_ptr< osg::Material > material = new osg::Material();
        material->setDiffuse(   osg::Material::FRONT, osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
        material->setSpecular(  osg::Material::FRONT, osg::Vec4( 0.0, 0.0, 0.0, 1.0 ) );
        material->setAmbient(   osg::Material::FRONT, osg::Vec4( 0.1, 0.1, 0.1, 1.0 ) );
        material->setEmission(  osg::Material::FRONT, osg::Vec4( 0.0, 0.0, 0.0, 1.0 ) );
        material->setShininess( osg::Material::FRONT, 25.0 );
        state->setAttribute( material );
    }

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    // Enable blending, select transparent bin.
    if ( m_shaderUseTransparency )
    {
        state->setMode( GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON );
        state->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );

        // Enable depth test so that an opaque polygon will occlude a transparent one behind it.
        state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON );

        // Conversely, disable writing to depth buffer so that
        // a transparent polygon will allow polygons behind it to shine thru.
        // OSG renders transparent polygons after opaque ones.
        osg::Depth* depth = new osg::Depth;
        depth->setWriteMask( false );
        state->setAttributeAndModes( depth, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON );

        // Disable conflicting modes.
        state->setMode( GL_LIGHTING,  osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
        state->setMode( GL_CULL_FACE, osg::StateAttribute::OFF );
    }

schurade's avatar
schurade committed
398 399 400
    surfaceGeometry->setUseDisplayList( false );
    surfaceGeometry->setUseVertexBufferObjects( true );

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    // ------------------------------------------------
    // Shader stuff

    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type0", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type1", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type2", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type3", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type4", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type5", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type6", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type7", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type8", 0 ) ) );
    m_typeUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "type9", 0 ) ) );

    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha0", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha1", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha2", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha3", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha4", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha5", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha6", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha7", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha8", 1.0f ) ) );
    m_alphaUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "alpha9", 1.0f ) ) );

    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold0", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold1", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold2", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold3", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold4", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold5", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold6", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold7", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold8", 0.0f ) ) );
    m_thresholdUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "threshold9", 0.0f ) ) );

    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex0", 0 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex1", 1 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex2", 2 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex3", 3 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex4", 4 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex5", 5 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex6", 6 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex7", 7 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex8", 8 ) ) );
    m_samplerUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "tex9", 9 ) ) );

448 449 450 451 452 453 454 455 456 457 458
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap0", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap1", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap2", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap3", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap4", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap5", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap6", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap7", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap8", 0 ) ) );
    m_cmapUniforms.push_back( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useCmap9", 0 ) ) );

459
    for ( int i = 0; i < m_maxNumberOfTextures; ++i )
460 461 462 463 464
    {
        state->addUniform( m_typeUniforms[i] );
        state->addUniform( m_thresholdUniforms[i] );
        state->addUniform( m_alphaUniforms[i] );
        state->addUniform( m_samplerUniforms[i] );
465
        state->addUniform( m_cmapUniforms[i] );
466 467 468
    }

    state->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useLighting", m_shaderUseLighting ) ) );
469 470
    if( m_shaderUseTransparency )
    {
471
        state->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "opacity", m_opacityProp->get( true ) ) ) );
472 473 474 475 476
    }
    else
    {
        state->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "opacity", 100 ) ) );
    }
477

478
    // NOTE: the following code should not be necessary. The update callback does this job just before the mesh is rendered
479
    // initially. Just set the texture changed flag to true. If this however might be needed use WSubject::getDataTextures.
480
    m_textureChanged = true;
481

482
    m_shader = osg::ref_ptr< WShader > ( new WShader( "WMMarchingCubes", m_localPath ) );
483
    m_shader->apply( m_surfaceGeode );
484

485
    m_moduleNode->insert( m_surfaceGeode );
486 487 488 489 490
    if ( !m_moduleNodeInserted )
    {
        WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_moduleNode );
        m_moduleNodeInserted = true;
    }
491

492
    m_moduleNode->addUpdateCallback( new SurfaceNodeCallback( this ) );
493 494
}

495 496 497 498
void WMMarchingCubes::notifyTextureChange()
{
    m_textureChanged = true;
}
499

500
bool WMMarchingCubes::save() const
501
{
502 503 504
    m_saveTriggerProp->set( WPVBaseTypes::PV_TRIGGER_READY, false );

    if( m_triMesh->vertSize() == 0 )
505 506 507 508 509
    {
        WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Marching Cubes", LL_ERROR );
        return false;
    }

510
    if( m_triMesh->triangleSize() == 0 )
511 512 513 514 515
    {
        WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 triangles.", "Marching Cubes", LL_ERROR );
        return false;
    }

516
    const char* c_file = m_meshFile->get().file_string().c_str();
517
    std::ofstream dataFile( c_file, std::ios_base::binary );
518 519 520 521 522 523 524

    if ( dataFile )
    {
        WLogger::getLogger()->addLogMessage( "opening file", "Marching Cubes", LL_DEBUG );
    }
    else
    {
525
        WLogger::getLogger()->addLogMessage( "open file failed" + m_meshFile->get().file_string() , "Marching Cubes", LL_ERROR );
526 527 528 529 530 531 532 533 534 535 536 537
        return false;
    }

    dataFile.precision( 16 );

    WLogger::getLogger()->addLogMessage( "start writing file", "Marching Cubes", LL_DEBUG );
    dataFile << ( "# vtk DataFile Version 2.0\n" );
    dataFile << ( "generated using OpenWalnut\n" );
    dataFile << ( "ASCII\n" );
    dataFile << ( "DATASET UNSTRUCTURED_GRID\n" );

    wmath::WPosition point;
538 539
    dataFile << "POINTS " << m_triMesh->vertSize() << " float\n";
    for ( size_t i = 0; i < m_triMesh->vertSize(); ++i )
540
    {
541
        point = m_triMesh->getVertexAsPosition( i );
542
        if( !( wmath::myIsfinite( point[0] ) && wmath::myIsfinite( point[1] ) && wmath::myIsfinite( point[2] ) ) )
543 544 545 546 547 548 549
        {
            WLogger::getLogger()->addLogMessage( "Will not write file from data that contains NAN or INF.", "Marching Cubes", LL_ERROR );
            return false;
        }
        dataFile << point[0] << " " << point[1] << " " << point[2] << "\n";
    }

550 551
    dataFile << "CELLS " << m_triMesh->triangleSize() << " " << m_triMesh->triangleSize() * 4 << "\n";
    for ( size_t i = 0; i < m_triMesh->triangleSize(); ++i )
552
    {
553 554 555
        dataFile << "3 " << m_triMesh->getTriVertId0( i ) << " "
                 <<  m_triMesh->getTriVertId1( i ) << " "
                 <<  m_triMesh->getTriVertId2( i ) << "\n";
556
    }
557 558
    dataFile << "CELL_TYPES "<< m_triMesh->triangleSize() <<"\n";
    for ( size_t i = 0; i < m_triMesh->triangleSize(); ++i )
559 560 561
    {
        dataFile << "5\n";
    }
562
    dataFile << "POINT_DATA " << m_triMesh->vertSize() << "\n";
563 564
    dataFile << "SCALARS scalars float\n";
    dataFile << "LOOKUP_TABLE default\n";
565
    for ( size_t i = 0; i < m_triMesh->vertSize(); ++i )
566 567 568 569 570
    {
        dataFile << "0\n";
    }
    dataFile.close();
    WLogger::getLogger()->addLogMessage( "saving done", "Marching Cubes", LL_DEBUG );
571
    return true;
572
}
573

574
void WMMarchingCubes::updateGraphicsForCallback()
575
{
576 577 578
    boost::unique_lock< boost::shared_mutex > lock;
    lock = boost::unique_lock< boost::shared_mutex >( m_updateLock );

579 580 581 582 583 584 585 586 587 588 589
    if( m_surfaceColor->changed() )
    {
        osg::Vec4Array* colors = new osg::Vec4Array;

        WColor c = m_surfaceColor->get( true );
        colors->push_back( osg::Vec4( c.getRed(), c.getGreen(), c.getBlue(), 1.0f ) );
        osg::ref_ptr< osg::Geometry > surfaceGeometry = m_surfaceGeode->getDrawable( 0 )->asGeometry();
        surfaceGeometry->setColorArray( colors );
        surfaceGeometry->setColorBinding( osg::Geometry::BIND_OVERALL );
    }

590
    if ( m_textureChanged || m_opacityProp->changed() || m_useTextureProp->changed()  )
591
    {
592
        bool localTextureChangedFlag = m_textureChanged;
593 594 595 596
        m_textureChanged = false;

        // grab a list of data textures
        std::vector< boost::shared_ptr< WDataTexture3D > > tex = WDataHandler::getDefaultSubject()->getDataTextures( true );
597

598
        if ( tex.size() > 0 )
599
        {
600
            osg::StateSet* rootState = m_surfaceGeode->getOrCreateStateSet();
601 602

            // reset all uniforms
603
            for ( int i = 0; i < m_maxNumberOfTextures; ++i )
604 605 606 607
            {
                m_typeUniforms[i]->set( 0 );
            }

608
            rootState->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useTexture", m_useTextureProp->get( true ) ) ) );
609
            rootState->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "useLighting", m_shaderUseLighting ) ) );
610

611 612
            if( m_shaderUseTransparency )
            {
613
                rootState->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "opacity", m_opacityProp->get( true ) ) ) );
614 615 616
            }
            else
            {
617
                rootState->addUniform( osg::ref_ptr<osg::Uniform>( new osg::Uniform( "opacity", 100 ) ) );
618
            }
619

620
            // for each texture -> apply
621
            int c = 0;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
            //////////////////////////////////////////////////////////////////////////////////////////////////
            if ( WKernel::getRunningKernel()->getSelectionManager()->getUseTexture() )
            {
                boost::shared_ptr< WGridRegular3D > grid = WKernel::getRunningKernel()->getSelectionManager()->getGrid();
                osg::ref_ptr< osg::Geometry > surfaceGeometry = m_surfaceGeode->getDrawable( 0 )->asGeometry();
                osg::Vec3Array* texCoords = new osg::Vec3Array;

                for( size_t i = 0; i < m_triMesh->vertSize(); ++i )
                {
                    osg::Vec3 vertPos = m_triMesh->getVertex( i );
                    texCoords->push_back( wge::wv3D2ov3( grid->worldCoordToTexCoord( wmath::WPosition( vertPos[0], vertPos[1], vertPos[2] ) ) ) );
                }
                surfaceGeometry->setTexCoordArray( c, texCoords );

                osg::ref_ptr<osg::Texture3D> texture3D = WKernel::getRunningKernel()->getSelectionManager()->getTexture();

                m_typeUniforms[c]->set( W_DT_UNSIGNED_CHAR  );
                m_thresholdUniforms[c]->set( 0.0f );
                m_alphaUniforms[c]->set( WKernel::getRunningKernel()->getSelectionManager()->getTextureOpacity() );
                m_cmapUniforms[c]->set( 4 );

                texture3D->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST );
                texture3D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::NEAREST );

                rootState->setTextureAttributeAndModes( c, texture3D, osg::StateAttribute::ON );

                ++c;
            }
            //////////////////////////////////////////////////////////////////////////////////////////////////


653
            for ( std::vector< boost::shared_ptr< WDataTexture3D > >::const_iterator iter = tex.begin(); iter != tex.end(); ++iter )
654
            {
655 656 657 658 659 660 661 662 663 664 665 666 667
                if( localTextureChangedFlag )
                {
                    osg::ref_ptr< osg::Geometry > surfaceGeometry = m_surfaceGeode->getDrawable( 0 )->asGeometry();
                    osg::Vec3Array* texCoords = new osg::Vec3Array;
                    boost::shared_ptr< WGridRegular3D > grid = ( *iter )->getGrid();
                    for( size_t i = 0; i < m_triMesh->vertSize(); ++i )
                    {
                        osg::Vec3 vertPos = m_triMesh->getVertex( i );
                        texCoords->push_back( wge::wv3D2ov3( grid->worldCoordToTexCoord( wmath::WPosition( vertPos[0], vertPos[1], vertPos[2] ) ) ) );
                    }
                    surfaceGeometry->setTexCoordArray( c, texCoords );
                }

668 669
                osg::ref_ptr<osg::Texture3D> texture3D = ( *iter )->getTexture();
                rootState->setTextureAttributeAndModes( c, texture3D, osg::StateAttribute::ON );
670

671
                // set threshold/opacity as uniforms
672 673 674
                float minValue = ( *iter )->getMinValue();
                float maxValue = ( *iter )->getMaxValue();
                float t = ( ( *iter )->getThreshold() - minValue ) / ( maxValue - minValue ); // rescale to [0,1]
675
                float a = ( *iter )->getAlpha();
676
                int cmap = ( *iter )->getSelectedColormap();
677

678
                m_typeUniforms[c]->set( ( *iter )->getDataType() );
679 680
                m_thresholdUniforms[c]->set( t );
                m_alphaUniforms[c]->set( a );
681
                m_cmapUniforms[c]->set( cmap );
682

683
                ++c;
684 685 686 687
                if( c == m_maxNumberOfTextures )
                {
                    break;
                }
688 689 690
            }
        }
    }
691
    lock.unlock();
692 693
}