OpenWalnut  1.4.0
WGEColormapping.cpp
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #include <algorithm>
00026 #include <iostream>
00027 #include <sstream>
00028 #include <string>
00029 
00030 #include "../common/WLogger.h"
00031 #include "../common/WStringUtils.h"
00032 
00033 #include "WGETextureUtils.h"
00034 #include "exceptions/WGESignalSubscriptionFailed.h"
00035 
00036 #include "WGEColormapping.h"
00037 
00038 // instance as singleton
00039 boost::shared_ptr< WGEColormapping > WGEColormapping::m_instance = boost::shared_ptr< WGEColormapping >();
00040 
00041 /**
00042  * This functions simply sets some defines to a shader. It sets the texture unit and gl_MultiTexCoord variable names properly.
00043  *
00044  * \param shader the shader where to add the defines
00045  * \param start the start index of the unit for colormap0
00046  */
00047 void setDefines( osg::ref_ptr< WGEShader > shader, size_t start = 0 )
00048 {
00049     // simply set some proper defines for each colormap -> the unit and multitex coords
00050     for( size_t unit = 0; unit < wge::getMaxTexUnits(); ++unit )
00051     {
00052         // disable textures with invalid unit numbers
00053         if( unit < wge::getMaxTexUnits() - start )
00054         {
00055             shader->setDefine( "Colormap" + string_utils::toString( unit ) + "Enabled", true );
00056             shader->setDefine( "Colormap" + string_utils::toString( unit ) + "Unit", start + unit );
00057         }
00058     }
00059 }
00060 
00061 /**
00062  * This functions simply sets the specified pre transformation matrix to the shader. It therefore uses a preprocessor define. This allows a
00063  * hard-coded matrix to be optimized be the shader compiler.
00064  *
00065  * \param shader the shader where to add the defines
00066  * \param preTransform the transformation matrix used to pre-multiply with all texture coordinates
00067  */
00068 void setPreTransform( osg::ref_ptr< WGEShader > shader, osg::Matrixd preTransform )
00069 {
00070     std::ostringstream out;
00071     out << "mat4( ";
00072     const osg::Matrixd::value_type* m = preTransform.ptr();
00073 
00074     out.precision( 10 );
00075     out.setf( std::ios::fixed, std::ios::floatfield );
00076 
00077     // print all 16 values
00078     for( size_t i = 0; i < 15; ++i )
00079     {
00080         out << m[ i ] << ", ";
00081     }
00082     out << m[ 15 ] << " )";
00083 
00084     // set as define
00085     shader->setDefine( "ColormapPreTransform", out.str() );
00086 }
00087 
00088 WGEColormapping::WGEColormapping()
00089 {
00090     // initialize members
00091     m_textures.getChangeCondition()->subscribeSignal( boost::bind( &WGEColormapping::textureUpdate, this ) );
00092     m_boundingBox.getWriteTicket()->get().set( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 );
00093 }
00094 
00095 WGEColormapping::~WGEColormapping()
00096 {
00097     // cleanup
00098 }
00099 
00100 boost::shared_ptr< WGEColormapping > WGEColormapping::instance()
00101 {
00102     if( !m_instance )
00103     {
00104         m_instance = boost::shared_ptr< WGEColormapping >( new WGEColormapping() );
00105     }
00106 
00107     return m_instance;
00108 }
00109 
00110 void WGEColormapping::apply( osg::ref_ptr< osg::Node > node, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
00111 {
00112     instance()->applyInst( NodeList( 1, node ), WMatrix4d( WMatrix4d::identity() ), shader, startTexUnit );
00113 }
00114 
00115 void WGEColormapping::apply( osg::ref_ptr< osg::Node > node, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader,
00116                              size_t startTexUnit )
00117 {
00118     instance()->applyInst( NodeList( 1, node ), preTransform, shader, startTexUnit );
00119 }
00120 
00121 void WGEColormapping::apply( NodeList nodes, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
00122 {
00123     instance()->applyInst( nodes, preTransform, shader, startTexUnit );
00124 }
00125 
00126 void WGEColormapping::apply( NodeList nodes, osg::ref_ptr< WGEShader > shader, size_t startTexUnit )
00127 {
00128     instance()->applyInst( nodes, WMatrix4d( WMatrix4d::identity() ), shader, startTexUnit );
00129 }
00130 
00131 void WGEColormapping::registerTexture( osg::ref_ptr< WGETexture3D > texture, std::string name )
00132 {
00133     instance()->registerTextureInst( texture, name );
00134 }
00135 
00136 void WGEColormapping::deregisterTexture( osg::ref_ptr< WGETexture3D > texture )
00137 {
00138     instance()->deregisterTextureInst( texture );
00139 }
00140 
00141 void WGEColormapping::replaceTexture( osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name )
00142 {
00143     instance()->replaceTextureInst( old, newTex, name );
00144 }
00145 
00146 void WGEColormapping::applyInst( NodeList nodes, WMatrix4d preTransform, osg::ref_ptr< WGEShader > shader,
00147                                  size_t startTexUnit )
00148 {
00149     // init shader
00150     osg::ref_ptr< WGEShader > s = shader;
00151     if( !s )
00152     {
00153         // we use a new instance of the default shader here because the preTransform is varying between several nodes.
00154         s = new WGEShader( "WGEDefaultColormapper" );
00155     }
00156     setDefines( s, startTexUnit );
00157     setPreTransform( s, preTransform );
00158 
00159     // do this for each node
00160     for( NodeList::const_iterator i = nodes.begin(); i != nodes.end(); ++i )
00161     {
00162         // applying to a node simply means adding a callback :-)
00163         NodeInfo* info = new NodeInfo;
00164         info->m_rebind = true;
00165         info->m_texUnitStart = startTexUnit;
00166         info->m_preTransform = preTransform;
00167         m_nodeInfo.insert( std::make_pair( *i, info ) );
00168 
00169         ( *i )->addUpdateCallback( new WGEFunctorCallback< osg::Node >( boost::bind( &WGEColormapping::callback, this, _1 ) ) );
00170 
00171         // add the default shader if no other shader has been specified.
00172         s->apply( *i );
00173     }
00174 }
00175 
00176 void WGEColormapping::registerTextureInst( osg::ref_ptr< WGETexture3D > texture, std::string name )
00177 {
00178     wlog::debug( "WGEColormapping" ) << "Registering texture \"" << name << "\".";
00179     if( !m_textures.count( texture ) )
00180     {
00181         if( !name.empty() )
00182         {
00183             texture->name()->set( name );
00184         }
00185         m_textures.push_front( texture );
00186         updateBounds();
00187         m_registerSignal( texture );
00188     }
00189 }
00190 
00191 void WGEColormapping::deregisterTextureInst( osg::ref_ptr< WGETexture3D > texture )
00192 {
00193     wlog::debug( "WGEColormapping" ) << "De-registering texture \"" << texture->name()->get() << "\".";
00194     if( m_textures.count( texture ) )
00195     {
00196         m_textures.remove( texture );
00197         updateBounds();
00198         m_deregisterSignal( texture );
00199     }
00200 }
00201 
00202 void WGEColormapping::replaceTextureInst( osg::ref_ptr< WGETexture3D > old, osg::ref_ptr< WGETexture3D > newTex, std::string name )
00203 {
00204     wlog::debug( "WGEColormapping" ) << "Replacing texture.";
00205     if( !name.empty() )
00206     {
00207         newTex->name()->set( name );
00208     }
00209 
00210     // if it exists, replace it
00211     if( m_textures.count( old ) )
00212     {
00213         m_textures.replace( old, newTex );
00214         updateBounds();
00215         m_replaceSignal( old, newTex );
00216     }
00217     else    // <- if not exists: add
00218     {
00219         registerTextureInst( newTex, name );
00220     }
00221 }
00222 
00223 void WGEColormapping::updateBounds()
00224 {
00225     TextureContainerType::ReadTicket r = m_textures.getReadTicket();
00226     WSharedObject< WBoundingBox >::WriteTicket bbw = m_boundingBox.getWriteTicket();
00227 
00228     bool first = true;
00229     for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
00230     {
00231         if( first )
00232         {
00233             bbw->get() = ( *iter )->getBoundingBox();
00234             first = false;
00235         }
00236         else
00237         {
00238             bbw->get().expandBy( ( *iter )->getBoundingBox() );
00239         }
00240     }
00241 }
00242 
00243 WBoundingBox WGEColormapping::getBoundingBox() const
00244 {
00245     return m_boundingBox.getReadTicket()->get();
00246 }
00247 
00248 void WGEColormapping::textureUpdate()
00249 {
00250     NodeInfoContainerType::WriteTicket w = m_nodeInfo.getWriteTicket();
00251     for( NodeInfoContainerType::Iterator iter = w->get().begin(); iter != w->get().end(); ++iter )
00252     {
00253         iter->second->m_rebind = true;
00254     }
00255 }
00256 
00257 /**
00258  * Custom comparator which uses a textures sortIndex for comparing.
00259  *
00260  * \param a first element
00261  * \param b second element
00262  *
00263  * \return  true if a's sortIndex is smaller than b's.
00264  */
00265 bool sortIndexComparator( osg::ref_ptr< WGETexture3D > a, osg::ref_ptr< WGETexture3D > b )
00266 {
00267     return ( a->sortIndex()->get() < b->sortIndex()->get() );
00268 }
00269 
00270 void WGEColormapping::sortByIndex()
00271 {
00272     // use sort with custom comparator
00273     stableSort( &sortIndexComparator );
00274 }
00275 
00276 void WGEColormapping::setSortIndices()
00277 {
00278     TextureContainerType::ReadTicket r = m_textures.getReadTicket();
00279     size_t index = 0;
00280     for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
00281     {
00282         ( *iter )->sortIndex()->set( index );
00283         index++;
00284     }
00285 }
00286 
00287 void WGEColormapping::resetSortIndices()
00288 {
00289     TextureContainerType::ReadTicket r = m_textures.getReadTicket();
00290     for( TextureContainerType::ConstIterator iter = r->get().begin(); iter != r->get().end(); ++iter )
00291     {
00292         ( *iter )->sortIndex()->set( WGETexture3D::getUnsetSortIndex() );
00293     }
00294 }
00295 
00296 void WGEColormapping::callback( osg::Node* node )
00297 {
00298     // get node info
00299     NodeInfoContainerType::ReadTicket r = m_nodeInfo.getReadTicket();
00300     NodeInfoContainerType::ConstIterator infoItem = r->get().find( node );
00301     if( infoItem == r->get().end() )
00302     {
00303         return;
00304     }
00305     r.reset();
00306 
00307     NodeInfo* info = infoItem->second;
00308 
00309     // need (re-)binding?
00310     if( info->m_rebind )
00311     {
00312         info->m_rebind = false;
00313 
00314         size_t maxTexUnits = wge::getMaxTexUnits();
00315         wge::unbindTexture( node, info->m_texUnitStart, maxTexUnits - info->m_texUnitStart );
00316 
00317         TextureContainerType::ReadTicket rt = m_textures.getReadTicket();
00318 
00319         // bind each texture, provide all needed uniforms too
00320         size_t unit = info->m_texUnitStart;
00321         for( TextureContainerType::ConstIterator iter = rt->get().begin();
00322              ( unit < maxTexUnits ) && ( iter != rt->get().end() );
00323              ++iter )
00324         {
00325             wge::bindTexture( node, *iter, unit, "u_colormap" + string_utils::toString( unit - info->m_texUnitStart ) );
00326             unit++;
00327         }
00328 
00329         rt.reset();
00330     }
00331 }
00332 
00333 bool WGEColormapping::moveDown( osg::ref_ptr< WGETexture3D > texture )
00334 {
00335     TextureContainerType::WriteTicket w = m_textures.getWriteTicket();
00336 
00337     // does the texture exist?
00338     TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
00339     if( iter == w->get().end() )
00340     {
00341         return false;
00342     }
00343 
00344     // is it already the last item?
00345     if( iter + 1 == w->get().end() )
00346     {
00347         return false;
00348     }
00349 
00350     // swap items
00351     std::iter_swap( iter, iter + 1 );
00352 
00353     // unlock and call callbacks
00354     w.reset();
00355     m_sortSignal();
00356 
00357     return true;
00358 }
00359 
00360 bool WGEColormapping::moveUp( osg::ref_ptr< WGETexture3D > texture )
00361 {
00362     TextureContainerType::WriteTicket w = m_textures.getWriteTicket();
00363 
00364     // does the texture exist?
00365     TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
00366     if( iter == w->get().end() )
00367     {
00368         return false;
00369     }
00370 
00371     // is it already the first item?
00372     if( iter == w->get().begin() )
00373     {
00374         return false;
00375     }
00376 
00377     // swap items
00378     std::iter_swap( iter, iter - 1 );
00379 
00380     // unlock and call callbacks
00381     w.reset();
00382     m_sortSignal();
00383 
00384     return true;
00385 }
00386 
00387 bool WGEColormapping::moveToTop( osg::ref_ptr< WGETexture3D > texture )
00388 {
00389     TextureContainerType::WriteTicket w = m_textures.getWriteTicket();
00390 
00391     // does the texture exist?
00392     TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
00393     if( iter == w->get().end() )
00394     {
00395         return false;
00396     }
00397 
00398     // is it already the first item?
00399     if( iter == w->get().begin() )
00400     {
00401         return false;
00402     }
00403 
00404     // do the op
00405     w->get().erase( iter );
00406     w->get().insert( w->get().begin(), texture );
00407 
00408     // unlock and call callbacks
00409     w.reset();
00410     m_sortSignal();
00411 
00412     return true;
00413 }
00414 
00415 bool WGEColormapping::moveToBottom( osg::ref_ptr< WGETexture3D > texture )
00416 {
00417     TextureContainerType::WriteTicket w = m_textures.getWriteTicket();
00418 
00419     // does the texture exist?
00420     TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
00421     if( iter == w->get().end() )
00422     {
00423         return false;
00424     }
00425 
00426     // is it already the last item?
00427     if( iter + 1 == w->get().end() )
00428     {
00429         return false;
00430     }
00431 
00432     // do the op
00433     w->get().erase( iter );
00434     w->get().push_back( texture );
00435 
00436     // unlock and call callbacks
00437     w.reset();
00438     m_sortSignal();
00439 
00440     return true;
00441 }
00442 
00443 bool WGEColormapping::moveTo( osg::ref_ptr< WGETexture3D > texture, size_t idx )
00444 {
00445     TextureContainerType::WriteTicket w = m_textures.getWriteTicket();
00446 
00447     // does the texture exist?
00448     TextureContainerType::Iterator iter = std::find( w->get().begin(), w->get().end(), texture );
00449     if( iter == w->get().end() )
00450     {
00451         return false;
00452     }
00453 
00454     // valid index?
00455     // NOTE: we accept index == size as the end iterator.
00456     if( idx > w->get().size() )
00457     {
00458         return false;
00459     }
00460 
00461     // is it already there?
00462     if( iter == ( w->get().begin() + idx  ) )
00463     {
00464         return false;
00465     }
00466 
00467     // after inserting the item somewhere, the index of the original item might change
00468     size_t eraseIdx = iter - w->get().begin();  // item is inserted behind the current one -> index of the original item stays the same
00469     size_t eraseShift = 0;
00470     // if the inserted element is in front of the old one, the old one's index is increasing
00471     if( ( w->get().begin() + idx ) < iter )
00472     {
00473         eraseShift++;
00474     }
00475 
00476     // do the op
00477     // NOTE: this is not the best way to do it. Manually moving items should be better. But as the colormapper has to handle only a small number
00478     // of elements, this is not critical.
00479     w->get().insert( w->get().begin() + idx, texture );
00480     w->get().erase( w->get().begin() + eraseIdx + eraseShift );
00481 
00482     // unlock and call callbacks
00483     w.reset();
00484     m_sortSignal();
00485     return true;
00486 }
00487 
00488 size_t WGEColormapping::size() const
00489 {
00490     return m_textures.size();
00491 }
00492 
00493 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureRegisterHandler notifier )
00494 {
00495     switch( signal )
00496     {
00497         case Registered:
00498             return m_registerSignal.connect( notifier );
00499         case Deregistered:
00500             return m_deregisterSignal.connect( notifier );
00501         default:
00502             throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureRegisterHandler to sort signal." ) );
00503     }
00504 }
00505 
00506 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureReplaceHandler notifier )
00507 {
00508     switch( signal )
00509     {
00510         case Replaced:
00511             return m_replaceSignal.connect( notifier );
00512         default:
00513             throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureReplaceHandler to signal." ) );
00514     }
00515 }
00516 
00517 boost::signals2::connection WGEColormapping::subscribeSignal( TextureListSignal signal, TextureSortHandler notifier )
00518 {
00519     switch( signal )
00520     {
00521         case Sorted:
00522             return m_sortSignal.connect( notifier );
00523         default:
00524             throw new WGESignalSubscriptionFailed( std::string( "Could not register TextureSortHandler to register/deregister signal." ) );
00525     }
00526 }
00527 
00528 WGEColormapping::TextureContainerType::ReadTicket WGEColormapping::getReadTicket()
00529 {
00530     return m_textures.getReadTicket();
00531 }
00532 
00533 WCondition::SPtr WGEColormapping::getChangeCondition() const
00534 {
00535     return m_textures.getChangeCondition();
00536 }
00537