00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <map>
00026 #include <ostream>
00027 #include <sstream>
00028 #include <string>
00029
00030 #include <boost/algorithm/string.hpp>
00031
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
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
00067 addShader( m_vertexShader );
00068 addShader( m_fragmentShader );
00069 addShader( m_geometryShader );
00070
00071
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
00080 m_reloadSignalConnection.disconnect();
00081 }
00082
00083 void WGEShader::apply( osg::ref_ptr< osg::Node > node )
00084 {
00085
00086
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
00093 node->addUpdateCallback( osg::ref_ptr< SafeUpdaterCallback >( new SafeUpdaterCallback( this ) ) );
00094 }
00095
00096 void WGEShader::applyDirect( osg::State& state )
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
00110
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
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
00131 removeShader( m_vertexShader );
00132 removeShader( m_fragmentShader );
00133 removeShader( m_geometryShader );
00134
00135
00136
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
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
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
00172 removeShader( m_vertexShader );
00173 removeShader( m_fragmentShader );
00174 removeShader( m_geometryShader );
00175 }
00176
00177
00178 m_reload = false;
00179 }
00180
00181 void WGEShader::updatePrograms()
00182 {
00183
00184 if( m_deactivated )
00185 {
00186
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
00207 traverse( node, nv );
00208 }
00209
00210 std::string WGEShader::processShaderRecursive( const std::string filename, bool optional, int level )
00211 {
00212 std::stringstream output;
00213
00214
00215
00216
00217
00218
00219
00220
00221 if( level > 32 )
00222 {
00223
00224
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
00230 return "";
00231 }
00232
00233
00234 static const boost::regex includeRegexp( "^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
00235
00236
00237
00238
00239
00240
00241
00242
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
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
00300 std::string line;
00301 boost::smatch 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
00315
00316
00317 output << std::endl;
00318 }
00319
00320 input.close();
00321
00322
00323
00324
00325
00326
00327 return output.str();
00328 }
00329
00330 std::string WGEShader::processShader( const std::string filename, bool optional )
00331 {
00332
00333 std::string code = processShaderRecursive( filename, optional );
00334 if( code.empty() )
00335 {
00336 return "";
00337 }
00338
00339
00340
00341
00342
00343
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
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 ) )
00359 {
00360
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 ) )
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
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