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