WMTriangleMeshRenderer.cpp 18.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-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/>.
//
//---------------------------------------------------------------------------

Mathias Goldau's avatar
Mathias Goldau committed
25
#include <list>
26
#include <map>
Mathias Goldau's avatar
Mathias Goldau committed
27
#include <string>
28
#include <vector>
29

30
#include <osg/Geode>
31

32
#include "core/graphicsEngine/WGEManagedGroupNode.h"
33 34
#include "core/graphicsEngine/WGEUtils.h"
#include "core/graphicsEngine/WTriangleMesh.h"
35 36
#include "core/graphicsEngine/WGEGeodeUtils.h"
#include "core/graphicsEngine/shaders/WGEShader.h"
37
#include "core/kernel/WKernel.h"
38 39 40 41 42 43
#include "core/graphicsEngine/WGEColormapping.h"
#include "core/common/math/WMath.h"
#include "core/common/WLimits.h"
#include "core/graphicsEngine/shaders/WGEShaderPropertyDefineOptions.h"
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"

44

45
#include "WMTriangleMeshRenderer.xpm"
46
#include "WMTriangleMeshRenderer.h"
47

48 49 50
// This line is needed by the module loader to actually find your module.
W_LOADABLE_MODULE( WMTriangleMeshRenderer )

51
WMTriangleMeshRenderer::WMTriangleMeshRenderer():
52
    WModule()
53 54 55 56 57 58 59 60 61 62 63 64 65 66
{
}

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

boost::shared_ptr< WModule > WMTriangleMeshRenderer::factory() const
{
    // See "src/modules/template/" for an extensively documented example.
    return boost::shared_ptr< WModule >( new WMTriangleMeshRenderer() );
}

67 68
const char** WMTriangleMeshRenderer::getXPMIcon() const
{
69
    // just return the icon
Alexander Wiebel's avatar
Alexander Wiebel committed
70
    return WMTriangleMeshRenderer_xpm;
71 72
}

73 74 75 76 77 78 79 80 81 82 83
const std::string WMTriangleMeshRenderer::getName() const
{
    // Specify your module name here. This name must be UNIQUE!
    return "Triangle Mesh Renderer";
}

const std::string WMTriangleMeshRenderer::getDescription() const
{
    return "Takes a triangle mesh as input and renders it as a shaded surface.";
}

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
84
void WMTriangleMeshRenderer::updateMinMax( double& minX, double& maxX, // NOLINT
85 86 87
                                           double& minY, double& maxY, // NOLINT
                                           double& minZ, double& maxZ, const osg::Vec3d& vector ) const // NOLINT
{
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
88 89 90
    minX = std::min( minX, vector.x() );
    minY = std::min( minY, vector.y() );
    minZ = std::min( minZ, vector.z() );
91

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
92 93 94
    maxX = std::max( maxX, vector.x() );
    maxY = std::max( maxY, vector.y() );
    maxZ = std::max( maxZ, vector.z() );
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
}

double WMTriangleMeshRenderer::getMedian( double x, double y, double z ) const
{
    if( ( y < x && z > x ) || ( z < x && y > x) )
    {
        return x;
    }

    if( ( x < y && z > y ) || ( x < y && z > y) )
    {
        return y;
    }

    if( ( y < z && x > z ) || ( x < z && y > z) )
    {
        return z;
    }

    if( x == y )
    {
        return x;
    }
    if( x == z )
    {
        return x;
    }
    if( y == z )
    {
        return z;
    }
    if( x == y && y == z && x == z)
    {
        return x;
    }

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
131
    return 0;
132 133 134 135 136 137 138
}

double WMTriangleMeshRenderer::getIntervallCenterMiddle( double min, double max ) const
{
    return min + ( max - min) / 2;
}

139 140
void WMTriangleMeshRenderer::connectors()
{
141 142
    // this input contains the triangle data
    m_meshInput = WModuleInputData< WTriangleMesh >::createAndAdd( shared_from_this(), "mesh", "The mesh to display" );
143

144 145 146
    // this input provides an additional map from vertex ID to color. This is especially useful for using the trimesh renderer in conjunction
    // with  clustering mechanisms and so on
    m_colorMapInput = WModuleInputData< WColoredVertices >::createAndAdd( shared_from_this(), "colorMap", "The special colors" );
147

148
    // call WModule's initialization
149 150 151 152 153
    WModule::connectors();
}

