Commit 32c25444 authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[CHANGE] - removed old shader class and replaced by new one

parent 4e394aca
......@@ -22,147 +22,204 @@
//
//---------------------------------------------------------------------------
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#include "WShader.h"
#include <osg/StateSet>
#include <osg/Node>
#include "WGraphicsEngine.h"
#include "../common/WLogger.h"
WShader::WShader()
#include "WShader.h"
WShader::WShader( std::string name ):
osg::Program(),
m_shaderPath( WGraphicsEngine::getGraphicsEngine()->getShaderPath() ),
m_name( name ),
m_reload( true )
{
// create shader
m_vertexShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::VERTEX ) );
m_fragmentShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::FRAGMENT ) );
m_geometryShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::GEOMETRY ) );
// add them
addShader( m_vertexShader );
addShader( m_fragmentShader );
addShader( m_geometryShader );
}
WShader::WShader( std::string fileName, std::string shaderPath ):
m_shaderPath( shaderPath )
WShader::~WShader()
{
m_VertexObject = readShaderFromFile( fileName + ".vs", osg::Shader::VERTEX );
m_FragmentObject = readShaderFromFile( fileName + ".fs", osg::Shader::FRAGMENT );
m_ProgramObject = new osg::Program;
// cleanup
}
if ( m_FragmentObject )
{
m_ProgramObject->addShader( m_FragmentObject );
}
void WShader::apply( osg::ref_ptr< osg::Node > node )
{
// set the shader attribute
osg::StateSet* rootState = node->getOrCreateStateSet();
rootState->setAttributeAndModes( this, osg::StateAttribute::ON );
if ( m_VertexObject )
{
m_ProgramObject->addShader( m_VertexObject );
}
// add a custom callback which actually sets and updated the shader.
node->addUpdateCallback( osg::ref_ptr< SafeUpdaterCallback >( new SafeUpdaterCallback( this ) ) );
}
WShader::~WShader()
void WShader::reload()
{
m_reload = true;
}
WShader::SafeUpdaterCallback::SafeUpdaterCallback( WShader* shader ):
m_shader( shader )
{
}
osg::Shader* WShader::readShaderFromFile( std::string fileName, osg::Shader::Type type )
void WShader::SafeUpdaterCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
{
std::string fileText = readTextFile( fileName );
// is it needed to do something here?
if ( m_shader->m_reload )
{
try
{
// remove the shaders
m_shader->removeShader( m_shader->m_vertexShader );
m_shader->removeShader( m_shader->m_fragmentShader );
m_shader->removeShader( m_shader->m_geometryShader );
// reload the sources and set the shader
// vertex shader
WLogger::getLogger()->addLogMessage( "Reloading vertex shader \"" + m_shader->m_name + ".vs\"", "WShader", LL_DEBUG );
std::string source = m_shader->processShader( m_shader->m_name + ".vs" );
if ( source != "" )
{
m_shader->m_vertexShader->setShaderSource( source );
m_shader->addShader( m_shader->m_vertexShader );
}
// fragment shader
WLogger::getLogger()->addLogMessage( "Reloading fragment shader \"" + m_shader->m_name + ".fs\"", "WShader", LL_DEBUG );
source = m_shader->processShader( m_shader->m_name + ".fs" );
if ( source != "" )
{
m_shader->m_fragmentShader->setShaderSource( source );
m_shader->addShader( m_shader->m_fragmentShader );
}
// Geometry Shader
WLogger::getLogger()->addLogMessage( "Reloading geometry shader \"" + m_shader->m_name + ".gs\"", "WShader", LL_DEBUG );
source = m_shader->processShader( m_shader->m_name + ".gs", true );
if ( source != "" )
{
m_shader->m_geometryShader->setShaderSource( source );
m_shader->addShader( m_shader->m_geometryShader );
}
}
catch( const std::exception& e )
{
WLogger::getLogger()->addLogMessage( "Problem loading shader.", "WShader", LL_ERROR );
// std::cout << "\n=====OW==SHADER============\n"
// << fileText
// << "\n=====OW==SHADER=END========\n"
// << std::endl;
// clean up the mess
m_shader->removeShader( m_shader->m_vertexShader );
m_shader->removeShader( m_shader->m_fragmentShader );
m_shader->removeShader( m_shader->m_geometryShader );
}
osg::Shader* shader = new osg::Shader( type, fileText );
// everything done now.
m_shader->m_reload = false;
}
return shader;
// forward the call
traverse( node, nv );
}
std::string WShader::readTextFile( std::string fileName )
std::string WShader::processShader( const std::string filename, bool optional, int level )
{
std::string fileText;
namespace fs = boost::filesystem;
std::stringstream output; // processed output
std::ifstream ifs( ( ( fs::path( m_shaderPath ) / fileName ).file_string() ).c_str() );
// std::ifstream ifs( ( m_shaderPath + fileName ).c_str() );
std::string line;
if ( level == 0 )
{
// for the shader (not the included one, for which level != 0)
std::map< std::string, float >::const_iterator mi = m_defines.begin();
// apply defines
std::map< std::string, float >::const_iterator mi = m_defines.begin();
while ( mi != m_defines.end() )
{
output << "#define " << mi->first << " " << boost::lexical_cast< std::string, float >( mi->second ) << std::endl;
}
}
while ( mi != m_defines.end() )
// we encountered an endless loop
if ( level > 32 )
{
fileText += "#define ";
fileText += mi->first;
fileText += " ";
fileText += boost::lexical_cast< std::string, float >( mi->second );
fileText += '\n';
// reached a certain level. This normally denotes a inclusion cycle.
// We do not throw an exception here to avoid OSG to crash.
WLogger::getLogger()->addLogMessage( "Inclusion depth is too large. Maybe there is a inclusion cycle in the shader code.",
"WShader (" + filename + ")", LL_ERROR
);
// just return unprocessed source
return "";
}
while ( getline( ifs, line ) )
// this is the proper regular expression for includes. This also excludes commented includes
static const boost::regex re( "^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
// the input stream
std::string fn = ( boost::filesystem::path( m_shaderPath ) / filename ).file_string();
std::ifstream input( fn.c_str() );
if ( !input.is_open() )
{
if ( isIncludeLine( line ) )
if ( optional )
{
fileText += readTextFile( getIncludeFileName( line ) );
return "";
}
// file does not exists. Do not throw an exception to avoid OSG crash
if ( level == 0 )
{
WLogger::getLogger()->addLogMessage( "Can't open shader file \"" + fn + "\".",
"WShader (" + filename + ")", LL_ERROR
);
}
else
{
fileText += line;
fileText += '\n';
WLogger::getLogger()->addLogMessage( "Can't open shader file for inclusion \"" + fn + "\".",
"WShader (" + filename + ")", LL_ERROR
);
}
}
return fileText;
}
bool WShader::isIncludeLine( std::string line )
{
if( line[0] == '/' && line[1] == '/' )
{
return false; // we encountered a comment
return "";
}
if ( boost::find_first( line, "#include" ) )
{
return true;
}
return false;
}
// go through each line and process includes
std::string line; // the current line
boost::smatch matches; // the list of matches
std::string WShader::getIncludeFileName( std::string line )
{
std::string fileName;
int count = 0;
for ( size_t i = 0 ; i < line.length() ; ++i )
while ( std::getline( input, line ) )
{
if ( line[i] == '\"' )
if ( boost::regex_search( line, matches, re ) )
{
++count;
output << processShader( matches[1], false, level + 1 );
}
else
{
output << line;
}
}
if ( count < 2 )
{
WLogger::getLogger()->addLogMessage( "Missing quotes around file name in include statement of shader.", "WShader", LL_ERROR );
// TODO(schurade): here we could throw an exception
return 0;
}
typedef boost::tokenizer< boost::char_separator< char > > tokenizer;
boost::char_separator<char> sep( "\"" );
tokenizer tok( line, sep );
tokenizer::iterator it = tok.begin();
++it;
return *it;
}
output << std::endl;
}
input.close();
osg::Program* WShader::getProgramObject()
{
return m_ProgramObject;
return output.str();
}
void WShader::setDefine( std::string key, float value )
......@@ -173,7 +230,6 @@ void WShader::setDefine( std::string key, float value )
}
}
void WShader::eraseDefine( std::string key )
{
m_defines.erase( key );
......
......@@ -25,46 +25,45 @@
#ifndef WSHADER_H
#define WSHADER_H
#include <string>
#include <map>
#include <string>
#include <osg/Geometry>
#include <boost/filesystem.hpp>
#include <boost/shared_ptr.hpp>
#include <osg/Shader>
#include <osg/Program>
/**
* Class the encapsulates the loading and compiling of shader objects
* \ingroup ge
* Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader.
*/
class WShader
class WShader: public osg::Program
{
public:
/**
* default constructor
*/
WShader();
/**
* constructor with initialization
* Default constructor. Loads the specified shader programs.
*
* \param fileName the file name
* \param shaderPath path to the directory where all shader file are stored
* \param name the name of the shader. It gets searched in the shader path.
*/
WShader( std::string fileName, std::string shaderPath );
explicit WShader( std::string name );
/**
* destructor
* Destructor.
*/
virtual ~WShader();
/**
* Returns the OSG program object
* Apply this shader to the specified node. Use this method to ensure, that reload events can be handled properly during the
* update cycle.
*
* \return the program object
* \param node the node where the program should be registered to.
*/
virtual void apply( osg::ref_ptr< osg::Node > node );
/**
* Initiate a reload of the shader during the next update cycle.
*/
osg::Program* getProgramObject();
virtual void reload();
/**
* Sets a define which is include into the shader source code.
......@@ -82,68 +81,89 @@ public:
*/
void eraseDefine( std::string key );
private:
protected:
/**
* Helper routine to load a shader program from a text file
* This method searches and processes all includes in the shader source. The filenames in the include statement are assumed to
* be relative to this shader's path.
*
* \return An osg shader
* \param filename the filename of the shader to process.
* \param optional denotes whether a "file not found" is critical or not
* \param level the inclusion level. This is used to avoid cycles.
*
* \param fileName the file name
* \param type The type of the shader, eg. Vertex or fragment shader
* \return the processed source.
*/
osg::Shader* readShaderFromFile( std::string fileName, osg::Shader::Type type );
std::string processShader( const std::string filename, bool optional = false, int level = 0 );
/**
* Helper routine to load a text file into a string
*
* \return String
*
* \param fileName The file name
* String that stores the location of all shader files
*/
std::string readTextFile( std::string fileName );
std::string m_shaderPath;
/**
* Helper routine to test if a line contains an include statement
*
* \return true if line contains an include statement
*
* \param line a string with the line
* The name of the shader. It is used to construct the actual filename to load.
*/
bool isIncludeLine( std::string line );
std::string m_name;
/**
* Parses a line with an include statement and returns the file name
*
* \return string with the file name
*
* \param line a string with the line
* Flag denoting whether a shader should be reloaded.
*/
std::string getIncludeFileName( std::string line );
bool m_reload;
/**
* String that stores the location of all shader files
* a map of all set defines
*/
std::string m_shaderPath;
std::map< std::string, float > m_defines;
/**
* the vertex shader object
*/
osg::Shader* m_VertexObject;
osg::ref_ptr< osg::Shader > m_vertexShader;
/**
* the fragment shader object
*/
osg::Shader* m_FragmentObject;
osg::ref_ptr< osg::Shader > m_fragmentShader;
/**
* the shader program
* the geometry shader object
*/
osg::Program* m_ProgramObject;
osg::ref_ptr< osg::Shader > m_geometryShader;
/**
* a map of all set defines
* Update callback which handles the shader reloading.
* This ensures thread safe modification of the osg node.
*/
std::map< std::string, float>m_defines;
class SafeUpdaterCallback : public osg::NodeCallback
{
public:
/**
* Constructor. Creates a new callback.
*
* \param shader the shader which needs to be updated.
*/
explicit SafeUpdaterCallback( WShader* shader );
/**
* Callback method called by the NodeVisitor when visiting a node.
* This inserts and removes enqueued nodes from this group node instance.
*
* \param node the node calling this update
* \param nv The node visitor which performs the traversal. Should be an
* update visitor.
*/
virtual void operator()( osg::Node* node, osg::NodeVisitor* nv );
protected:
/**
* The shader belonging to the node currently getting updated.
*/
WShader* m_shader;
};
private:
};
#endif // WSHADER_H
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <map>
#include <string>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#include <osg/StateSet>
#include <osg/Node>
#include "WGraphicsEngine.h"
#include "../common/WLogger.h"
#include "WShader2.h"
WShader2::WShader2( std::string name ):
osg::Program(),
m_shaderPath( WGraphicsEngine::getGraphicsEngine()->getShaderPath() ),
m_name( name ),
m_reload( true )
{
// create shader
m_vertexShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::VERTEX ) );
m_fragmentShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::FRAGMENT ) );
m_geometryShader = osg::ref_ptr< osg::Shader >( new osg::Shader( osg::Shader::GEOMETRY ) );
// add them
addShader( m_vertexShader );
addShader( m_fragmentShader );
addShader( m_geometryShader );
}
WShader2::~WShader2()
{
// cleanup
}
void WShader2::apply( osg::ref_ptr< osg::Node > node )
{
// set the shader attribute
osg::StateSet* rootState = node->getOrCreateStateSet();
rootState->setAttributeAndModes( this, osg::StateAttribute::ON );
// add a custom callback which actually sets and updated the shader.
node->addUpdateCallback( osg::ref_ptr< SafeUpdaterCallback >( new SafeUpdaterCallback( this ) ) );
}
void WShader2::reload()
{
m_reload = true;
}
WShader2::SafeUpdaterCallback::SafeUpdaterCallback( WShader2* shader ):
m_shader( shader )
{
}
void WShader2::SafeUpdaterCallback::operator()( osg::Node* node, osg::NodeVisitor* nv )
{
// is it needed to do something here?
if ( m_shader->m_reload )
{
try
{
// remove the shaders
m_shader->removeShader( m_shader->m_vertexShader );
m_shader->removeShader( m_shader->m_fragmentShader );
m_shader->removeShader( m_shader->m_geometryShader );
// reload the sources and set the shader
// vertex shader
WLogger::getLogger()->addLogMessage( "Reloading vertex shader \"" + m_shader->m_name + ".vs\"", "WShader", LL_DEBUG );
std::string source = m_shader->processShader( m_shader->m_name + ".vs" );
if ( source != "" )
{
m_shader->m_vertexShader->setShaderSource( source );
m_shader->addShader( m_shader->m_vertexShader );
}
// fragment shader
WLogger::getLogger()->addLogMessage( "Reloading fragment shader \"" + m_shader->m_name + ".fs\"", "WShader", LL_DEBUG );
source = m_shader->processShader( m_shader->m_name + ".fs" );
if ( source != "" )
{
m_shader->m_fragmentShader->setShaderSource( source );
m_shader->addShader( m_shader->m_fragmentShader );
}
// Geometry Shader
WLogger::getLogger()->addLogMessage( "Reloading geometry shader \"" + m_shader->m_name + ".gs\"", "WShader", LL_DEBUG );
source = m_shader->processShader( m_shader->m_name + ".gs", true );
if ( source != "" )
{
m_shader->m_geometryShader->setShaderSource( source );
m_shader->addShader( m_shader->m_geometryShader );