#include "shadermanager.hpp" #include #include #include #include #include #include #include #include namespace Shader { void ShaderManager::setShaderPath(const std::string &path) { mPath = path; } bool parseIncludes(boost::filesystem::path shaderPath, std::string& source) { boost::replace_all(source, "\r\n", "\n"); std::set includedFiles; size_t foundPos = 0; int fileNumber = 1; while ((foundPos = source.find("#include")) != std::string::npos) { size_t start = source.find('"', foundPos); if (start == std::string::npos || start == source.size()-1) { std::cerr << "Invalid #include " << std::endl; return false; } size_t end = source.find('"', start+1); if (end == std::string::npos) { std::cerr << "Invalid #include " << std::endl; return false; } std::string includeFilename = source.substr(start+1, end-(start+1)); boost::filesystem::path includePath = shaderPath / includeFilename; boost::filesystem::ifstream includeFstream; includeFstream.open(includePath); if (includeFstream.fail()) { std::cerr << "Failed to open " << includePath.string() << std::endl; return false; } std::stringstream buffer; buffer << includeFstream.rdbuf(); // insert #line directives so we get correct line numbers in compiler errors int includedFileNumber = fileNumber++; int lineNumber = std::count(source.begin(), source.begin() + foundPos, '\n'); std::stringstream toInsert; toInsert << "#line 0 " << includedFileNumber << "\n" << buffer.str() << "\n#line " << lineNumber << " 0\n"; source.replace(foundPos, (end-foundPos+1), toInsert.str()); if (includedFiles.insert(includePath).second == false) { std::cerr << "Detected cyclic #includes" << std::endl; return false; } } return true; } bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines) { 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) { std::cerr << "Unexpected EOF" << std::endl; return false; } std::string define = source.substr(foundPos+1, endPos - (foundPos+1)); ShaderManager::DefineMap::const_iterator defineFound = defines.find(define); if (defineFound == defines.end()) { std::cerr << "Undefined " << define << std::endl; return false; } else { source.replace(foundPos, endPos-foundPos, defineFound->second); } } return true; } osg::ref_ptr ShaderManager::getShader(const std::string &shaderTemplate, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType, bool disableShadows) { OpenThreads::ScopedLock lock(mMutex); // set up shadows in the shader // get these values from settings manager bool shadows = true & !disableShadows; int numShadowMaps = 2; DefineMap definesWithShadows; if (shadows) { definesWithShadows.insert(std::make_pair(std::string("shadows_enabled"), std::string("1"))); /*definesWithShadows.insert(std::string("shadow_texture_unit_declarations"), std::string("")); definesWithShadows.insert(std::string("shadow_space_coordinate_declarations"), std::string("")); definesWithShadows.insert(std::string("shadow_space_coordinate_calculations"), std::string("")); definesWithShadows.insert(std::string("shadow_texture_sampler_declarations"), std::string("")); definesWithShadows.insert(std::string("shadow_texture_lookup_calculations"), std::string(""));*/ for (int i = 0; i < numShadowMaps; ++i) { definesWithShadows["shadow_texture_unit_declarations"] += "uniform int shadowTextureUnit" + std::to_string(i) + ";\n"; definesWithShadows["shadow_space_coordinate_declarations"] += "varying vec4 shadowSpaceCoords" + std::to_string(i) + ";\n"; definesWithShadows["shadow_space_coordinate_calculations"] += "eyePlaneMat = mat4(gl_EyePlaneS[shadowTextureUnit" + std::to_string(i) + "], gl_EyePlaneT[shadowTextureUnit" + std::to_string(i) + "], gl_EyePlaneR[shadowTextureUnit" + std::to_string(i) + "], gl_EyePlaneQ[shadowTextureUnit" + std::to_string(i) + "]);\n" + "shadowSpaceCoords" + std::to_string(i) + " = viewPos * eyePlaneMat;\n"; definesWithShadows["shadow_texture_sampler_declarations"] += "uniform sampler2DShadow shadowTexture" + std::to_string(i) + ";\n"; definesWithShadows["shadow_texture_lookup_calculations"] += "shadowing *= shadow2DProj(shadowTexture" + std::to_string(i) + ", shadowSpaceCoords" + std::to_string(i) + ").r;\n"; } } definesWithShadows.insert(defines.begin(), defines.end()); // read the template if we haven't already TemplateMap::iterator templateIt = mShaderTemplates.find(shaderTemplate); if (templateIt == mShaderTemplates.end()) { boost::filesystem::path p = (boost::filesystem::path(mPath) / shaderTemplate); boost::filesystem::ifstream stream; stream.open(p); if (stream.fail()) { std::cerr << "Failed to open " << p.string() << std::endl; return NULL; } std::stringstream buffer; buffer << stream.rdbuf(); // parse includes std::string source = buffer.str(); if (!parseIncludes(boost::filesystem::path(mPath), source)) return NULL; templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; } ShaderMap::iterator shaderIt = mShaders.find(std::make_pair(shaderTemplate, definesWithShadows)); if (shaderIt == mShaders.end()) { std::string shaderSource = templateIt->second; if (!parseDefines(shaderSource, definesWithShadows)) return NULL; osg::ref_ptr shader (new osg::Shader(shaderType)); shader->setShaderSource(shaderSource); // Assign a unique name to allow the SharedStateManager to compare shaders efficiently static unsigned int counter = 0; shader->setName(std::to_string(counter++)); shaderIt = mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, definesWithShadows), shader)).first; } return shaderIt->second; } osg::ref_ptr ShaderManager::getProgram(osg::ref_ptr vertexShader, osg::ref_ptr fragmentShader) { OpenThreads::ScopedLock lock(mMutex); ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader)); if (found == mPrograms.end()) { osg::ref_ptr program (new osg::Program); program->addShader(vertexShader); program->addShader(fragmentShader); found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first; } return found->second; } void ShaderManager::releaseGLObjects(osg::State *state) { OpenThreads::ScopedLock lock(mMutex); for (auto shader : mShaders) shader.second->releaseGLObjects(state); for (auto program : mPrograms) program.second->releaseGLObjects(state); } }