void WMTriangleMeshRenderer::properties()
{
154 155 156 157
    // some properties need to trigger an update
    m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );

    // setup all the properties. See header file for their meaning and purpose.
158 159
    m_mainComponentOnly = m_properties->addProperty( "Main component", "Main component only", false, m_propCondition );
    m_showCoordinateSystem = m_properties->addProperty( "Coordinate system", "If enabled, the coordinate system of the mesh will be shown.",
160 161 162 163 164
                                                        false, m_propCondition );

    m_coloringGroup = m_properties->addPropertyGroup( "Coloring", "Coloring options and colormap options." );

    m_opacity = m_coloringGroup->addProperty( "Opacity %", "Opaqueness of surface.", 100.0 );
165 166
    m_opacity->setMin( 0.0 );
    m_opacity->setMax( 100.0 );
167

168 169 170 171 172
    // Allow the user to select different colormodes
    boost::shared_ptr< WItemSelection > colorModes( boost::shared_ptr< WItemSelection >( new WItemSelection() ) );
    colorModes->addItem( "Single Color", "The whole surface is colored using the default color." );
    colorModes->addItem( "From Mesh", "The surface is colored according to the mesh." );
    colorModes->addItem( "From colormap connector", "The surface is colored using the colormap on colorMap connector." );
173
    m_colorMode = m_coloringGroup->addProperty( "Color mode", "Choose one of the available colorings.", colorModes->getSelectorFirst(),
174 175 176 177
                                             m_propCondition );
    WPropertyHelper::PC_SELECTONLYONE::addTo( m_colorMode );

    // this is the color used if single color is selected
178
    m_color = m_coloringGroup->addProperty( "Default color", "The color of of the surface.",
179
                                         WColor( .9f, .9f, 0.9f, 1.0f ), m_propCondition );
180

181 182
    m_colormap = m_coloringGroup->addProperty( "Enable colormapping", "Turn colormapping on", false );
    m_colormapRatio = m_coloringGroup->addProperty( "Colormap ratio", "Set the colormap Ratio", 0.5 );
183 184
    m_colormapRatio->setMin( 0.0 );
    m_colormapRatio->setMax( 1.0 );
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    m_groupTransformation = m_properties->addPropertyGroup( "Transformation",  "A group which contains transformation tools." );

    //Scaling
    m_scale = m_groupTransformation->addProperty( "Scale whole surface?", "The whole surface will be scaled.", false );

    m_scaleX = m_groupTransformation->addProperty( "Scale X", "Scaling X of surface.", 1.0 );
    m_scaleX->setMin( -10.0 );
    m_scaleX->setMax( 10.0 );

    m_scaleY = m_groupTransformation->addProperty( "Scale Y", "Scaling Y of surface.", 1.0 );
    m_scaleY->setMin( -10.0 );
    m_scaleY->setMax( 10.0 );

    m_scaleZ = m_groupTransformation->addProperty( "Scale Z", "Scaling Z of surface.", 1.0 );
    m_scaleZ->setMin( -10.0 );
    m_scaleZ->setMax( 10.0 );

    //Rotating
    m_rotateX = m_groupTransformation->addProperty( "Rotate X", "Rotate X in °", 0.0 );
    m_rotateX->setMin( -360.0 );
    m_rotateX->setMax( 360.0 );

    m_rotateY = m_groupTransformation->addProperty( "Rotate Y", "Rotate Y in °", 0.0 );
    m_rotateY->setMin( -360.0 );
    m_rotateY->setMax( 360.0 );

    m_rotateZ = m_groupTransformation->addProperty( "Rotate Z", "Rotate Z in °", 0.0 );
    m_rotateZ->setMin( -360.0 );
    m_rotateZ->setMax( 360.0 );

    //Translating
    m_translateX = m_groupTransformation->addProperty( "Translate X", "Translate the surface to X", 0.0 );
    m_translateX->setMin( -100.0 );
    m_translateX->setMax( 100.0 );

    m_translateY = m_groupTransformation->addProperty( "Translate Y", "Translate the surface to Y", 0.0 );
    m_translateY->setMin( -100.0 );
    m_translateY->setMax( 100.0 );

    m_translateZ = m_groupTransformation->addProperty( "Translate Z", "Translate the surface to Z", 0.0 );
    m_translateZ->setMin( -100.0 );
    m_translateZ->setMax( 100.0 );

    m_setDefault = m_groupTransformation->addProperty( "Reset to default", "Set!",
                                                        WPVBaseTypes::PV_TRIGGER_READY,
                                                        boost::bind( &WMTriangleMeshRenderer::setToDefault, this ) );

