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