/*********************************************************************** * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. * * All rights reserved. * * Distributed under the terms of the MIT License (see LICENSE.TXT). * **********************************************************************/ #include #include #include #include #include #include using namespace ACGL::Base; using namespace ACGL::Utils; using namespace ACGL::OpenGL; bool Shader::setFromFileNoImportParsing(const std::string& _filename) { std::string line = ""; std::string fileContent = ""; std::ifstream fileStream(_filename.c_str(), std::ifstream::in); if(fileStream.is_open()) { while (fileStream.good()) { std::getline(fileStream,line); fileContent += line + "\n"; } fileStream.close(); } else { error() << "Failed to open file: " << _filename << std::endl; return false; } bool compileErrors = true; if ( setSource(fileContent, false) ) { // don't check for errors, we will do that on our own: std::string compileLog; getCompileLog( compileLog, compileErrors ); if (compileLog.size() > 0) { if (compileErrors) { error() << "\nIn file: " << _filename << ": \n" << compileLog << "\n" << std::endl; } else { warning() << "\nIn file: " << _filename << ": \n" << compileLog << "\n" << std::endl; } } } return !compileErrors; // return true iff there were no errors } bool Shader::setFromFile(const std::string& _filename) { ShaderParser sp( _filename ); bool compileErrors = true; if ( setSources( sp.getSources(), false) ) { // don't check for errors, we will do that on our own: std::string compileLog; getCompileLog( compileLog, compileErrors ); if (compileLog.size() > 0) { if (compileErrors) { error() << "\nIn files: \n" << sp.getFileNamesPrintable() << compileLog << "\n" << std::endl; } else { warning() << "\nIn files: \n" << sp.getFileNamesPrintable() << compileLog << "\n" << std::endl; } } } return !compileErrors; // return true iff there were no errors } bool Shader::setSource(const std::string& _source, bool _checkForCompileErrors) { const char *pProgramString = _source.c_str(); glShaderSource(mObjectName, 1, &pProgramString, NULL); // can't create OpenGL errors if (!compile()) { return false; } // the compile call should work even if there are compile errors itself: bool compileErrors = false; if (_checkForCompileErrors) { std::string compileLog; getCompileLog( compileLog, compileErrors ); if (compileLog.size() > 0) { if (compileErrors) { error() << compileLog << std::endl; } else { warning() << compileLog << std::endl; } } } return !compileErrors; // return true iff there were NO errors } bool Shader::setSources(const std::vector &_sources, bool _checkForCompileErrors ) { unsigned int numberOfStrings = _sources.size(); const char **pProgramStrings = new const char*[ numberOfStrings ]; for (unsigned int i = 0; i < numberOfStrings; ++i) { pProgramStrings[i] = _sources[i].c_str(); } glShaderSource(mObjectName, numberOfStrings, pProgramStrings, NULL); // can't create OpenGL errors delete[] pProgramStrings; if (!compile()) { return false; } // the compile call should work even if there are compile errors itself: bool compileErrors = false; if (_checkForCompileErrors) { std::string compileLog; getCompileLog( compileLog, compileErrors ); if (compileLog.size() > 0) { if (compileErrors) { error() << compileLog << std::endl; } else { warning() << compileLog << std::endl; } } } return !compileErrors; // return true iff there were NO errors } bool Shader::compile() const { glCompileShader(mObjectName); #ifdef ACGL_CHECK_CRITICAL_GL_ERRORS // problems with the shader creation are always a bad thing... // from here on only error checks are performed: // if ( openGLRareErrorOccured() ) { error() << "glCompileShader failed, that can only mean, that the object name used is not a valid shader object!" << std::endl; return false; } #endif return true; } void Shader::getCompileLog( std::string &_log, bool &_wasErrorLog ) const { // check for shader compile errors: GLint shaderError; glGetShaderiv(mObjectName, GL_COMPILE_STATUS, &shaderError); if(shaderError != GL_TRUE) { // yes, errors _wasErrorLog = true; } else { _wasErrorLog = false; } // the log could be warnings: GLsizei length = 0; glGetShaderiv(mObjectName, GL_INFO_LOG_LENGTH, &length); if(length > 1) // null terminated, so 1 could also be empty { // a compile log can get produced even if there were no errors, but warnings! GLchar* pInfo = new char[length]; glGetShaderInfoLog(mObjectName, length, &length, pInfo); //error() << "Compile log: " << std::string(pInfo) << std::endl; _log = std::string( pInfo ); delete[] pInfo; } else { _log = ""; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ShaderParser::ShaderParser( const std::string &_filename ) { mMaxVersion = 110; mSources.push_back( "#version 330\n" ); std::string path, file; StringOperations::splitLastFileOrFolder( _filename, path, file ); //readin( "./"+path+"/"+file ); readin( _filename ); if (mMaxVersion > 110) { mSources[0] = "#version "+StringOperations::toString(mMaxVersion)+"\n"; } } bool ShaderParser::lineContainsVersion( const std::string &line, unsigned int &version) { std::vector< std::string > token = StringOperations::split( line, ' ' ); if (token.size() >= 2 && token[0] == "#version") { version = StringOperations::to(token[1]); //debug() << "string " << line << " -> version " << version << std::endl; return true; } return false; } bool ShaderParser::lineContainsImport( const std::string &line, std::string &filename) { std::vector< std::string > token = StringOperations::split( line, ' ' ); if (token.size() >= 3 && token[0] == "#pragma" && token[1] == "ACGLimport") { if (token[2].size() >= 2) { size_t startName = line.find('"',0); if (startName == std::string::npos) return false; size_t endName = line.find('"',startName+1); if (endName == std::string::npos) return false; filename = line.substr(startName+1, endName-startName-1); // remove the "" //debug() << "string " << line << " -> import " << filename << std::endl; return true; } } return false; } std::string ShaderParser::getFileNamesPrintable() { std::string rString; for (size_t i = 0; i < mSourceFileNames.size(); ++i) { rString += " " + StringOperations::toString(i+1) + " " + mSourceFileNames[i] + "\n"; } return rString; } void ShaderParser::readin( const std::string &_filename ) { std::string line = ""; std::ifstream fileStream(_filename.c_str(), std::ifstream::in); unsigned int lineNumber = 1; mSourceFileNames.push_back( _filename ); unsigned int fileNumber = mSourceFileNames.size(); // define the file and line number to get correct errors from the shader compiler: std::string fileContent = "#line "+StringOperations::toString(lineNumber)+" "+StringOperations::toString(fileNumber)+"\n"; //debug() << "parse file " << _filename << std::endl; if(fileStream.is_open()) { while (fileStream.good()) { std::getline(fileStream,line); unsigned int version; std::string fileToImport; if ( lineContainsVersion(line, version) ) { mMaxVersion = std::max( version, mMaxVersion ); fileContent += "\n"; // remove the #version but keep a newline lineNumber++; } else if ( lineContainsImport(line, fileToImport ) ) { // handle import if (fileToImport[0] != '/') { // it's a relative path: // remove "./" in case this was given explicitly: if(fileToImport.size() > 2 && fileToImport[0] == '.' && fileToImport[1] == '/') { fileToImport = fileToImport.substr(2, fileToImport.size()-2); } std::string path, file; StringOperations::splitLastFileOrFolder( _filename, path, file ); fileToImport = path+"/"+fileToImport; } if ( std::find(mSourceFileNames.begin(), mSourceFileNames.end(), fileToImport) == mSourceFileNames.end()) { // not imported yet: // push what we have till now: mSources.push_back( fileContent ); readin( fileToImport ); lineNumber++; fileContent = "#line "+StringOperations::toString(lineNumber)+" "+StringOperations::toString(fileNumber)+"\n"; } else { // ignore: lineNumber++; fileContent += "\n"; } } else { fileContent += line + "\n"; lineNumber++; } } fileStream.close(); } else { error() << "Failed to open file: " << _filename << std::endl; return; } //debug() << "file: " << fileContent << std::endl; mSources.push_back( fileContent ); }