233
    // call WModule's initialization
234
    WModule::properties();
235 236
}

237 238 239 240 241 242 243 244 245 246 247 248 249
/**
 * Compares two WTrianglesMeshes on their size of vertices. This is private here since it makes sense only to this module ATM.
 */
struct WMeshSizeComp
{
    /**
     * Comparator on num vertex of two WTriangleMeshes
     *
     * \param m First Mesh
     * \param n Second Mesh
     *
     * \return True if and only if the first Mesh has less vertices as the second mesh.
     */
250
    bool operator()( const boost::shared_ptr< WTriangleMesh >& m, const boost::shared_ptr< WTriangleMesh >& n ) const
251
    {
252
        return m->vertSize() < n->vertSize();
253 254 255
    }
};

256 257
void WMTriangleMeshRenderer::moduleMain()
{
258
    // let the main loop awake if the data changes.
259
    m_moduleState.setResetable( true, true );
260 261
    m_moduleState.add( m_meshInput->getDataChangedCondition() );
    m_moduleState.add( m_colorMapInput->getDataChangedCondition() );
262
    m_moduleState.add( m_propCondition );
263

264
    // signal ready state. The module is now ready to be used.
265 266
    ready();

267 268 269 270 271
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // setup the main graphics-node:
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    // create a OSG node, which will contain the triangle data and allows easy transformations:
272
    m_moduleNode = new WGEManagedGroupNode( m_active );
273 274 275 276 277 278 279
    osg::StateSet* moduleNodeState = m_moduleNode->getOrCreateStateSet();
    WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_moduleNode );

    // set the member function "updateTransformation" as callback
    WGEFunctorCallback< osg::Node >::SPtr transformationCallback(
        new WGEFunctorCallback< osg::Node >( boost::bind( &WMTriangleMeshRenderer::updateTransformation, this ) )
    );
280
    m_moduleNode->addUpdateCallback( transformationCallback );
281 282

    // load the GLSL shader:
283
    osg::ref_ptr< WGEShader > shader( new WGEShader( "WMTriangleMeshRenderer", m_localPath ) );
284 285 286 287 288
    m_colorMapTransformation = new osg::Uniform( "u_colorMapTransformation", osg::Matrixd::identity() );
    shader->addPreprocessor( WGEShaderPreprocessor::SPtr(
        new WGEShaderPropertyDefineOptions< WPropBool >( m_colormap, "COLORMAPPING_DISABLED", "COLORMAPPING_ENABLED" ) )
    );

289 290 291

    // set the opacity and material color property as GLSL uniforms:
    moduleNodeState->addUniform( new WGEPropertyUniform< WPropDouble >( "u_opacity", m_opacity ) );
