/**********************************************************************
* *
* cgt - CAMP Graphics Toolbox, Copyright (C) 2012-2014 *
* Chair for Computer Aided Medical Procedures *
* Technische Universitaet Muenchen, Germany. *
* *
* *
* forked from tgt - Tiny Graphics Toolbox, Copyright (C) 2006-2011 *
* Visualization and Computer Graphics Group, Department of *
* Computer Science, University of Muenster, Germany. *
* *
* *
* This file is part of the cgt library. This library is free *
* software; you can redistribute it and/or modify it under the terms *
* of the GNU Lesser General Public License version 2.1 as published *
* by the Free Software Foundation. *
* *
* This library 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 in the file "LICENSE.txt" along with this library. *
* If not, see . *
* *
**********************************************************************/
#include "cgt/shadermanager.h"
#include "cgt/gpucapabilities.h"
#include
#include
using std::string;
namespace cgt {
//------------------------------------------------------------------------------
namespace {
/**
* Resolve the line number to take into account the include directives.
* Returns a string containing file name and line number in that file.
*/
std::string resolveLineNumber(int number, const std::vector& lineTracker) {
int found = static_cast(lineTracker.size()) - 1;
for (int i = static_cast(lineTracker.size()) - 1; i >= 0 ; i--) {
if (lineTracker[i].lineNumber_ <= number) {
found = i;
break;
}
}
std::ostringstream result;
//if (found >= 0) { //found is unsigned...
result << FileSystem::fileName(lineTracker[found].filename_) << ":"
<< (number - lineTracker[found].lineNumber_ + lineTracker[found].sourceLineNumber_);
//}
return result.str();
}
} // namespace
const std::string ShaderPreprocessor::loggerCat_("cgt.Shader.ShaderPreprocessor");
ShaderPreprocessor::ShaderPreprocessor(ShaderObject* obj, Mode /*mode*/)
: shd_(obj), lineTracker_(obj->lineTracker_), inputType_(0), outputType_(0), verticesOut_(0)
{
parse();
}
void ShaderPreprocessor::parsePart(const std::string& input, const std::string& name) {
std::istringstream source(input);
int locallinenumber = 0;
lineTracker_.push_back(ShaderObject::LineInfo(activeLine_, name, 1));
string line;
while (std::getline(source, line)) {
locallinenumber++;
string::size_type pos = line.find("#include");
// At least partly support for comments.
// "/*"-style comments starting on previous lines are not detected.
string::size_type comment1 = line.find("//");
string::size_type comment2 = line.find("/*");
bool inComment = (comment1 != string::npos && comment1 < pos) || (comment2 != string::npos && comment2 < pos);
if (pos != string::npos && !inComment) {
pos = line.find("\"", pos + 1);
string::size_type end = line.find("\"", pos + 1);
string filename(line, pos + 1, end - pos - 1);
filename = ShdrMgr.completePath(filename);
if (includedFilenames_.find(filename) == includedFilenames_.end()) {
File* file = FileSys.open(filename);
string content;
if ((!file) || (!file->isOpen())) {
LERROR("Cannot open shader include '" << filename << "' in " << resolveLineNumber(activeLine_, lineTracker_));
}
else {
size_t len = file->size();
// check if file is empty
if (len == 0)
content = "";
else
content = file->getAsString();
file->close();
outputComment("BEGIN INCLUDE " + filename, "BEGIN");
if (!content.empty() && content[content.size() - 1] != '\n')
content += "\n";
parsePart(content, filename);
outputComment("END INCLUDE " + filename, "END");
lineTracker_.push_back(ShaderObject::LineInfo(activeLine_, name, locallinenumber + 1));
}
delete file;
includedFilenames_.insert(filename);
}
} else {
result_ << line << "\n";
activeLine_++;
}
}
}
void ShaderPreprocessor::parse() {
activeLine_ = 1;
lineTracker_.clear();
result_.clear();
includedFilenames_.clear();
if (! shd_->customGlslVersion_.empty()) {
parsePart("#version " + shd_->customGlslVersion_ + "\n", "Custom per-shader version");
}
else {
if (! ShdrMgr.getDefaultGlslVersion().empty()) {
parsePart("#version " + ShdrMgr.getDefaultGlslVersion() + "\n", "Global version");
}
}
if (! ShdrMgr.getGlobalHeader().empty()) {
outputComment("BEGIN GLOBAL HEADER");
parsePart(ShdrMgr.getGlobalHeader(), "GLOBAL HEADER");
outputComment("END GLOBAL HEADER");
}
if (!shd_->header_.empty()) {
outputComment("BEGIN HEADER");
parsePart(shd_->header_, "HEADER");
outputComment("END HEADER");
}
parsePart(shd_->unparsedSource_, shd_->filename_);
}
void ShaderPreprocessor::outputComment(const std::string& comment, const std::string& type) {
result_ << "// " << comment << "\n";
lineTracker_.push_back(ShaderObject::LineInfo(activeLine_, type, 0));
activeLine_++;
}
bool ShaderPreprocessor::scanGeomShaderDirectives() {
LDEBUG("Scanning for geometry shader compile directives...");
std::string input = getGeomShaderDirective("GL_GEOMETRY_INPUT_TYPE_EXT");
if (input == "GL_POINTS")
inputType_ = GL_POINTS;
else if (input == "GL_LINES")
inputType_ = GL_LINES;
else if (input == "GL_LINES_ADJACENCY_EXT")
inputType_ = GL_LINES_ADJACENCY_EXT;
else if (input == "GL_TRIANGLES")
inputType_ = GL_TRIANGLES;
else if (input == "GL_TRIANGLES_ADJACENCY_EXT")
inputType_ = GL_TRIANGLES_ADJACENCY_EXT;
else {
LERROR("Unknown input type: " << input);
return false;
};
std::string output = getGeomShaderDirective("GL_GEOMETRY_OUTPUT_TYPE_EXT");
if (output == "GL_POINTS")
outputType_ = GL_POINTS;
else if (output == "GL_LINE_STRIP")
outputType_ = GL_LINE_STRIP;
else if (output == "GL_TRIANGLE_STRIP")
outputType_ = GL_TRIANGLE_STRIP;
else {
LERROR("Unknown output type: " << output);
return false;
};
std::string verticesOut = getGeomShaderDirective("GL_GEOMETRY_VERTICES_OUT_EXT");
std::istringstream myStream(verticesOut);
if (myStream >> verticesOut_) {
LDEBUG("VERTICES_OUT: " << verticesOut_);
}
else {
LERROR("Failed to parse argument(" << verticesOut << ") as integer for directive GL_GEOMETRY_VERTICES_OUT_EXT.");
return false;
}
return true;
}
std::string ShaderPreprocessor::getGeomShaderDirective(const std::string& d) {
string sourceStr(shd_->source_);
string::size_type curPos = sourceStr.find(d + "(", 0);
string::size_type length = d.length() + 1;
if (curPos != string::npos) {
string::size_type endPos = sourceStr.find(")", curPos);
if (endPos != string::npos) {
std::string ret = sourceStr.substr(curPos + length, endPos - curPos - length);
// test for space, newline:
if ((ret.find(" ", 0) == string::npos) && (ret.find("\n", 0) == string::npos) ) {
LINFO("Directive " << d << ": " << ret);
return ret;
}
else {
LERROR("No spaces/newlines allowed inbetween directive brackets! Directive: " << d);
return "";
}
}
LERROR("Missing ending bracket for directive " << d);
return "";
}
else {
LWARNING("Could not locate directive " << d << "!");
return "";
}
}
std::string ShaderPreprocessor::getResult() const {
return result_.str();
}
GLint ShaderPreprocessor::getGeomShaderOutputType() const {
return outputType_;
}
GLint ShaderPreprocessor::getGeomShaderInputType() const {
return inputType_;
}
GLint ShaderPreprocessor::getGeomShaderVerticesOut() const {
return verticesOut_;
}
//------------------------------------------------------------------------------
const string ShaderObject::loggerCat_("cgt.Shader.ShaderObject");
ShaderObject::ShaderObject(const string& filename, ShaderType type)
: filename_(filename)
, shaderType_(type)
, isCompiled_(false)
, inputType_(GL_TRIANGLES)
, outputType_(GL_TRIANGLE_STRIP)
, verticesOut_(16)
{
id_ = glCreateShader(shaderType_);
if (id_ == 0)
LERROR("ShaderObject(" + filename + ")::glCreateShader() returned 0");
}
ShaderObject::~ShaderObject() {
glDeleteShader(id_);
}
void ShaderObject::loadSourceFromFile(const string& filename) throw (Exception) {
//LDEBUG("Loading " << filename);
File* file = FileSys.open(filename);
// check if file is open
if (!file || !file->isOpen()) {
LDEBUG("File not found: " << filename);
delete file;
throw FileNotFoundException("", filename);
}
filename_ = filename;
unparsedSource_ = file->getAsString();
source_ = unparsedSource_;
file->close();
delete file;
}
void ShaderObject::uploadSource() {
const GLchar* s = source_.c_str();
glShaderSource(id_, 1, &s, 0);
}
bool ShaderObject::compileShader() {
isCompiled_ = false;
ShaderPreprocessor p(this);
source_ = p.getResult();
if (shaderType_ == GEOMETRY_SHADER) {
if (p.getGeomShaderInputType())
inputType_ = p.getGeomShaderInputType();
if (p.getGeomShaderOutputType())
outputType_ = p.getGeomShaderOutputType();
if (p.getGeomShaderVerticesOut())
verticesOut_ = p.getGeomShaderVerticesOut();
}
uploadSource();
glCompileShader(id_);
GLint check = 0;
glGetShaderiv(id_, GL_COMPILE_STATUS, &check);
isCompiled_ = (check == GL_TRUE);
return isCompiled_;
}
namespace {
int parseLogLineNumberNVIDIA(const std::string& message) {
// Errors look like this:
// 0(1397) : error C0000: syntax error, unexpected '=' at token "="
std::istringstream ls(message);
int id; // first number (probably used when multiple sources are bound), ignored
if (ls >> id) {
char c = 0; // should be an opening parenthesis
if (ls >> c &&c == '(') {
int num; // line number
if (ls >> num)
return num;
}
}
return 0;
}
int parseLogLineNumberATI(const std::string& message) {
// Errors look like this:
// ERROR: 0:2785: 'frontPos' : undeclared identifier
std::istringstream ls(message);
std::string s;
if (ls >> s && s == "ERROR:") {
int id; // first number (probably used when multiple sources are bound), ignored
if (ls >> id) {
char c = 0; // should be a colon
if (ls >> c && c == ':') {
int num; // line number
if (ls >> num)
return num;
}
}
}
return 0;
}
/**
* Tries to extract the line number for a given compile messages.
* Returns 0 if no line number was found.
*/
int parseLogLineNumber(const std::string& message) {
int n = 0;
n = parseLogLineNumberNVIDIA(message);
if (n > 0)
return n;
n = parseLogLineNumberATI(message);
if (n > 0)
return n;
return 0;
}
} // namespace
string ShaderObject::getCompilerLog() const {
GLint len;
glGetShaderiv(id_, GL_INFO_LOG_LENGTH , &len);
if (len > 1) {
GLchar* log = new GLchar[len];
if (log == 0)
return "Memory allocation for log failed!";
GLsizei l; // length returned
glGetShaderInfoLog(id_, len, &l, log);
std::istringstream str(log);
delete[] log;
std::ostringstream result;
string line;
while (getline(str, line)) {
result << line;
int num = parseLogLineNumber(line);
if (num > 0)
result << " [" << resolveLineNumber(num, lineTracker_) << "]";
result << '\n';
}
return result.str();
} else {
return "";
}
}
void ShaderObject::setHeader(const string& h) {
header_ = h;
}
bool ShaderObject::rebuildFromFile() {
try {
loadSourceFromFile(filename_);
}
catch (const Exception& e) {
LWARNING("Failed to load shader " << filename_ << ": " << e.what());
return false;
}
uploadSource();
if (!compileShader()) {
LERROR("Failed to compile shader object " << filename_);
LERROR("Compiler Log: \n" << getCompilerLog());
return false;
}
return true;
}
//------------------------------------------------------------------------------
const string Shader::loggerCat_("cgt.Shader.Shader");
Shader::Shader()
: id_(0)
, isLinked_(false)
, ignoreError_(false)
{
id_ = glCreateProgram();
if (id_ == 0)
LERROR("Shader(): glCreateProgram() returned 0");
}
Shader::~Shader() {
for (ShaderObjects::iterator iter = objects_.begin(); iter != objects_.end(); ++iter) {
glDetachShader(id_, (*iter)->id_);
delete (*iter);
}
glDeleteProgram(id_);
}
void Shader::attachObject(ShaderObject* obj) {
glAttachShader(id_, obj->id_);
objects_.push_back(obj);
}
void Shader::detachObject(ShaderObject* obj) {
glDetachShader(id_, obj->id_);
objects_.remove(obj);
isLinked_ = false;
}
void Shader::activate() {
if (isLinked_)
activate(id_);
}
void Shader::activate(GLint id) {
#ifdef CGT_DEBUG
if (cgt::getGlInt(GL_CURRENT_PROGRAM) != 0)
LWARNING("Binding a new Shader while another Shader is active. Do you really want to do this?");
#endif
glUseProgram(id);
}
void Shader::deactivate() {
glUseProgram(0);
}
GLint Shader::getCurrentProgram() {
GLint id;
glGetIntegerv(GL_CURRENT_PROGRAM, &id);
return id;
}
bool Shader::isActivated() {
GLint shader_nr;
glGetIntegerv(GL_CURRENT_PROGRAM, &shader_nr);
return (id_ == static_cast(shader_nr));
}
void Shader::detachObjectsByType(ShaderObject::ShaderType type) {
for (ShaderObjects::iterator iter = objects_.begin(); iter != objects_.end(); ++iter) {
if ((*iter)->getType() == type)
detachObject(*iter);
delete (*iter);
}
isLinked_ = false;
}
bool Shader::linkProgram() {
isLinked_ = false;
glLinkProgram(id_);
GLint check = 0;
glGetProgramiv(id_, GL_LINK_STATUS, &check);
if (check)
isLinked_ = true;
return isLinked_;
}
string Shader::getLinkerLog() const {
GLint len;
glGetProgramiv(id_, GL_INFO_LOG_LENGTH , &len);
if (len > 1) {
GLchar* log = new GLchar[len];
if (log == 0)
return "Memory allocation for log failed!";
GLsizei l; // length returned
glGetProgramInfoLog(id_, len, &l, log);
string retStr(log);
delete[] log;
return retStr;
}
return "";
}
bool Shader::rebuild() {
if (isLinked_) {
// program is already linked: detach and re-attach everything
for (ShaderObjects::iterator iter = objects_.begin(); iter != objects_.end(); ++iter) {
glDetachShader(id_, (*iter)->id_);
(*iter)->uploadSource();
if (!(*iter)->compileShader()) {
LERROR("Failed to compile shader object.");
LERROR("Compiler Log: \n" << (*iter)->getCompilerLog());
return false;
}
glAttachShader(id_, (*iter)->id_);
}
}
isLinked_ = false;
glLinkProgram(id_);
GLint check = 0;
glGetProgramiv(id_, GL_LINK_STATUS, &check);
if (check) {
isLinked_ = true;
return true;
} else {
LERROR("Shader::rebuild(): Failed to link shader." );
LERROR("Linker Log: \n" << getLinkerLog());
return false;
}
}
bool Shader::rebuildFromFile() {
bool result = true;
for (ShaderObjects::iterator iter = objects_.begin(); iter != objects_.end(); ++iter)
result &= (*iter)->rebuildFromFile();
result &= rebuild();
return result;
}
void Shader::setHeaders(const string& customHeader) {
for (ShaderObjects::iterator iter = objects_.begin(); iter != objects_.end(); ++iter) {
(*iter)->setHeader(customHeader);
}
}
void Shader::bindFragDataLocation(GLuint colorNumber, std::string name) {
if (GpuCaps.getShaderVersion() >= GpuCapabilities::GlVersion::SHADER_VERSION_130) {
glBindFragDataLocation(id_, colorNumber, name.c_str());
}
}
void Shader::load(const string& filename, const string& customHeader) throw (Exception) {
return loadSeparate(filename + ".vert", "", filename + ".frag", customHeader);
}
void Shader::loadSeparate(const string& vert_filename, const string& geom_filename,
const string& frag_filename, const string& customHeader, const std::string& customGlslVersion)
throw (Exception)
{
ShaderObject* frag = 0;
ShaderObject* vert = 0;
ShaderObject* geom = 0;
if (!vert_filename.empty()) {
vert = new ShaderObject(vert_filename, ShaderObject::VERTEX_SHADER);
if (!customHeader.empty()) {
vert->setHeader(customHeader);
}
if (!customGlslVersion.empty()) {
vert->setCustomGlslVersion(customGlslVersion);
}
try {
vert->loadSourceFromFile(vert_filename);
}
catch (const Exception& e) {
LDEBUG("Failed to load vertex shader " << vert_filename << ": " << e.what());
delete vert;
throw Exception("Failed to load vertex shader " + vert_filename + ": " + e.what());
}
vert->uploadSource();
if (!vert->compileShader()) {
LERROR("Failed to compile vertex shader " << vert_filename);
LERROR("Compiler Log: \n" << vert->getCompilerLog());
delete vert;
throw Exception("Failed to compile vertex shader: " + vert_filename);
}
}
if (!geom_filename.empty()) {
geom = new ShaderObject(geom_filename, ShaderObject::GEOMETRY_SHADER);
if (!customHeader.empty()) {
geom->setHeader(customHeader);
}
if (!customGlslVersion.empty()) {
geom->setCustomGlslVersion(customGlslVersion);
}
try {
geom->loadSourceFromFile(geom_filename);
}
catch (const Exception& e) {
LDEBUG("Failed to load geometry shader " << geom_filename << ": " << e.what());
delete vert;
delete geom;
throw Exception("Failed to load geometry shader " + geom_filename + ": " + e.what());
}
geom->uploadSource();
if (!geom->compileShader()) {
LERROR("Failed to compile geometry shader " << geom_filename);
LERROR("Compiler Log: \n" << geom->getCompilerLog());
delete vert;
delete geom;
throw Exception("Failed to compile geometry shader: " + geom_filename);
}
}
if (!frag_filename.empty()) {
frag = new ShaderObject(frag_filename, ShaderObject::FRAGMENT_SHADER);
if (!customHeader.empty()) {
frag->setHeader(customHeader);
}
if (!customGlslVersion.empty()) {
frag->setCustomGlslVersion(customGlslVersion);
}
try {
frag->loadSourceFromFile(frag_filename);
}
catch (const Exception& e) {
LDEBUG("Failed to load fragment shader " << frag_filename);
delete frag;
delete geom;
delete vert;
throw Exception("Failed to load fragment shader " + frag_filename + ": " + e.what());
}
if (GpuCaps.getShaderVersion() >= GpuCapabilities::GlVersion::SHADER_VERSION_130)
bindFragDataLocation(0, "FragData0");
frag->uploadSource();
if (!frag->compileShader()) {
LERROR("Failed to compile fragment shader " << frag_filename);
LERROR("Compiler Log: \n" << frag->getCompilerLog());
delete vert;
delete geom;
delete frag;
throw Exception("Failed to compile fragment shader: " + frag_filename);
}
}
// Attach ShaderObjects, dtor will take care of freeing them
if (frag)
attachObject(frag);
if (vert)
attachObject(vert);
if (geom)
attachObject(geom);
if (!linkProgram()) {
LERROR("Failed to link shader (" << vert_filename << "," << frag_filename << "," << geom_filename << ")");
if (vert) {
LERROR(vert->filename_ << " Vertex shader compiler log: \n" << vert->getCompilerLog());
detachObject(vert);
delete vert;
}
if (geom) {
LERROR(geom->filename_ << " Geometry shader compiler log: \n" << geom->getCompilerLog());
detachObject(geom);
delete geom;
}
if (frag) {
LERROR(frag->filename_ << " Fragment shader compiler log: \n" << frag->getCompilerLog());
detachObject(frag);
delete frag;
}
LERROR("Linker Log: \n" << getLinkerLog());
throw Exception("Failed to link shader (" + vert_filename + "," + frag_filename + "," + geom_filename + ")");
}
if (vert && vert->getCompilerLog().size() > 1) {
LDEBUG("Vertex shader compiler log for file '" << vert_filename
<< "': \n" << vert->getCompilerLog());
}
if (geom && geom->getCompilerLog().size() > 1) {
LDEBUG("Geometry shader compiler log for file '" << geom_filename
<< "': \n" << geom->getCompilerLog());
}
if (frag && frag->getCompilerLog().size() > 1) {
LDEBUG("Fragment shader compiler log for file '" << frag_filename
<< "': \n" << frag->getCompilerLog());
}
if (getLinkerLog().size() > 1) {
LDEBUG("Linker log for '" << vert_filename << "' and '"
<< frag_filename << "' and '"
<< geom_filename << "': \n" << getLinkerLog());
}
}
GLint Shader::getUniformLocation(const string& name) {
GLint l;
l = glGetUniformLocation(id_, name.c_str());
if (l == -1 && !ignoreError_)
LWARNING("Failed to locate uniform Location: " << name);
return l;
}
void Shader::setIgnoreUniformLocationError(bool ignoreError) {
ignoreError_ = ignoreError;
}
bool Shader::getIgnoreUniformLocationError() {
return ignoreError_;
}
// Floats
bool Shader::setUniform(const string& name, GLfloat value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1f(l, value);
return true;
}
bool Shader::setUniform(const string& name, GLfloat v1, GLfloat v2) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform2f(l, v1, v2);
return true;
}
bool Shader::setUniform(const string& name, GLfloat v1, GLfloat v2, GLfloat v3) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform3f(l, v1, v2, v3);
return true;
}
bool Shader::setUniform(const string& name, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform4f(l, v1, v2, v3, v4);
return true;
}
bool Shader::setUniform(const string& name, GLfloat* v, int count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1fv(l, count, v);
return true;
}
// Integers
bool Shader::setUniform(const string& name, GLint value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1i(l, value);
return true;
}
bool Shader::setUniform(const string& name, GLint v1, GLint v2) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform2i(l, v1, v2);
return true;
}
bool Shader::setUniform(const string& name, GLint v1, GLint v2, GLint v3) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform3i(l, v1, v2, v3);
return true;
}
bool Shader::setUniform(const string& name, GLint v1, GLint v2, GLint v3, GLint v4) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform4i(l, v1, v2, v3, v4);
return true;
}
bool Shader::setUniform(const string& name, GLint* v, int count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1iv(l, count, v);
return true;
}
// Unsigned Integers
bool Shader::setUniform(const string& name, GLuint value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1ui(l, value);
return true;
}
bool Shader::setUniform(const string& name, GLuint v1, GLuint v2) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform2ui(l, v1, v2);
return true;
}
bool Shader::setUniform(const string& name, GLuint v1, GLuint v2, GLuint v3) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform3ui(l, v1, v2, v3);
return true;
}
bool Shader::setUniform(const string& name, GLuint v1, GLuint v2, GLuint v3, GLuint v4) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform4ui(l, v1, v2, v3, v4);
return true;
}
bool Shader::setUniform(const string& name, GLuint* v, int count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
glUniform1uiv(l, count, v);
return true;
}
bool Shader::setUniform(const string& name, bool value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, static_cast(value));
return true;
}
bool Shader::setUniform(const string& name, bool v1, bool v2) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, static_cast(v1), static_cast(v2));
return true;
}
bool Shader::setUniform(const string& name, bool v1, bool v2, bool v3) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, static_cast(v1), static_cast(v2), static_cast(v3));
return true;
}
bool Shader::setUniform(const string& name, bool v1, bool v2, bool v3, bool v4) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, static_cast(v1), static_cast(v2), static_cast(v3), static_cast(v4));
return true;
}
bool Shader::setUniform(const string& name, GLboolean* v, int count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLint* vector = new GLint[count];
for (int i=0; i < count; i++)
vector[i] = static_cast( v[i] );
glUniform1iv(l, count, vector);
delete[] vector;
return true;
}
// Vectors
bool Shader::setUniform(const string& name, const Vector2f& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, Vector2f* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
//TODO: use the adress directly, without copying, same below. joerg
GLfloat* values = new GLfloat[2*count];
for (int i=0; i < count; i++){
values[2*i] = vectors[i].x;
values[2*i+1] = vectors[i].y;
}
glUniform2fv(l, count, values);
delete[] values;
return true;
}
bool Shader::setUniform(const string& name, const Vector3f& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, Vector3f* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLfloat* values = new GLfloat[3*count];
for (int i=0; i < count; i++) {
values[3*i] = vectors[i].x;
values[3*i+1] = vectors[i].y;
values[3*i+2] = vectors[i].z;
}
glUniform3fv(l, count, values);
delete[] values;
return true;
}
bool Shader::setUniform(const string& name, const Vector4f& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, Vector4f* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLfloat* values = new GLfloat[4*count];
for (int i=0; i < count; i++) {
values[4*i] = vectors[i].x;
values[4*i+1] = vectors[i].y;
values[4*i+2] = vectors[i].z;
values[4*i+3] = vectors[i].a;
}
glUniform4fv(l, count, values);
delete[] values;
return true;
}
bool Shader::setUniform(const string& name, const ivec2& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, ivec2* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLint* values = new GLint[2*count];
for (int i=0; i < count; i++) {
values[2*i] = vectors[i].x;
values[2*i+1] = vectors[i].y;
}
glUniform2iv(l, count, values);
delete[] values;
return true;
}
bool Shader::setUniform(const string& name, const ivec3& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, ivec3* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLint* values = new GLint[3*count];
for (int i=0; i < count; i++) {
values[3*i] = vectors[i].x;
values[3*i+1] = vectors[i].y;
values[3*i+2] = vectors[i].z;
}
glUniform3iv(l, count, values);
delete[] values;
return true;
}
bool Shader::setUniform(const string& name, const ivec4& value) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value);
return true;
}
bool Shader::setUniform(const string& name, ivec4* vectors, GLsizei count) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
GLint* values = new GLint[4*count];
for (int i=0; i < count; i++) {
values[4*i] = vectors[i].x;
values[4*i+1] = vectors[i].y;
values[4*i+2] = vectors[i].z;
values[4*i+3] = vectors[i].a;
}
glUniform4iv(l, count, values);
delete[] values;
return true;
}
// Note: Matrix is transposed by OpenGL
bool Shader::setUniform(const string& name, const Matrix2f& value, bool transpose) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value, transpose);
return true;
}
bool Shader::setUniform(const string& name, const Matrix3f& value, bool transpose) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value, transpose);
return true;
}
bool Shader::setUniform(const string& name, const Matrix4f& value, bool transpose) {
GLint l = getUniformLocation(name);
if (l == -1)
return false;
setUniform(l, value, transpose);
return true;
}
// No location lookup
//
// Floats
void Shader::setUniform(GLint l, GLfloat value) {
glUniform1f(l, value);
}
void Shader::setUniform(GLint l, GLfloat v1, GLfloat v2) {
glUniform2f(l, v1, v2);
}
void Shader::setUniform(GLint l, GLfloat v1, GLfloat v2, GLfloat v3) {
glUniform3f(l, v1, v2, v3);
}
void Shader::setUniform(GLint l, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4) {
glUniform4f(l, v1, v2, v3, v4);
}
// Integers
void Shader::setUniform(GLint l, GLint value) {
glUniform1i(l, value);
}
void Shader::setUniform(GLint l, GLint v1, GLint v2) {
glUniform2i(l, v1, v2);
}
void Shader::setUniform(GLint l, GLint v1, GLint v2, GLint v3) {
glUniform3i(l, v1, v2, v3);
}
void Shader::setUniform(GLint l, GLint v1, GLint v2, GLint v3, GLint v4) {
glUniform4i(l, v1, v2, v3, v4);
}
// Vectors
void Shader::setUniform(GLint l, const Vector2f& value) {
glUniform2fv(l, 1, value.elem);
}
void Shader::setUniform(GLint l, const Vector3f& value) {
glUniform3fv(l, 1, value.elem);
}
void Shader::setUniform(GLint l, const Vector4f& value) {
glUniform4fv(l, 1, value.elem);
}
void Shader::setUniform(GLint l, const ivec2& value) {
glUniform2i(l, static_cast(value.x), static_cast(value.y));
}
void Shader::setUniform(GLint l, const ivec3& value) {
glUniform3i(l, static_cast(value.x), static_cast(value.y), static_cast(value.z));
}
void Shader::setUniform(GLint l, const ivec4& value) {
glUniform4i(l, static_cast(value.x), static_cast(value.y),
static_cast(value.z), static_cast(value.w));
}
void Shader::setUniform(GLint l, const Matrix2f& value, bool transpose) {
glUniformMatrix2fv(l, 1, !transpose, value.elem);
}
void Shader::setUniform(GLint l, const Matrix3f& value, bool transpose) {
glUniformMatrix3fv(l, 1, !transpose, value.elem);
}
void Shader::setUniform(GLint l, const Matrix4f& value, bool transpose) {
glUniformMatrix4fv(l, 1, !transpose, value.elem);
}
// Attributes
//
// 1 component
void Shader::setAttribute(GLint index, GLfloat v1) {
glVertexAttrib1f(index, v1);
}
void Shader::setAttribute(GLint index, GLshort v1) {
glVertexAttrib1s(index, v1);
}
void Shader::setAttribute(GLint index, GLdouble v1) {
glVertexAttrib1d(index, v1);
}
// 2 components
void Shader::setAttribute(GLint index, GLfloat v1, GLfloat v2) {
glVertexAttrib2f(index, v1, v2);
}
void Shader::setAttribute(GLint index, GLshort v1, GLshort v2) {
glVertexAttrib2s(index, v1, v2);
}
void Shader::setAttribute(GLint index, GLdouble v1, GLdouble v2) {
glVertexAttrib2d(index, v1, v2);
}
// 3 components
void Shader::setAttribute(GLint index, GLfloat v1, GLfloat v2, GLfloat v3) {
glVertexAttrib3f(index, v1, v2, v3);
}
void Shader::setAttribute(GLint index, GLshort v1, GLshort v2, GLshort v3) {
glVertexAttrib3s(index, v1, v2, v3);
}
void Shader::setAttribute(GLint index, GLdouble v1, GLdouble v2, GLdouble v3) {
glVertexAttrib3d(index, v1, v2, v3);
}
// 4 components
void Shader::setAttribute(GLint index, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4) {
glVertexAttrib4f(index, v1, v2, v3, v4);
}
void Shader::setAttribute(GLint index, GLshort v1, GLshort v2, GLshort v3, GLshort v4) {
glVertexAttrib4s(index, v1, v2, v3, v4);
}
void Shader::setAttribute(GLint index, GLdouble v1, GLdouble v2, GLdouble v3, GLdouble v4) {
glVertexAttrib4d(index, v1, v2, v3, v4);
}
// For vectors
void Shader::setAttribute(GLint index, const Vector2f& v) {
glVertexAttrib2fv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector3f& v) {
glVertexAttrib3fv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4f& v) {
glVertexAttrib4fv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector2d& v) {
glVertexAttrib2dv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector3d& v) {
glVertexAttrib3dv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4d& v) {
glVertexAttrib4dv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector2& v) {
glVertexAttrib2sv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector3& v) {
glVertexAttrib3sv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4sv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4iv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4bv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4ubv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4usv(index, v.elem);
}
void Shader::setAttribute(GLint index, const Vector4& v) {
glVertexAttrib4uiv(index, v.elem);
}
// ATTENTION: This method was deliberately deactivated, since it was not working as expected
// with AMD GPUs.
// Attribute locations
//void Shader::setAttributeLocation(GLuint index, const std::string& name) {
// glBindAttribLocation(id_, index, name.c_str());
//}
GLint Shader::getAttributeLocation(const string& name) {
GLint l;
l = glGetAttribLocation(id_, name.c_str());
if (l == -1)
LERROR("Failed to locate attribute Location: " << name);
return l;
}
// Normalized attributes
void Shader::setNormalizedAttribute(GLint index, GLubyte v1, GLubyte v2, GLubyte v3, GLubyte v4) {
glVertexAttrib4Nub(index, v1, v2, v3, v4);
}
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Nbv(index, v.elem);
}
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Nsv(index, v.elem);
}
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Niv(index, v.elem);
}
// Unsigned version
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Nubv(index, v.elem);
}
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Nusv(index, v.elem);
}
void Shader::setNormalizedAttribute(GLint index, const Vector4& v) {
glVertexAttrib4Nuiv(index, v.elem);
}
bool Shader::selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName) {
GLuint index = glGetSubroutineIndex(id_, type, subroutineName.c_str());
if (index == GL_INVALID_INDEX) {
LWARNING("Failed to locate subroutine Location: " << subroutineName);
return false;
}
glUniformSubroutinesuiv(type, 1, &index);
return true;
}
//------------------------------------------------------------------------------
const string ShaderManager::loggerCat_("cgt.Shader.Manager");
ShaderManager::ShaderManager()
: ResourceManager(false)
{}
Shader* ShaderManager::load(const std::string& vertFilename, const std::string& fragFilename, const std::string& customHeader) throw (Exception) {
return loadWithCustomGlslVersion(vertFilename, "", fragFilename, customHeader, "");
}
Shader* ShaderManager::load(const std::string& vertFilename, const std::string& geomFilename, const std::string& fragFilename, const std::string& customHeader) throw(Exception) {
return loadWithCustomGlslVersion(vertFilename, geomFilename, fragFilename, customHeader, "");
}
Shader* ShaderManager::loadWithCustomGlslVersion(const std::string& vertFilename, const std::string& geomFilename, const std::string& fragFilename, const std::string& customHeader, const std::string& customGlslVersion) throw(Exception) {
//LDEBUG("Loading files " << vertFilename << " and " << fragFilename);
if (!GpuCaps.areShadersSupported()) {
LERROR("Shaders are not supported.");
throw Exception("Shaders are not supported.");
}
// create a somewhat unique identifier for this shader triple
string identifier = vertFilename + "#" + fragFilename + "#" + geomFilename;
if (isLoaded(identifier)) {
LDEBUG("Shader already loaded. Increase usage count.");
increaseUsage(identifier);
return get(identifier);
}
Shader* shdr = new Shader();
// searching in all paths for every shader
string vert_completeFilename;
if (!vertFilename.empty())
vert_completeFilename = completePath(vertFilename);
string geom_completeFilename;
if (!geomFilename.empty())
geom_completeFilename = completePath(geomFilename);
string frag_completeFilename;
if (!fragFilename.empty())
frag_completeFilename = completePath(fragFilename);
// loading and linking found shaders
try {
shdr->loadSeparate(vert_completeFilename, geom_completeFilename,
frag_completeFilename, customHeader, customGlslVersion);
// register even when caching is disabled, needed for rebuildFromFile()
reg(shdr, identifier);
return shdr;
}
catch (const Exception& /*e*/) {
delete shdr;
throw;
}
}
bool ShaderManager::rebuildAllShadersFromFile() {
bool result = true;
for (std::map::Resource*>::iterator iter = resourcesByPtr_.begin();
iter != resourcesByPtr_.end(); ++iter)
{
result &= iter->first->rebuildFromFile();
}
return result;
}
} // namespace