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