292 293 294 295 296 297 298 299 300 301

    // loop until the module container requests the module to quit
    while( !m_shutdownFlag() )
    {
        // Now, the moduleState variable comes into play. The module can wait for the condition, which gets fired whenever the input receives data
        // or an property changes. The main loop now waits until something happens.
        debugLog() << "Waiting ...";
        m_moduleState.wait();

        // woke up since the module is requested to finish
302
        if( m_shutdownFlag() )
303 304 305
        {
            break;
        }
306 307

        // Get data and check for invalid data.
308
        boost::shared_ptr< WTriangleMesh > mesh = m_meshInput->getData();
309
        boost::shared_ptr< WColoredVertices > colorMap = m_colorMapInput->getData();
310
        if( !mesh )
311 312 313 314
        {
            debugLog() << "Invalid Data. Disabling.";
            continue;
        }
315

316
          // prepare the geometry node
317
        debugLog() << "Start rendering Mesh";
318 319
        osg::ref_ptr< osg::Geometry > geometry;
        osg::ref_ptr< osg::Geode > geode( new osg::Geode );
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        geode->getOrCreateStateSet()->addUniform( m_colorMapTransformation );
        geode->getOrCreateStateSet()->addUniform( new WGEPropertyUniform< WPropDouble >( "u_colormapRatio", m_colormapRatio ) );

        // apply shader only to mesh
        shader->apply( geode );

        // get the middle point of the mesh
        std::vector< size_t >triangles = mesh->getTriangles();
        std::vector< size_t >::const_iterator trianglesIterator;
        double minX = wlimits::MAX_DOUBLE;
        double minY = wlimits::MAX_DOUBLE;
        double minZ = wlimits::MAX_DOUBLE;

        double maxX = wlimits::MIN_DOUBLE;
        double maxY = wlimits::MIN_DOUBLE;
        double maxZ = wlimits::MIN_DOUBLE;

        for( trianglesIterator = triangles.begin();
                trianglesIterator != triangles.end();
                trianglesIterator++ )
        {
            osg::Vec3d vectorX = mesh->getVertex( *trianglesIterator );
Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
342
            updateMinMax( minX, maxX, minY, maxY, minZ, maxZ, vectorX );
343 344
        }

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
345 346 347
        m_meshCenter = WVector3d( getIntervallCenterMiddle( minX, maxX ),
                                  getIntervallCenterMiddle( minY, maxY ),
                                  getIntervallCenterMiddle( minZ, maxZ ) );
348

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
349
        // start rendering
350 351 352
        boost::shared_ptr< WProgress > progress = boost::shared_ptr< WProgress >( new WProgress( "Rendering", 3 ) );
        m_progress->addSubProgress( progress );

353 354
        if( m_mainComponentOnly->get( true ) )
        {
355
            // component decomposition
356
            debugLog() << "Start mesh decomposition";
357
            boost::shared_ptr< std::list< boost::shared_ptr< WTriangleMesh > > > m_components = tm_utils::componentDecomposition( *mesh );
358
            mesh = *std::max_element( m_components->begin(), m_components->end(), WMeshSizeComp() );
359
            debugLog() << "Decomposing mesh done";
360
        }
361
        ++*progress;
362

363 364 365
        // now create the mesh but handle the color mode properly
        WItemSelector s = m_colorMode->get( true );
        if( s.getItemIndexOfSelected( 0 ) == 0 )
366
        {
367 368
            // use single color
            geometry = wge::convertToOsgGeometry( mesh, m_color->get(), true, true, false );
369
        }
370
        else if( s.getItemIndexOfSelected( 0 ) == 1 )
371
        {
372 373
            // take color from mesh
            geometry = wge::convertToOsgGeometry( mesh, m_color->get(), true, true, true );
374 375 376
        }
        else
        {
377 378 379 380 381 382 383 384 385 386
            // take color from map
            if( colorMap )
            {
                geometry = wge::convertToOsgGeometry( mesh, *colorMap, m_color->get(), true, true );
            }
            else
            {
                warnLog() << "External colormap not connected. Using default color.";
                geometry = wge::convertToOsgGeometry( mesh, m_color->get(), true, true, false );
            }
387
        }
388 389
        ++*progress;

390 391
        WGEColormapping::apply( geode, shader );

392 393
        // done. Set the new drawable
        geode->addDrawable( geometry );
394 395
        m_moduleNode->clear();
        m_moduleNode->insert( geode );
396 397 398 399 400 401 402 403 404 405 406
        if( m_showCoordinateSystem->get() )
        {
            m_moduleNode->insert(
                wge::creatCoordinateSystem(
                    m_meshCenter,
                    maxX-minX,
                    maxY-minY,
                    maxZ-minZ
                )
             );
        }
407 408 409
        debugLog() << "Rendering Mesh done";
        ++*progress;
        progress->finish();
410 411
    }

