2016-02-16 17:18:48 +00:00
# include "shadermanager.hpp"
# include <fstream>
2016-02-20 18:26:59 +00:00
# include <algorithm>
2017-05-06 21:05:13 +00:00
# include <sstream>
2020-12-09 18:42:15 +00:00
# include <regex>
2016-02-16 17:18:48 +00:00
2016-02-17 14:04:05 +00:00
# include <osg/Program>
2016-02-16 17:18:48 +00:00
# include <boost/filesystem/path.hpp>
# include <boost/filesystem/fstream.hpp>
2021-02-21 18:38:15 +00:00
# include <components/sceneutil/lightmanager.hpp>
2018-08-14 15:42:41 +00:00
# include <components/debug/debuglog.hpp>
2020-01-09 11:57:05 +00:00
# include <components/misc/stringops.hpp>
2018-08-14 15:42:41 +00:00
2016-02-16 17:18:48 +00:00
namespace Shader
{
2021-03-02 07:30:54 +00:00
ShaderManager : : ShaderManager ( )
2021-03-15 04:42:34 +00:00
: mLightingMethod ( SceneUtil : : LightingMethod : : FFP )
2021-02-21 18:38:15 +00:00
{
}
2016-02-16 17:18:48 +00:00
void ShaderManager : : setShaderPath ( const std : : string & path )
{
mPath = path ;
}
2021-03-15 04:42:34 +00:00
void ShaderManager : : setLightingMethod ( SceneUtil : : LightingMethod method )
2021-03-02 07:30:54 +00:00
{
2021-03-15 04:42:34 +00:00
mLightingMethod = method ;
2021-03-02 07:30:54 +00:00
}
2017-11-21 16:53:12 +00:00
bool addLineDirectivesAfterConditionalBlocks ( std : : string & source )
{
for ( size_t position = 0 ; position < source . length ( ) ; )
{
size_t foundPos = source . find ( " #endif " , position ) ;
foundPos = std : : min ( foundPos , source . find ( " #elif " , position ) ) ;
foundPos = std : : min ( foundPos , source . find ( " #else " , position ) ) ;
if ( foundPos = = std : : string : : npos )
break ;
foundPos = source . find_first_of ( " \n \r " , foundPos ) ;
foundPos = source . find_first_not_of ( " \n \r " , foundPos ) ;
2020-08-04 01:46:54 +00:00
if ( foundPos = = std : : string : : npos )
break ;
2017-11-21 16:53:12 +00:00
size_t lineDirectivePosition = source . rfind ( " #line " , foundPos ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) - 1 ;
}
else
{
lineDirectivePosition = 0 ;
lineNumber = 1 ;
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + foundPos , ' \n ' ) ;
source . replace ( foundPos , 0 , " #line " + std : : to_string ( lineNumber ) + " \n " ) ;
position = foundPos ;
}
return true ;
}
2020-08-05 22:58:18 +00:00
// Recursively replaces include statements with the actual source of the included files.
// Adjusts #line statements accordingly and detects cyclic includes.
// includingFiles is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup.
static bool parseIncludes ( boost : : filesystem : : path shaderPath , std : : string & source , const std : : string & fileName , int & fileNumber , std : : set < boost : : filesystem : : path > includingFiles )
2016-02-17 22:29:07 +00:00
{
2020-08-05 22:58:18 +00:00
// An include is cyclic if it is being included by itself
if ( includingFiles . insert ( shaderPath / fileName ) . second = = false )
{
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Detected cyclic #includes " ;
return false ;
}
2020-01-09 11:57:05 +00:00
Misc : : StringUtils : : replaceAll ( source , " \r \n " , " \n " ) ;
2016-02-20 18:26:59 +00:00
2016-02-17 22:29:07 +00:00
size_t foundPos = 0 ;
while ( ( foundPos = source . find ( " #include " ) ) ! = std : : string : : npos )
{
size_t start = source . find ( ' " ' , foundPos ) ;
2020-08-05 22:58:18 +00:00
if ( start = = std : : string : : npos | | start = = source . size ( ) - 1 )
2016-02-17 22:29:07 +00:00
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Invalid #include " ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
size_t end = source . find ( ' " ' , start + 1 ) ;
2016-02-17 22:29:07 +00:00
if ( end = = std : : string : : npos )
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Invalid #include " ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
std : : string includeFilename = source . substr ( start + 1 , end - ( start + 1 ) ) ;
2016-02-17 22:29:07 +00:00
boost : : filesystem : : path includePath = shaderPath / includeFilename ;
2016-02-20 18:26:59 +00:00
2020-08-05 22:58:18 +00:00
// Determine the line number that will be used for the #line directive following the included source
2017-11-21 16:53:12 +00:00
size_t lineDirectivePosition = source . rfind ( " #line " , foundPos ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) - 1 ;
}
else
{
lineDirectivePosition = 0 ;
2020-10-03 12:22:34 +00:00
lineNumber = 0 ;
2017-11-21 16:53:12 +00:00
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + foundPos , ' \n ' ) ;
2016-02-20 18:26:59 +00:00
2020-08-05 22:58:18 +00:00
// Include the file recursively
boost : : filesystem : : ifstream includeFstream ;
includeFstream . open ( includePath ) ;
if ( includeFstream . fail ( ) )
{
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Failed to open include " < < includePath . string ( ) ;
return false ;
}
int includedFileNumber = fileNumber + + ;
2016-02-17 22:29:07 +00:00
2020-08-05 22:58:18 +00:00
std : : stringstream buffer ;
buffer < < includeFstream . rdbuf ( ) ;
std : : string stringRepresentation = buffer . str ( ) ;
if ( ! addLineDirectivesAfterConditionalBlocks ( stringRepresentation )
| | ! parseIncludes ( shaderPath , stringRepresentation , includeFilename , fileNumber , includingFiles ) )
2016-02-17 22:29:07 +00:00
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " In file included from " < < fileName < < " . " < < lineNumber ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
std : : stringstream toInsert ;
toInsert < < " #line 0 " < < includedFileNumber < < " \n " < < stringRepresentation < < " \n #line " < < lineNumber < < " 0 \n " ;
source . replace ( foundPos , ( end - foundPos + 1 ) , toInsert . str ( ) ) ;
2016-02-17 22:29:07 +00:00
}
return true ;
}
2020-12-09 18:42:15 +00:00
struct DeclarationMeta
{
std : : string interpolationType ;
std : : string interfaceKeyword ;
std : : string type ;
std : : string identifier ;
std : : string mangledIdentifier ;
} ;
// Mangle identifiers of the interface declarations of this shader source, updating all identifiers, and returning a list of the declarations for use in generating
// a geometry shader.
// IN/OUT source: The source to mangle
// IN interfaceKeywordPattern: A regular expression matching all interface keywords to look for (e.g. "out|varying" when mangling output variables). Must not contain subexpressions.
// IN mangleString: Identifiers are mangled by prepending this string. Must be a valid identifier prefix.
// OUT declarations: All mangled declarations are added to this vector. Includes interpolation, interface, and type information as well as both the mangled and unmangled identifier.
static void mangleInterface ( std : : string & source , const std : : string & interfaceKeywordPattern , const std : : string & mangleString , std : : vector < DeclarationMeta > & declarations )
{
std : : string commentPattern = " //.* " ;
std : : regex commentRegex ( commentPattern ) ;
std : : string commentlessSource = std : : regex_replace ( source , commentRegex , " " ) ;
std : : string identifierPattern = " [a-zA-Z_][0-9a-zA-Z_]* " ;
std : : string declarationPattern = " (centroid|flat)? \\ s* \\ b( " + interfaceKeywordPattern + " ) \\ s+( " + identifierPattern + " ) \\ s+( " + identifierPattern + " ) \\ s*; " ;
std : : regex declarationRegex ( declarationPattern ) ;
std : : vector < std : : smatch > matches ( std : : sregex_iterator ( commentlessSource . begin ( ) , commentlessSource . end ( ) , declarationRegex ) , std : : sregex_iterator ( ) ) ;
std : : string replacementPattern ;
for ( auto & match : matches )
{
declarations . emplace_back ( DeclarationMeta { match [ 1 ] . str ( ) , match [ 2 ] . str ( ) , match [ 3 ] . str ( ) , match [ 4 ] . str ( ) , mangleString + match [ 4 ] . str ( ) } ) ;
if ( ! replacementPattern . empty ( ) )
replacementPattern + = " | " ;
replacementPattern = replacementPattern + " ( " + declarations . back ( ) . identifier + " \\ b) " ;
}
if ( ! replacementPattern . empty ( ) )
{
std : : regex replacementRegex ( replacementPattern ) ;
source = std : : regex_replace ( source , replacementRegex , mangleString + " $& " ) ;
}
}
static std : : string generateGeometryShader ( const std : : string & geometryTemplate , const std : : vector < DeclarationMeta > & declarations )
{
if ( geometryTemplate . empty ( ) )
return " " ;
static std : : map < std : : string , std : : string > overriddenForwardStatements =
{
{ " linearDepth " , " linearDepth = gl_Position.z; " } ,
{ " euclideanDepth " , " euclideanDepth = length(viewPos.xyz); " } ,
{ " passViewPos " , " passViewPos = viewPos.xyz; " } ,
{
" screenCoordsPassthrough " ,
" mat4 scalemat = mat4(0.25, 0.0, 0.0, 0.0, \n "
" 0.0, -0.5, 0.0, 0.0, \n "
" 0.0, 0.0, 0.5, 0.0, \n "
" 0.25, 0.5, 0.5, 1.0); \n "
" vec4 texcoordProj = ((scalemat) * (gl_Position)); \n "
" screenCoordsPassthrough = texcoordProj.xyw; \n "
" if(viewport == 1) \n "
" screenCoordsPassthrough.x += 0.5 * screenCoordsPassthrough.z; \n "
}
} ;
std : : stringstream ssInputDeclarations ;
std : : stringstream ssOutputDeclarations ;
std : : stringstream ssForwardStatements ;
std : : stringstream ssExtraStatements ;
std : : set < std : : string > identifiers ;
for ( auto & declaration : declarations )
{
if ( ! declaration . interpolationType . empty ( ) )
{
ssInputDeclarations < < declaration . interpolationType < < " " ;
ssOutputDeclarations < < declaration . interpolationType < < " " ;
}
ssInputDeclarations < < " in " < < declaration . type < < " " < < declaration . mangledIdentifier < < " [3]; \n " ;
ssOutputDeclarations < < " out " < < declaration . type < < " " < < declaration . identifier < < " ; \n " ;
if ( overriddenForwardStatements . count ( declaration . identifier ) > 0 )
ssForwardStatements < < overriddenForwardStatements [ declaration . identifier ] < < " ; \n " ;
else
ssForwardStatements < < " " < < declaration . identifier < < " = " < < declaration . mangledIdentifier < < " [vertex]; \n " ;
identifiers . insert ( declaration . identifier ) ;
}
// passViewPos output is required
if ( identifiers . find ( " passViewPos " ) = = identifiers . end ( ) )
{
Log ( Debug : : Error ) < < " Vertex shader is missing 'vec3 passViewPos' on its interface. Geometry shader will NOT work. " ;
return " " ;
}
std : : string geometryShader = geometryTemplate ;
geometryShader = std : : regex_replace ( geometryShader , std : : regex ( " @INPUTS " ) , ssInputDeclarations . str ( ) ) ;
geometryShader = std : : regex_replace ( geometryShader , std : : regex ( " @OUTPUTS " ) , ssOutputDeclarations . str ( ) ) ;
geometryShader = std : : regex_replace ( geometryShader , std : : regex ( " @FORWARDING " ) , ssForwardStatements . str ( ) ) ;
return geometryShader ;
}
2020-05-01 17:11:04 +00:00
bool parseFors ( std : : string & source , const std : : string & templateName )
2017-11-10 02:02:27 +00:00
{
const char escapeCharacter = ' $ ' ;
size_t foundPos = 0 ;
while ( ( foundPos = source . find ( escapeCharacter ) ) ! = std : : string : : npos )
{
size_t endPos = source . find_first_of ( " \n \r ()[].;, " , foundPos ) ;
if ( endPos = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
std : : string command = source . substr ( foundPos + 1 , endPos - ( foundPos + 1 ) ) ;
if ( command ! = " foreach " )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unknown shader directive: $ " < < command ;
2017-11-10 02:02:27 +00:00
return false ;
}
size_t iterNameStart = endPos + 1 ;
size_t iterNameEnd = source . find_first_of ( " \n \r ()[].;, " , iterNameStart ) ;
if ( iterNameEnd = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
std : : string iteratorName = " $ " + source . substr ( iterNameStart , iterNameEnd - iterNameStart ) ;
size_t listStart = iterNameEnd + 1 ;
size_t listEnd = source . find_first_of ( " \n \r " , listStart ) ;
if ( listEnd = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
std : : string list = source . substr ( listStart , listEnd - listStart ) ;
std : : vector < std : : string > listElements ;
2017-11-21 17:20:57 +00:00
if ( list ! = " " )
2020-12-09 18:42:15 +00:00
Misc : : StringUtils : : split ( list , listElements , " , " ) ;
2017-11-10 02:02:27 +00:00
size_t contentStart = source . find_first_not_of ( " \n \r " , listEnd ) ;
size_t contentEnd = source . find ( " $endforeach " , contentStart ) ;
if ( contentEnd = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
std : : string content = source . substr ( contentStart , contentEnd - contentStart ) ;
size_t overallEnd = contentEnd + std : : string ( " $endforeach " ) . length ( ) ;
2020-04-23 17:16:32 +00:00
2017-11-17 17:18:33 +00:00
size_t lineDirectivePosition = source . rfind ( " #line " , overallEnd ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) ;
}
else
{
lineDirectivePosition = 0 ;
lineNumber = 2 ;
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + overallEnd , ' \n ' ) ;
2017-11-10 02:02:27 +00:00
std : : string replacement = " " ;
for ( std : : vector < std : : string > : : const_iterator element = listElements . cbegin ( ) ; element ! = listElements . cend ( ) ; element + + )
{
std : : string contentInstance = content ;
size_t foundIterator ;
while ( ( foundIterator = contentInstance . find ( iteratorName ) ) ! = std : : string : : npos )
contentInstance . replace ( foundIterator , iteratorName . length ( ) , * element ) ;
replacement + = contentInstance ;
}
replacement + = " \n #line " + std : : to_string ( lineNumber ) ;
source . replace ( foundPos , overallEnd - foundPos , replacement ) ;
}
return true ;
}
2020-04-23 17:16:32 +00:00
bool parseDefines ( std : : string & source , const ShaderManager : : DefineMap & defines ,
2020-05-01 17:11:04 +00:00
const ShaderManager : : DefineMap & globalDefines , const std : : string & templateName )
2016-02-17 22:29:07 +00:00
{
const char escapeCharacter = ' @ ' ;
size_t foundPos = 0 ;
2017-11-10 02:02:27 +00:00
std : : vector < std : : string > forIterators ;
2016-02-17 22:29:07 +00:00
while ( ( foundPos = source . find ( escapeCharacter ) ) ! = std : : string : : npos )
{
2017-11-10 02:02:27 +00:00
size_t endPos = source . find_first_of ( " \n \r ()[].;, " , foundPos ) ;
2016-02-17 22:29:07 +00:00
if ( endPos = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-12-09 18:42:15 +00:00
std : : string define = source . substr ( foundPos + 1 , endPos - ( foundPos + 1 ) ) ;
2016-02-17 22:29:07 +00:00
ShaderManager : : DefineMap : : const_iterator defineFound = defines . find ( define ) ;
2017-11-22 20:07:07 +00:00
ShaderManager : : DefineMap : : const_iterator globalDefineFound = globalDefines . find ( define ) ;
2017-11-10 02:02:27 +00:00
if ( define = = " foreach " )
{
source . replace ( foundPos , 1 , " $ " ) ;
size_t iterNameStart = endPos + 1 ;
size_t iterNameEnd = source . find_first_of ( " \n \r ()[].;, " , iterNameStart ) ;
if ( iterNameEnd = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
forIterators . push_back ( source . substr ( iterNameStart , iterNameEnd - iterNameStart ) ) ;
}
else if ( define = = " endforeach " )
{
source . replace ( foundPos , 1 , " $ " ) ;
if ( forIterators . empty ( ) )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: endforeach without foreach " ;
2017-11-10 02:02:27 +00:00
return false ;
}
else
forIterators . pop_back ( ) ;
}
else if ( std : : find ( forIterators . begin ( ) , forIterators . end ( ) , define ) ! = forIterators . end ( ) )
{
source . replace ( foundPos , 1 , " $ " ) ;
}
2017-11-22 20:07:07 +00:00
else if ( defineFound ! = defines . end ( ) )
2016-02-17 22:29:07 +00:00
{
2017-11-22 20:07:07 +00:00
source . replace ( foundPos , endPos - foundPos , defineFound - > second ) ;
}
else if ( globalDefineFound ! = globalDefines . end ( ) )
{
source . replace ( foundPos , endPos - foundPos , globalDefineFound - > second ) ;
2016-02-17 22:29:07 +00:00
}
else
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Undefined " < < define ;
2017-11-22 20:07:07 +00:00
return false ;
2016-02-17 22:29:07 +00:00
}
}
return true ;
}
2020-12-09 18:42:15 +00:00
osg : : ref_ptr < osg : : Shader > ShaderManager : : getShader ( const std : : string & templateName , const ShaderManager : : DefineMap & defines , osg : : Shader : : Type shaderType )
2016-02-16 17:18:48 +00:00
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mMutex ) ;
2016-02-16 17:18:48 +00:00
2020-05-01 17:11:04 +00:00
ShaderMap : : iterator shaderIt = mShaders . find ( std : : make_pair ( templateName , defines ) ) ;
2016-02-16 17:18:48 +00:00
if ( shaderIt = = mShaders . end ( ) )
{
2020-12-09 18:42:15 +00:00
std : : string shaderSource = getTemplateSource ( templateName ) ;
2020-05-01 17:11:04 +00:00
if ( ! parseDefines ( shaderSource , defines , mGlobalDefines , templateName ) | | ! parseFors ( shaderSource , templateName ) )
2017-11-07 22:54:31 +00:00
{
// Add to the cache anyway to avoid logging the same error over and over.
2020-05-01 17:11:04 +00:00
mShaders . insert ( std : : make_pair ( std : : make_pair ( templateName , defines ) , nullptr ) ) ;
2018-10-09 06:21:12 +00:00
return nullptr ;
2017-11-07 22:54:31 +00:00
}
2016-02-16 17:18:48 +00:00
2020-12-09 18:42:15 +00:00
osg : : ref_ptr < osg : : Shader > shader ( new osg : : Shader ( shaderType ) ) ;
2016-02-16 17:18:48 +00:00
// Assign a unique name to allow the SharedStateManager to compare shaders efficiently
static unsigned int counter = 0 ;
2017-05-06 21:05:13 +00:00
shader - > setName ( std : : to_string ( counter + + ) ) ;
2016-02-16 17:18:48 +00:00
2020-12-09 18:42:15 +00:00
if ( mGeometryShadersEnabled & & defines . count ( " geometryShader " ) & & defines . find ( " geometryShader " ) - > second = = " 1 " & & shaderType = = osg : : Shader : : VERTEX )
{
std : : vector < DeclarationMeta > declarations ;
mangleInterface ( shaderSource , " out|varying " , " vertex_ " , declarations ) ;
std : : string geometryTemplate = getTemplateSource ( " stereo_geometry.glsl " ) ;
std : : string geometryShaderSource = generateGeometryShader ( geometryTemplate , declarations ) ;
if ( ! geometryShaderSource . empty ( ) )
{
osg : : ref_ptr < osg : : Shader > geometryShader ( new osg : : Shader ( osg : : Shader : : GEOMETRY ) ) ;
geometryShader - > setShaderSource ( geometryShaderSource ) ;
geometryShader - > setName ( shader - > getName ( ) + " .geom " ) ;
mGeometryShaders [ shader ] = geometryShader ;
}
else
{
Log ( Debug : : Error ) < < " Failed to generate geometry shader for " < < templateName ;
}
}
shader - > setShaderSource ( shaderSource ) ;
2020-05-01 17:11:04 +00:00
shaderIt = mShaders . insert ( std : : make_pair ( std : : make_pair ( templateName , defines ) , shader ) ) . first ;
2016-02-16 17:18:48 +00:00
}
return shaderIt - > second ;
}
2016-02-17 14:04:05 +00:00
osg : : ref_ptr < osg : : Program > ShaderManager : : getProgram ( osg : : ref_ptr < osg : : Shader > vertexShader , osg : : ref_ptr < osg : : Shader > fragmentShader )
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mMutex ) ;
2016-02-17 14:04:05 +00:00
ProgramMap : : iterator found = mPrograms . find ( std : : make_pair ( vertexShader , fragmentShader ) ) ;
if ( found = = mPrograms . end ( ) )
{
2020-12-09 18:42:15 +00:00
osg : : ref_ptr < osg : : Program > program ( new osg : : Program ) ;
2016-02-17 14:04:05 +00:00
program - > addShader ( vertexShader ) ;
program - > addShader ( fragmentShader ) ;
2020-01-12 07:42:47 +00:00
program - > addBindAttribLocation ( " aOffset " , 6 ) ;
program - > addBindAttribLocation ( " aRotation " , 7 ) ;
2020-12-09 18:42:15 +00:00
auto git = mGeometryShaders . find ( vertexShader ) ;
if ( git ! = mGeometryShaders . end ( ) )
{
program - > addShader ( git - > second ) ;
}
2021-03-15 04:42:34 +00:00
if ( mLightingMethod = = SceneUtil : : LightingMethod : : SingleUBO )
program - > addBindUniformBlock ( " LightBufferBinding " , static_cast < int > ( UBOBinding : : LightBuffer ) ) ;
2016-02-17 14:04:05 +00:00
found = mPrograms . insert ( std : : make_pair ( std : : make_pair ( vertexShader , fragmentShader ) , program ) ) . first ;
}
return found - > second ;
}
2017-11-22 20:07:07 +00:00
ShaderManager : : DefineMap ShaderManager : : getGlobalDefines ( )
{
return DefineMap ( mGlobalDefines ) ;
}
2020-12-09 18:42:15 +00:00
void ShaderManager : : setStereoGeometryShaderEnabled ( bool enabled )
{
mGeometryShadersEnabled = enabled ;
}
bool ShaderManager : : stereoGeometryShaderEnabled ( ) const
{
return mGeometryShadersEnabled ;
}
void ShaderManager : : setGlobalDefines ( DefineMap & globalDefines )
2017-11-22 20:07:07 +00:00
{
2017-12-17 23:55:19 +00:00
mGlobalDefines = globalDefines ;
2020-12-09 18:42:15 +00:00
for ( auto shaderMapElement : mShaders )
2017-12-17 01:57:53 +00:00
{
std : : string templateId = shaderMapElement . first . first ;
ShaderManager : : DefineMap defines = shaderMapElement . first . second ;
osg : : ref_ptr < osg : : Shader > shader = shaderMapElement . second ;
if ( shader = = nullptr )
// I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it.
continue ;
std : : string shaderSource = mShaderTemplates [ templateId ] ;
2020-04-23 17:16:32 +00:00
if ( ! parseDefines ( shaderSource , defines , mGlobalDefines , templateId ) | | ! parseFors ( shaderSource , templateId ) )
2017-12-17 01:57:53 +00:00
// We just broke the shader and there's no way to force existing objects back to fixed-function mode as we would when creating the shader.
// If we put a nullptr in the shader map, we just lose the ability to put a working one in later.
continue ;
shader - > setShaderSource ( shaderSource ) ;
}
2017-11-22 20:07:07 +00:00
}
2020-12-09 18:42:15 +00:00
void ShaderManager : : releaseGLObjects ( osg : : State * state )
2017-08-26 19:28:23 +00:00
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mMutex ) ;
2017-08-26 19:28:23 +00:00
for ( auto shader : mShaders )
2017-11-24 22:43:40 +00:00
{
if ( shader . second ! = nullptr )
shader . second - > releaseGLObjects ( state ) ;
}
2017-08-26 19:28:23 +00:00
for ( auto program : mPrograms )
program . second - > releaseGLObjects ( state ) ;
}
2020-12-09 18:42:15 +00:00
std : : string ShaderManager : : getTemplateSource ( const std : : string & templateName )
{
// read the template if we haven't already
TemplateMap : : iterator templateIt = mShaderTemplates . find ( templateName ) ;
if ( templateIt = = mShaderTemplates . end ( ) )
{
boost : : filesystem : : path path = ( boost : : filesystem : : path ( mPath ) / templateName ) ;
boost : : filesystem : : ifstream stream ;
stream . open ( path ) ;
if ( stream . fail ( ) )
{
Log ( Debug : : Error ) < < " Failed to open " < < path . string ( ) ;
return std : : string ( ) ;
}
std : : stringstream buffer ;
buffer < < stream . rdbuf ( ) ;
// parse includes
int fileNumber = 1 ;
std : : string source = buffer . str ( ) ;
if ( ! addLineDirectivesAfterConditionalBlocks ( source )
| | ! parseIncludes ( boost : : filesystem : : path ( mPath ) , source , templateName , fileNumber , { } ) )
return std : : string ( ) ;
templateIt = mShaderTemplates . insert ( std : : make_pair ( templateName , source ) ) . first ;
}
return templateIt - > second ;
}
2016-02-16 17:18:48 +00:00
}