OpenWalnut
1.2.5
|
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