WGEShader.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 <map>
00026 #include <ostream>
00027 #include <sstream>
00028 #include <string>
00029 
00030 #include <boost/algorithm/string.hpp>
00031 // Use filesystem version 2 for compatibility with newer boost versions.
00032 #ifndef BOOST_FILESYSTEM_VERSION
00033     #define BOOST_FILESYSTEM_VERSION 2
00034 #endif
00035 #include <boost/filesystem.hpp>
00036 #include <boost/function.hpp>
00037 #include <boost/lexical_cast.hpp>
00038 #include <boost/regex.hpp>
00039 #include <boost/signals2/signal.hpp>
00040 #include <boost/tokenizer.hpp>
00041 
00042 #include <osg/Node>
00043 #include <osg/StateSet>
00044 
00045 #include "../../common/WLogger.h"
00046 #include "../../common/WPathHelper.h"
00047 #include "../../common/WPredicateHelper.h"
00048 #include "../WGraphicsEngine.h"
00049 #include "WGEShader.h"
00050 #include "WGEShaderPreprocessor.h"
00051 #include "WGEShaderVersionPreprocessor.h"
00052 
00053 WGEShader::WGEShader( std::string name, boost::filesystem::path search ):
00054     osg::Program(),
00055     m_shaderPath( search ),
00056     m_name( name ),
00057     m_reload( true ),
00058     m_shaderLoaded( false ),
00059     m_deactivated( false )
00060 {
00061     // create shader
00062     m_vertexShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::VERTEX ) );
00063     m_fragmentShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::FRAGMENT ) );
00064     m_geometryShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::GEOMETRY ) );
00065 
00066     // add them
00067     addShader( m_vertexShader );
00068     addShader( m_fragmentShader );
00069     addShader( m_geometryShader );
00070 
00071     // this preprocessor is always needed. It removes the #version statement from the code and puts it to the beginning.
00072     m_versionPreprocessor = WGEShaderPreprocessor::SPtr( new WGEShaderVersionPreprocessor() );
00073 
00074     m_reloadSignalConnection = WGraphicsEngine::getGraphicsEngine()->subscribeSignal( GE_RELOADSHADERS, boost::bind( &WGEShader::reload, this ) );
00075 }
00076 
00077 WGEShader::~WGEShader()
00078 {
00079     // cleanup
00080     m_reloadSignalConnection.disconnect();
00081 }
00082 
00083 void WGEShader::apply( osg::ref_ptr< osg::Node > node )
00084 {
00085     // set the shader attribute
00086     // NOTE: the attribute is protected to avoid father nodes overwriting it
00087     osg::StateSet* rootState = node->getOrCreateStateSet();
00088     rootState->setAttributeAndModes( this, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
00089     m_deactivated = false;
00090     m_reload = m_reload || !m_shaderLoaded;
00091 
00092     // add a custom callback which actually sets and updated the shader.
00093     node->addUpdateCallback( osg::ref_ptr< SafeUpdaterCallback >( new SafeUpdaterCallback( this ) ) );
00094 }
00095 
00096 void WGEShader::applyDirect( osg::State& state ) // NOLINT <- ensure this matches the official OSG API by using a non-const ref
00097 {
00098     updatePrograms();
00099     osg::Program::apply( state );
00100 }
00101 
00102 void WGEShader::deactivate( osg::ref_ptr< osg::Node > node )
00103 {
00104     if( !node )
00105     {
00106         return;
00107     }
00108 
00109     // set the shader attribute
00110     // NOTE: the attribute is protected to avoid father nodes overwriting it
00111     osg::StateSet* rootState = node->getOrCreateStateSet();
00112     rootState->setAttributeAndModes( this, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
00113 
00114     m_deactivated = true;
00115     m_shaderLoaded = false;
00116 
00117     // add a custom callback which actually sets and updated the shader.
00118     node->addUpdateCallback( osg::ref_ptr< SafeUpdaterCallback >( new SafeUpdaterCallback( this ) ) );
00119 }
00120 
00121 void WGEShader::reload()
00122 {
00123     m_reload = true;
00124 }
00125 
00126 void WGEShader::reloadShader()
00127 {
00128     try
00129     {
00130         // remove the shaders
00131         removeShader( m_vertexShader );
00132         removeShader( m_fragmentShader );
00133         removeShader( m_geometryShader );
00134 
00135         // reload the sources and set the shader
00136         // vertex shader
00137         WLogger::getLogger()->addLogMessage( "Reloading vertex shader \"" + m_name + "-vertex.glsl\"", "WGEShader", LL_DEBUG );
00138         std::string source = processShader( m_name + "-vertex.glsl" );
00139         if( source != "" )
00140         {
00141             m_vertexShader->setShaderSource( source );
00142             addShader( m_vertexShader );
00143         }
00144 
00145         // fragment shader
00146         WLogger::getLogger()->addLogMessage( "Reloading fragment shader \"" + m_name + "-fragment.glsl\"", "WGEShader", LL_DEBUG );
00147         source = processShader( m_name + "-fragment.glsl" );
00148         if( source != "" )
00149         {
00150             m_fragmentShader->setShaderSource( source );
00151             addShader( m_fragmentShader );
00152         }
00153 
00154         // Geometry Shader
00155         WLogger::getLogger()->addLogMessage( "Reloading geometry shader \"" + m_name + "-geometry.glsl\"", "WGEShader", LL_DEBUG );
00156         source = processShader( m_name + "-geometry.glsl", true );
00157         if( source != "" )
00158         {
00159             m_geometryShader->setShaderSource( source );
00160             addShader( m_geometryShader );
00161         }
00162 
00163         m_shaderLoaded = true;
00164     }
00165     catch( const std::exception& e )
00166     {
00167         m_shaderLoaded = false;
00168 
00169         WLogger::getLogger()->addLogMessage( "Problem loading shader.", "WGEShader", LL_ERROR );
00170 
00171         // clean up the mess
00172         removeShader( m_vertexShader );
00173         removeShader( m_fragmentShader );
00174         removeShader( m_geometryShader );
00175     }
00176 
00177     // everything done now.
00178     m_reload = false;
00179 }
00180 
00181 void WGEShader::updatePrograms()
00182 {
00183     // is it needed to do something here?
00184     if( m_deactivated )
00185     {
00186         // remove the shaders
00187         removeShader( m_vertexShader );
00188         removeShader( m_fragmentShader );
00189         removeShader( m_geometryShader );
00190     }
00191     else if( m_reload )
00192     {
00193         reloadShader();
00194     }
00195 }
00196 
00197 WGEShader::SafeUpdaterCallback::SafeUpdaterCallback( WGEShader* shader ):
00198     m_shader( shader )
00199 {
00200 }
00201 
00202 void WGEShader::SafeUpdaterCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
00203 {
00204     m_shader->updatePrograms();
00205 
00206     // forward the call
00207     traverse( node, nv );
00208 }
00209 
00210 std::string WGEShader::processShaderRecursive( const std::string filename, bool optional, int level )
00211 {
00212     std::stringstream output;    // processed output
00213 
00214     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00215     // Before the preprocessors get applied, the following code build the complete shader code from many parts (includes) and handles the version
00216     // statement automatically. This is important since the GLSL compiler (especially ATI's) relies on it. After completely loading the whole
00217     // code, the preprocessors get applied.
00218     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00219 
00220     // we encountered an endless loop
00221     if( level > 32 )
00222     {
00223         // reached a certain level. This normally denotes a inclusion cycle.
00224         // We do not throw an exception here to avoid OSG to crash.
00225         WLogger::getLogger()->addLogMessage( "Inclusion depth is too large. Maybe there is a inclusion cycle in the shader code.",
00226                 "WGEShader (" + filename + ")", LL_ERROR
00227         );
00228 
00229         // just return unprocessed source
00230         return "";
00231     }
00232 
00233     // this is the proper regular expression for includes. This also excludes commented includes
00234     static const boost::regex includeRegexp( "^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
00235 
00236     // the input stream, first check existence of shader
00237     // search these places in this order:
00238     // 1. m_shaderPath
00239     // 2. m_shaderPath / shaders
00240     // 3. WPathHelper::getShaderPath()
00241 
00242     // use one of the following paths
00243     std::string fn = filename;
00244     std::string fnLocal = ( m_shaderPath / filename ).file_string();
00245     std::string fnLocalShaders = ( m_shaderPath / "shaders" / filename ).file_string();
00246     std::string fnGlobal = ( WPathHelper::getShaderPath() / filename ).file_string();
00247 
00248     if( boost::filesystem::exists( m_shaderPath / filename ) )
00249     {
00250         fn = fnLocal;
00251     }
00252     else if( boost::filesystem::exists( m_shaderPath / "shaders" / filename ) )
00253     {
00254         fn = fnLocalShaders;
00255     }
00256     else if( boost::filesystem::exists( WPathHelper::getShaderPath() / filename ) )
00257     {
00258         fn = fnGlobal;
00259     }
00260     else if( !optional )
00261     {
00262         WLogger::getLogger()->addLogMessage( "The requested shader \"" + filename + "\" does not exist in \"" +
00263                                              m_shaderPath.file_string() + "\", \"" + ( m_shaderPath / "shaders" ).file_string() + "\" or \"" +
00264                                              WPathHelper::getShaderPath().file_string() + "\".", "WGEShader (" + filename + ")", LL_ERROR
00265         );
00266 
00267         return "";
00268     }
00269     else
00270     {
00271         return "";
00272     }
00273 
00274     std::ifstream input( fn.c_str() );
00275     if( !input.is_open() )
00276     {
00277         if( optional )
00278         {
00279             return "";
00280         }
00281 
00282         // file does not exist. Do not throw an exception to avoid OSG crash
00283         if( level == 0 )
00284         {
00285             WLogger::getLogger()->addLogMessage( "Can't open shader file \"" + filename + "\".",
00286                     "WGEShader (" + filename + ")", LL_ERROR
00287             );
00288         }
00289         else
00290         {
00291             WLogger::getLogger()->addLogMessage( "Can't open shader file for inclusion \"" + filename + "\".",
00292                     "WGEShader (" + filename + ")", LL_ERROR
00293             );
00294         }
00295 
00296         return "";
00297     }
00298 
00299     // go through each line and process includes
00300     std::string line;               // the current line
00301     boost::smatch matches;          // the list of matches
00302 
00303     while( std::getline( input, line ) )
00304     {
00305         if( boost::regex_search( line, matches, includeRegexp ) )
00306         {
00307             output << processShaderRecursive( matches[1], false, level + 1 );
00308         }
00309         else
00310         {
00311             output << line;
00312         }
00313 
00314         // NOTE: we do not apply the m_processors here since the recursive processShaders may have produced many lines. We would need to loop
00315         // through each one of them. This is done later on for the whole code.
00316 
00317         output << std::endl;
00318     }
00319 
00320     input.close();
00321 
00322     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00323     // Done. Return code.
00324     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00325 
00326     // this string contains the processed shader code
00327     return output.str();
00328 }
00329 
00330 std::string WGEShader::processShader( const std::string filename, bool optional )
00331 {
00332     // load all the code
00333     std::string code = processShaderRecursive( filename, optional );
00334     if( code.empty() )
00335     {
00336         return "";
00337     }
00338 
00339     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00340     // The whole code is loaded now. Apply preprocessors.
00341     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00342 
00343     // apply all preprocessors
00344     PreprocessorsList::ReadTicket r = m_preprocessors.getReadTicket();
00345     for( PreprocessorsList::ConstIterator pp = r->get().begin(); pp != r->get().end(); ++pp )
00346     {
00347         code = ( *pp ).first->process( filename, code );
00348     }
00349     r.reset();
00350 
00351     // finally ensure ONE #version at the beginning.
00352     return m_versionPreprocessor->process( filename, code );
00353 }
00354 
00355 void WGEShader::addPreprocessor( WGEShaderPreprocessor::SPtr preproc )
00356 {
00357     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00358     if( !w->get().count( preproc ) )   // if already exists, no connection needed
00359     {
00360         // subscribe the preprocessors update condition
00361         boost::signals2::connection con = preproc->getChangeCondition()->subscribeSignal( boost::bind( &WGEShader::reload, this ) );
00362         w->get().insert( std::make_pair( preproc, con ) );
00363     }
00364     w.reset();
00365     reload();
00366 }
00367 
00368 void WGEShader::removePreprocessor( WGEShaderPreprocessor::SPtr preproc )
00369 {
00370     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00371     if( w->get().count( preproc ) )   // is it in our list?
00372     {
00373         w->get().operator[]( preproc ).disconnect();
00374         w->get().erase( preproc );
00375     }
00376     w.reset();
00377     reload();
00378 }
00379 
00380 void WGEShader::clearPreprocessors()
00381 {
00382     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00383 
00384     // we need to disconnect each signal subscription
00385     for( PreprocessorsList::Iterator pp = w->get().begin(); pp != w->get().end(); ++pp )
00386     {
00387         ( *pp ).second.disconnect();
00388     }
00389     w->get().clear();
00390     w.reset();
00391     reload();
00392 }
00393 
00394 WGEShaderDefineSwitch::SPtr WGEShader::setDefine( std::string key )
00395 {
00396     return this->setDefine< bool >( key, true );
00397 }
00398 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends