OpenWalnut 1.3.1
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 
00299     while( std::getline( input, line ) )
00300     {
00301         if( boost::regex_search( line, matches, includeRegexp ) )
00302         {
00303             output << processShaderRecursive( matches[1], false, level + 1 );
00304         }
00305         else
00306         {
00307             output << line;
00308         }
00309 
00310         // NOTE: we do not apply the m_processors here since the recursive processShaders may have produced many lines. We would need to loop
00311         // through each one of them. This is done later on for the whole code.
00312 
00313         output << std::endl;
00314     }
00315 
00316     input.close();
00317 
00318     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00319     // Done. Return code.
00320     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00321 
00322     // this string contains the processed shader code
00323     return output.str();
00324 }
00325 
00326 std::string WGEShader::processShader( const std::string filename, bool optional )
00327 {
00328     // load all the code
00329     std::string code = processShaderRecursive( filename, optional );
00330     if( code.empty() )
00331     {
00332         return "";
00333     }
00334 
00335     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00336     // The whole code is loaded now. Apply preprocessors.
00337     //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
00338 
00339     // apply all preprocessors
00340     PreprocessorsList::ReadTicket r = m_preprocessors.getReadTicket();
00341     for( PreprocessorsList::ConstIterator pp = r->get().begin(); pp != r->get().end(); ++pp )
00342     {
00343         code = ( *pp ).first->process( filename, code );
00344     }
00345     r.reset();
00346 
00347     // finally ensure ONE #version at the beginning.
00348     return m_versionPreprocessor->process( filename, code );
00349 }
00350 
00351 void WGEShader::addPreprocessor( WGEShaderPreprocessor::SPtr preproc )
00352 {
00353     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00354     if( !w->get().count( preproc ) )   // if already exists, no connection needed
00355     {
00356         // subscribe the preprocessors update condition
00357         boost::signals2::connection con = preproc->getChangeCondition()->subscribeSignal( boost::bind( &WGEShader::reload, this ) );
00358         w->get().insert( std::make_pair( preproc, con ) );
00359     }
00360     w.reset();
00361     reload();
00362 }
00363 
00364 void WGEShader::removePreprocessor( WGEShaderPreprocessor::SPtr preproc )
00365 {
00366     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00367     if( w->get().count( preproc ) )   // is it in our list?
00368     {
00369         w->get().operator[]( preproc ).disconnect();
00370         w->get().erase( preproc );
00371     }
00372     w.reset();
00373     reload();
00374 }
00375 
00376 void WGEShader::clearPreprocessors()
00377 {
00378     PreprocessorsList::WriteTicket w = m_preprocessors.getWriteTicket();
00379 
00380     // we need to disconnect each signal subscription
00381     for( PreprocessorsList::Iterator pp = w->get().begin(); pp != w->get().end(); ++pp )
00382     {
00383         ( *pp ).second.disconnect();
00384     }
00385     w->get().clear();
00386     w.reset();
00387     reload();
00388 }
00389 
00390 WGEShaderDefineSwitch::SPtr WGEShader::setDefine( std::string key )
00391 {
00392     return this->setDefine< bool >( key, true );
00393 }
00394