diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 9c0e70d48..286642e57 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -19,6 +19,73 @@ namespace Shader mPath = path; } + bool parseIncludes(boost::filesystem::path shaderPath, std::string& source) + { + std::set includedFiles; + size_t foundPos = 0; + 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(); + source.replace(foundPos, (end-foundPos+1), buffer.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) { OpenThreads::ScopedLock lock(mMutex); @@ -38,36 +105,20 @@ namespace Shader std::stringstream buffer; buffer << stream.rdbuf(); - templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, buffer.str())).first; + // 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, defines)); if (shaderIt == mShaders.end()) { std::string shaderSource = templateIt->second; - - const char escapeCharacter = '@'; - size_t foundPos = 0; - while ((foundPos = shaderSource.find(escapeCharacter)) != std::string::npos) - { - size_t endPos = shaderSource.find_first_of(" \n\r()[].;", foundPos); - if (endPos == std::string::npos) - { - std::cerr << "Unexpected EOF" << std::endl; - return NULL; - } - std::string define = shaderSource.substr(foundPos+1, endPos - (foundPos+1)); - DefineMap::const_iterator defineFound = defines.find(define); - if (defineFound == defines.end()) - { - std::cerr << "Undefined " << define << " in shader " << shaderTemplate << std::endl; - return NULL; - } - else - { - shaderSource.replace(foundPos, endPos-foundPos, defineFound->second); - } - } + if (!parseDefines(shaderSource, defines)) + return NULL; osg::ref_ptr shader (new osg::Shader(shaderType)); shader->setShaderSource(shaderSource);