412
    // it is important to always remove the modules again
413 414 415 416 417
    WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_moduleNode );
}

void WMTriangleMeshRenderer::updateTransformation()
{
418
    if( m_moduleNode )
419
    {
420
        if( m_scale->changed() && m_scale->get( true ) )
421 422 423 424 425
        {
            m_scaleX->set( getMedian( m_scaleX->get(), m_scaleY->get(), m_scaleZ->get() ) );
            m_scaleY->set( getMedian( m_scaleX->get(), m_scaleY->get(), m_scaleZ->get() ) );
            m_scaleZ->set( getMedian( m_scaleX->get(), m_scaleY->get(), m_scaleZ->get() ) );
        }
426
        if( m_scale->get() )
427
        {
428
            if( m_scaleX->changed() && m_scaleX->get( true ) )
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
            {
                m_scaleY->set( m_scaleX->get() );
                m_scaleZ->set( m_scaleX->get() );
            }
            if( m_scaleY->changed() && m_scaleY->get( true ) )
            {
                m_scaleX->set( m_scaleY->get() );
                m_scaleZ->set( m_scaleZ->get() );
            }
            if( m_scaleZ->changed() && m_scaleZ->get( true ) )
            {
                m_scaleX->set( m_scaleZ->get() );
                m_scaleY->set( m_scaleZ->get() );
            }
        }

Sebastian Eichelbaum's avatar
[DOC]  
Sebastian Eichelbaum committed
445 446
        osg::Matrixd matrixTranslateTo0 = osg::Matrixd::translate( static_cast< osg::Vec3f >( m_meshCenter ) * -1.0 );
        osg::Matrixd matrixTranslateFrom0 = osg::Matrixd::translate( static_cast< osg::Vec3f >( m_meshCenter ) );
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
        osg::Matrixd matrixScale = osg::Matrixd::scale( m_scaleX->get(), m_scaleY->get(), m_scaleZ->get() );
        osg::Matrixd matrixRotateX = osg::Matrixd::rotate( m_rotateX->get() * piDouble / 180, osg::Vec3f( 1, 0, 0 ) );
        osg::Matrixd matrixRotateY = osg::Matrixd::rotate( m_rotateY->get() * piDouble / 180, osg::Vec3f( 0, 1, 0 ) );
        osg::Matrixd matrixRotateZ = osg::Matrixd::rotate( m_rotateZ->get() * piDouble / 180, osg::Vec3f( 0, 0, 1 ) );
        osg::Matrixd matrixRotate = matrixRotateX * matrixRotateY * matrixRotateZ;
        osg::Matrixd matrixTranslate = osg::Matrixd::translate( m_translateX->get(), m_translateY->get(), m_translateZ->get() );
        osg::Matrixd matrixComplete =
                                matrixTranslateTo0 *
                                matrixScale *
                                matrixTranslateFrom0 *
                                matrixTranslateTo0 *
                                matrixRotate *
                                matrixTranslateFrom0 *
                                matrixTranslate;

        m_moduleNode->setMatrix( matrixComplete );
        m_colorMapTransformation->set( matrixComplete );
    }
}

void WMTriangleMeshRenderer::setToDefault()
{
    if( m_setDefault->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED )
    {
        WMTriangleMeshRenderer::m_opacity->set( 100 );

        WMTriangleMeshRenderer::m_scale->set( false );
        WMTriangleMeshRenderer::m_scaleX->set( 1 );
        WMTriangleMeshRenderer::m_scaleY->set( 1 );
        WMTriangleMeshRenderer::m_scaleZ->set( 1 );

        WMTriangleMeshRenderer::m_rotateX->set( 0 );
        WMTriangleMeshRenderer::m_rotateY->set( 0 );
        WMTriangleMeshRenderer::m_rotateZ->set( 0 );

        WMTriangleMeshRenderer::m_translateX->set( 0 );
        WMTriangleMeshRenderer::m_translateY->set( 0 );
        WMTriangleMeshRenderer::m_translateZ->set( 0 );

        m_setDefault->set( WPVBaseTypes::PV_TRIGGER_READY );
    }
488 489
}