#include "ofShader.h"
#include "ofUtils.h"
#include "ofFileUtils.h"
#include "ofGraphics.h"
#include "ofGLProgrammableRenderer.h"
#include "ofTexture.h"
#include "ofMatrix4x4.h"
#include "ofMatrix3x3.h"
#include "ofVec2f.h"
#include "ofVec3f.h"
#include "ofVec4f.h"
#include "ofParameterGroup.h"
#include "ofParameter.h"
#include "ofBufferObject.h"
#include <regex>
#ifdef TARGET_ANDROID
#include "ofxAndroidUtils.h"
#endif
using namespace std;
static const string COLOR_ATTRIBUTE="color";
static const string POSITION_ATTRIBUTE="position";
static const string NORMAL_ATTRIBUTE="normal";
static const string TEXCOORD_ATTRIBUTE="texcoord";
static map<GLuint,int> & getShaderIds(){
static map<GLuint,int> * ids = new map<GLuint,int>;
return *ids;
}
static map<GLuint,int> & getProgramIds(){
static map<GLuint,int> * ids = new map<GLuint,int>;
return *ids;
}
static void retainShader(GLuint id){
if(id==0) return;
if(getShaderIds().find(id)!=getShaderIds().end()){
getShaderIds()[id]++;
}else{
getShaderIds()[id]=1;
}
}
static void releaseShader(GLuint program, GLuint id){
if(getShaderIds().find(id)!=getShaderIds().end()){
getShaderIds()[id]--;
if(getShaderIds()[id]==0){
glDetachShader(program, id);
glDeleteShader(id);
getShaderIds().erase(id);
}
}else{
ofLogWarning("ofShader") << "releaseShader(): something's wrong here, releasing unknown shader id " << id << " from program " << program;
glDetachShader(program, id);
glDeleteShader(id);
}
}
static void retainProgram(GLuint id){
if(id==0) return;
if(getProgramIds().find(id)!=getProgramIds().end()){
getProgramIds()[id]++;
}else{
getProgramIds()[id]=1;
}
}
static void releaseProgram(GLuint id){
if(getProgramIds().find(id)!=getProgramIds().end()){
getProgramIds()[id]--;
if(getProgramIds()[id]==0){
glDeleteProgram(id);
getProgramIds().erase(id);
}
}else{
ofLogWarning("ofShader") << "releaseProgram(): something's wrong here, releasing unknown program id " << id;
glDeleteProgram(id);
}
}
#ifndef TARGET_OPENGLES
ofShader::TransformFeedbackRangeBinding::TransformFeedbackRangeBinding(const ofBufferObject & buffer, GLuint offset, GLuint size)
:offset(offset)
,size(size)
,buffer(buffer){}
ofShader::TransformFeedbackBaseBinding::TransformFeedbackBaseBinding(const ofBufferObject & buffer)
:buffer(buffer){}
#endif
ofShader::ofShader() :
program(0),
bLoaded(false)
{
}
ofShader::~ofShader() {
unload();
}
ofShader::ofShader(const ofShader & mom)
:program(mom.program)
,bLoaded(mom.bLoaded)
,shaders(mom.shaders)
,uniformsCache(mom.uniformsCache)
,attributesBindingsCache(mom.attributesBindingsCache)
#ifndef TARGET_OPENGLES
,uniformBlocksCache(mom.uniformBlocksCache)
#endif
{
if(mom.bLoaded){
retainProgram(program);
for(auto it: shaders){
auto shader = it.second;
retainShader(shader.id);
}
#ifdef TARGET_ANDROID
ofAddListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
}
}
ofShader & ofShader::operator=(const ofShader & mom){
if(this == &mom) {
return *this;
}
if(bLoaded){
unload();
}
program = mom.program;
bLoaded = mom.bLoaded;
shaders = mom.shaders;
attributesBindingsCache = mom.attributesBindingsCache;
uniformsCache = mom.uniformsCache;
if(mom.bLoaded){
retainProgram(program);
for(auto it: shaders){
auto shader = it.second;
retainShader(shader.id);
}
#ifdef TARGET_ANDROID
ofAddListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
}
return *this;
}
ofShader::ofShader(ofShader && mom)
:program(std::move(mom.program))
,bLoaded(std::move(mom.bLoaded))
,shaders(std::move(mom.shaders))
,uniformsCache(std::move(mom.uniformsCache))
,attributesBindingsCache(std::move(mom.attributesBindingsCache)){
if(mom.bLoaded){
#ifdef TARGET_ANDROID
ofAddListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
}
mom.bLoaded = false;
}
ofShader & ofShader::operator=(ofShader && mom){
if(this == &mom) {
return *this;
}
if(bLoaded){
unload();
}
program = std::move(mom.program);
bLoaded = std::move(mom.bLoaded);
shaders = std::move(mom.shaders);
attributesBindingsCache = std::move(mom.attributesBindingsCache);
uniformsCache = std::move(mom.uniformsCache);
if(mom.bLoaded){
#ifdef TARGET_ANDROID
ofAddListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
}
mom.bLoaded = false;
return *this;
}
bool ofShader::load(const std::filesystem::path& shaderName) {
return load(shaderName.string() + ".vert", shaderName.string() + ".frag");
}
bool ofShader::load(const std::filesystem::path& vertName, const std::filesystem::path& fragName, const std::filesystem::path& geomName) {
if(vertName.empty() == false) setupShaderFromFile(GL_VERTEX_SHADER, vertName);
if(fragName.empty() == false) setupShaderFromFile(GL_FRAGMENT_SHADER, fragName);
#ifndef TARGET_OPENGLES
if(geomName.empty() == false) setupShaderFromFile(GL_GEOMETRY_SHADER_EXT, geomName);
#endif
if(ofIsGLProgrammableRenderer()){
bindDefaults();
}
return linkProgram();
}
#if !defined(TARGET_OPENGLES) && defined(glDispatchCompute)
bool ofShader::loadCompute(const std::filesystem::path& shaderName) {
return setupShaderFromFile(GL_COMPUTE_SHADER, shaderName) && linkProgram();
}
#endif
bool ofShader::setup(const ofShaderSettings & settings) {
for (auto shader : settings.shaderFiles) {
auto ty = shader.first;
auto file = shader.second;
auto shaderSource = sourceFromFile(ty, file);
shaderSource.intDefines = settings.intDefines;
shaderSource.floatDefines = settings.floatDefines;
if (!setupShaderFromSource(std::move(shaderSource))) {
return false;
}
}
for (auto shader : settings.shaderSources) {
auto ty = shader.first;
auto source = shader.second;
Source shaderSource{ty, source, settings.sourceDirectoryPath};
shaderSource.intDefines = settings.intDefines;
shaderSource.floatDefines = settings.floatDefines;
if (!setupShaderFromSource(std::move(shaderSource))) {
return false;
}
}
if (ofIsGLProgrammableRenderer() && settings.bindDefaults) {
bindDefaults();
}
return linkProgram();
}
#if !defined(TARGET_OPENGLES)
bool ofShader::setup(const TransformFeedbackSettings & settings) {
for (auto shader : settings.shaderFiles) {
auto ty = shader.first;
auto file = shader.second;
auto shaderSource = sourceFromFile(ty, file);
shaderSource.intDefines = settings.intDefines;
shaderSource.floatDefines = settings.floatDefines;
if (!setupShaderFromSource(std::move(shaderSource))) {
return false;
}
}
for (auto shader : settings.shaderSources) {
auto ty = shader.first;
auto source = shader.second;
Source shaderSource{ty, source, settings.sourceDirectoryPath};
shaderSource.intDefines = settings.intDefines;
shaderSource.floatDefines = settings.floatDefines;
if (!setupShaderFromSource(std::move(shaderSource))) {
return false;
}
}
if (ofIsGLProgrammableRenderer() && settings.bindDefaults) {
bindDefaults();
}
if (!settings.varyingsToCapture.empty()) {
std::vector<const char*> varyings(settings.varyingsToCapture.size());
std::transform(settings.varyingsToCapture.begin(), settings.varyingsToCapture.end(), varyings.begin(), [](const std::string & str) {
return str.c_str();
});
glTransformFeedbackVaryings(getProgram(), varyings.size(), varyings.data(), settings.bufferMode);
}
return linkProgram();
}
#endif
bool ofShader::setupShaderFromFile(GLenum type, const std::filesystem::path& filename) {
ofBuffer buffer = ofBufferFromFile(filename);
string absoluteFilePath = ofFilePath::getAbsolutePath(filename, true);
string sourceDirectoryPath = ofFilePath::getEnclosingDirectory(absoluteFilePath,false);
if(buffer.size()) {
return setupShaderFromSource(type, buffer.getText(), sourceDirectoryPath);
} else {
ofLogError("ofShader") << "setupShaderFromFile(): couldn't load " << nameForType(type) << " shader " << " from \"" << absoluteFilePath << "\"";
return false;
}
}
ofShader::Source ofShader::sourceFromFile(GLenum type, const std::filesystem::path& filename) {
ofBuffer buffer = ofBufferFromFile(filename);
string absoluteFilePath = ofFilePath::getAbsolutePath(filename, true);
string sourceDirectoryPath = ofFilePath::getEnclosingDirectory(absoluteFilePath,false);
if(buffer.size()) {
return Source{type, buffer.getText(), sourceDirectoryPath};
} else {
ofLogError("ofShader") << "setupShaderFromFile(): couldn't load " << nameForType(type) << " shader " << " from \"" << absoluteFilePath << "\"";
return Source{};
}
}
bool ofShader::setupShaderFromSource(GLenum type, string source, string sourceDirectoryPath) {
return setupShaderFromSource({type, source, sourceDirectoryPath});
}
bool ofShader::setupShaderFromSource(ofShader::Source && source){
unload();
checkAndCreateProgram();
GLuint clearErrors = glGetError();
if( clearErrors != GL_NO_ERROR ){
ofLogVerbose("ofShader") << "setupShaderFromSource(): OpenGL error after checkAndCreateProgram() (probably harmless): error " << clearErrors;
}
GLuint shaderId = glCreateShader(source.type);
if(shaderId == 0) {
ofLogError("ofShader") << "setupShaderFromSource(): failed creating " << nameForType(source.type) << " shader";
return false;
} else {
retainShader(shaderId);
}
source.expandedSource = parseForIncludes( source.source, source.directoryPath );
for(auto & define: source.intDefines){
const auto & name = define.first;
const auto & value = define.second;
std::regex re_define("#define[ \t]+" + name + "[ \t]+(([1-9][0-9]*)|(0[xX][0-9a-fA-F]+))");
source.expandedSource = std::regex_replace(source.expandedSource, re_define, "#define " + name + " " + std::to_string(value));
}
for(auto & define: source.floatDefines){
const auto & name = define.first;
const auto & value = define.second;
std::regex re_define("#define[ \t]+" + name + "[ \t]+[0-9]*(\\.[0-9]*f?)?");
source.expandedSource = std::regex_replace(source.expandedSource, re_define, "#define " + name + " " + std::to_string(value));
}
shaders[source.type] = { shaderId, std::move(source) };
auto & shader = shaders[source.type];
const char* sptr = shader.source.expandedSource.c_str();
int ssize = shader.source.expandedSource.size();
glShaderSource(shaderId, 1, &sptr, &ssize);
glCompileShader(shaderId);
GLint status = GL_FALSE;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
GLuint err = glGetError();
if (err != GL_NO_ERROR){
ofLogError("ofShader") << "setupShaderFromSource(): OpenGL generated error " << err << " trying to get the compile status for a " << nameForType(shader.source.type) << " shader, does your video card support this?";
return false;
}
if(status == GL_TRUE){
ofLogVerbose("ofShader") << "setupShaderFromSource(): " << nameForType(shader.source.type) + " shader compiled";
#ifdef TARGET_EMSCRIPTEN
checkShaderInfoLog(shaderId, source.type, OF_LOG_VERBOSE);
#else
checkShaderInfoLog(shaderId, source.type, OF_LOG_WARNING);
#endif
}else if (status == GL_FALSE) {
ofLogError("ofShader") << "setupShaderFromSource(): " << nameForType(shader.source.type) + " shader failed to compile";
checkShaderInfoLog(shaderId, source.type, OF_LOG_ERROR);
return false;
}
return true;
}
string ofShader::parseForIncludes( const string& source, const std::filesystem::path& sourceDirectoryPath) {
vector<string> included;
return parseForIncludes( source, included, 0, sourceDirectoryPath);
}
string ofShader::parseForIncludes( const string& source, vector<string>& included, int level, const std::filesystem::path& sourceDirectoryPath) {
if ( level > 32 ) {
ofLogError( "ofShader", "glsl header inclusion depth limit reached, might be caused by cyclic header inclusion" );
return "";
}
stringstream output;
stringstream input;
input << source;
auto match_pragma_include = [](const std::string& s_, std::string& filename_) -> bool {
filename_ = "";
std::istringstream s(s_);
s >> std::ws;
if (s.peek() != '#') return false;
s.seekg(1, std::ios::cur);
std::string p, i, f;
s >> std::skipws >> p >> i >> f;
if (p.empty() || i.empty() || (f.size() < 2) ) return false;
if (p != "pragma") return false;
if (i != "include") return false;
if (f[0] == '<' && f[f.size()-1] != '>') return false;
if ((f[0] == '"' || f[0] == '\'') && (f[0] != f[f.size()-1])) return false;
filename_ = f.substr(1,f.size()-2);
return true;
};
string line;
while( std::getline( input, line ) ) {
string include;
if (!match_pragma_include(line, include)){
output << line << endl;
continue;
};
if ( std::find( included.begin(), included.end(), include ) != included.end() ) {
ofLogVerbose("ofShader") << include << " already included";
continue;
}
include = ofFile(ofFilePath::join(sourceDirectoryPath, include)).getAbsolutePath();
included.push_back( include );
ofBuffer buffer = ofBufferFromFile( include );
if ( !buffer.size() ) {
ofLogError("ofShader") <<"Could not open glsl include file " << include;
continue;
}
string currentDir = ofFile(include).getEnclosingDirectory();
output << parseForIncludes( buffer.getText(), included, level + 1, currentDir ) << endl;
}
return output.str();
}
string ofShader::getShaderSource(GLenum type) const{
auto source = shaders.find(type);
if ( source != shaders.end()) {
return source->second.source.expandedSource;
} else {
ofLogError("ofShader") << "No shader source for shader of type: " << nameForType(type);
return "";
}
}
void ofShader::setGeometryInputType(GLenum type) {
#ifndef TARGET_OPENGLES
checkAndCreateProgram();
glProgramParameteri(program, GL_GEOMETRY_INPUT_TYPE_EXT, type);
#endif
}
void ofShader::setGeometryOutputType(GLenum type) {
#ifndef TARGET_OPENGLES
checkAndCreateProgram();
glProgramParameteri(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, type);
#endif
}
void ofShader::setGeometryOutputCount(int count) {
#ifndef TARGET_OPENGLES
checkAndCreateProgram();
glProgramParameteri(program, GL_GEOMETRY_VERTICES_OUT_EXT, count);
#endif
}
int ofShader::getGeometryMaxOutputCount() const{
#ifndef TARGET_OPENGLES
int temp;
glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, &temp);
return temp;
#else
return 0;
#endif
}
bool ofShader::checkProgramLinkStatus() {
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
GLuint err = glGetError();
if (err != GL_NO_ERROR){
ofLogError("ofShader") << "checkProgramLinkStatus(): OpenGL generated error " << err << " trying to get the program link status, does your video card support shader programs?";
return false;
}
if(status == GL_TRUE)
ofLogVerbose("ofShader") << "checkProgramLinkStatus(): program " << program << " linked";
else if (status == GL_FALSE) {
ofLogError("ofShader") << "checkProgramLinkStatus(): program failed to link";
checkProgramInfoLog();
return false;
}
return true;
}
void ofShader::checkShaderInfoLog(GLuint shader, GLenum type, ofLogLevel logLevel) {
GLsizei infoLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
if (infoLength > 1) {
ofBuffer infoBuffer;
infoBuffer.allocate(infoLength);
glGetShaderInfoLog(shader, infoLength, &infoLength, infoBuffer.getData());
ofLog(logLevel, "ofShader: %s shader reports:\n%s", nameForType(type).c_str(), infoBuffer.getText().c_str());
#if (!defined(TARGET_LINUX) || defined(GCC_HAS_REGEX))
if (shaders.find(type) != shaders.end()) {
std::regex nvidia_ati("^.*[(:]{1}(\\d+)[:)]{1}.*");
std::regex intel("^[0-9]+:([0-9]+)\\([0-9]+\\):.*$");
std::smatch matches;
string infoString = ofTrim(infoBuffer);
if (std::regex_search(infoString, matches, intel) || std::regex_search(infoString, matches, nvidia_ati)){
ofBuffer buf;
buf.set(shaders[type].source.expandedSource);
ofBuffer::Line line = buf.getLines().begin();
int offendingLineNumber = ofToInt(matches[1]);
ostringstream msg;
msg << "ofShader: " + nameForType(type) + ", offending line " << offendingLineNumber << " :"<< endl;
for(int i=0; line != buf.getLines().end(); line++, i++ ){
string s = *line;
if ( i >= offendingLineNumber -3 && i < offendingLineNumber + 2 ){
msg << "\t" << setw(5) << (i+1) << "\t" << s << endl;
}
}
ofLog(logLevel) << msg.str();
}else{
ofLog(logLevel) << shaders[type].source.expandedSource;
}
}
#endif
}
}
void ofShader::checkProgramInfoLog() {
GLsizei infoLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLength);
if (infoLength > 1) {
ofBuffer infoBuffer;
infoBuffer.allocate(infoLength);
glGetProgramInfoLog(program, infoLength, &infoLength, infoBuffer.getData());
string msg = "ofShader: program reports:\n";
ofLogError("ofShader") << msg + infoBuffer.getText();
#ifdef TARGET_RAPSBERRY_PI
for(auto it: shaders){
ofLogNotice("ofShader") << it.second.expandedSource;
}
#endif
}
}
void ofShader::checkAndCreateProgram() {
#ifndef TARGET_OPENGLES
if(GL_ARB_shader_objects) {
#else
if(ofIsGLProgrammableRenderer()){
#endif
if(program == 0) {
ofLogVerbose("ofShader") << "checkAndCreateProgram(): creating GLSL program";
program = glCreateProgram();
retainProgram(program);
}
} else {
ofLogError("ofShader") << "sorry, it looks like you can't run 'ARB_shader_objects'";
ofLogError("ofShader") << "please check the capabilites of your graphics card: http://www.ozone3d.net/gpu_caps_viewer";
}
}
bool ofShader::linkProgram() {
if(shaders.empty()) {
ofLogError("ofShader") << "linkProgram(): trying to link GLSL program, but no shaders created yet";
} else {
checkAndCreateProgram();
for(auto it: shaders){
auto shader = it.second;
if(shader.id>0) {
ofLogVerbose("ofShader") << "linkProgram(): attaching " << nameForType(it.first) << " shader to program " << program;
glAttachShader(program, shader.id);
}
}
glLinkProgram(program);
checkProgramLinkStatus();
GLint numUniforms = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
GLint uniformMaxLength = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxLength);
GLint count = -1;
GLenum type = 0;
GLsizei length;
GLint location;
vector<GLchar> uniformName(uniformMaxLength);
for(GLint i = 0; i < numUniforms; i++) {
glGetActiveUniform(program, i, uniformMaxLength, &length, &count, &type, uniformName.data());
string name(uniformName.begin(), uniformName.begin()+length);
location = glGetUniformLocation(program, name.c_str());
if (location == -1) continue;
uniformsCache[name] = location;
auto arrayPos = name.find('[');
if(arrayPos!=std::string::npos){
name = name.substr(0, arrayPos);
uniformsCache[name] = location;
}
}
#ifndef TARGET_OPENGLES
#ifdef GLEW_ARB_uniform_buffer_object
if(GLEW_ARB_uniform_buffer_object) {
GLint numUniformBlocks = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
count = -1;
type = 0;
vector<GLchar> uniformBlockName(uniformMaxLength);
for(GLint i = 0; i < numUniformBlocks; i++) {
glGetActiveUniformBlockName(program, i, uniformMaxLength, &length, uniformBlockName.data() );
string name(uniformBlockName.begin(), uniformBlockName.begin()+length);
uniformBlocksCache[name] = glGetUniformBlockIndex(program, name.c_str());
}
}
#endif
#endif
#ifdef TARGET_ANDROID
ofAddListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
bLoaded = true;
}
return bLoaded;
}
#ifdef TARGET_ANDROID
void ofShader::unloadGL(){
for(auto it: shaders) {
auto shader = it.second;
if(shader.id) {
releaseShader(program,shader.id);
}
}
if (program) {
releaseProgram(program);
program = 0;
}
bLoaded = false;
ofAddListener(ofxAndroidEvents().reloadGL,this,&ofShader::reloadGL);
}
void ofShader::reloadGL(){
auto source = shaders;
auto bindings = attributesBindingsCache;
shaders.clear();
uniformsCache.clear();
#ifndef TARGET_OPENGLES
#ifdef GLEW_ARB_uniform_buffer_object
uniformBlocksCache.clear();
#endif
#endif
attributesBindingsCache.clear();
for(auto & shader: source){
auto source = shader.second.source;
setupShaderFromSource(std::move(shader.second.source));
}
for(auto binding: bindings){
bindAttribute(binding.second, binding.first);
}
linkProgram();
}
#endif
void ofShader::bindAttribute(GLuint location, const string & name) const{
attributesBindingsCache[name] = location;
glBindAttribLocation(program,location,name.c_str());
}
bool ofShader::bindDefaults(){
if(shaders.empty()) {
ofLogError("ofShader") << "bindDefaults(): trying to link GLSL program, but no shaders created yet";
return false;
} else {
bindAttribute(ofShader::POSITION_ATTRIBUTE,::POSITION_ATTRIBUTE);
bindAttribute(ofShader::COLOR_ATTRIBUTE,::COLOR_ATTRIBUTE);
bindAttribute(ofShader::NORMAL_ATTRIBUTE,::NORMAL_ATTRIBUTE);
bindAttribute(ofShader::TEXCOORD_ATTRIBUTE,::TEXCOORD_ATTRIBUTE);
return true;
}
}
void ofShader::unload() {
if(bLoaded) {
for(auto it: shaders) {
auto shader = it.second;
if(shader.id) {
ofLogVerbose("ofShader") << "unload(): detaching and deleting " << nameForType(shader.source.type) << " shader from program " << program;
releaseShader(program,shader.id);
}
}
if (program) {
releaseProgram(program);
program = 0;
}
shaders.clear();
uniformsCache.clear();
#ifndef TARGET_OPENGLES
#ifdef GLEW_ARB_uniform_buffer_object
uniformBlocksCache.clear();
#endif
#endif
attributesBindingsCache.clear();
#ifdef TARGET_ANDROID
ofRemoveListener(ofxAndroidEvents().reloadGL,this,&ofShader::reloadGL);
ofRemoveListener(ofxAndroidEvents().unloadGL,this,&ofShader::unloadGL);
#endif
}
bLoaded = false;
}
bool ofShader::isLoaded() const{
return bLoaded;
}
void ofShader::begin() const{
ofGetGLRenderer()->bind(*this);
}
void ofShader::end() const{
ofGetGLRenderer()->unbind(*this);
}
#if !defined(TARGET_OPENGLES)
void ofShader::beginTransformFeedback(GLenum mode) const {
begin();
glEnable(GL_RASTERIZER_DISCARD);
glBeginTransformFeedback(mode);
}
void ofShader::beginTransformFeedback(GLenum mode, const TransformFeedbackRangeBinding & binding) const {
binding.buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index, binding.offset, binding.size);
beginTransformFeedback(mode);
}
void ofShader::beginTransformFeedback(GLenum mode, const std::vector<TransformFeedbackRangeBinding> & bindings) const {
for (auto & binding : bindings) {
binding.buffer.bindRange(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index, binding.offset, binding.size);
}
beginTransformFeedback(mode);
}
void ofShader::beginTransformFeedback(GLenum mode, const TransformFeedbackBaseBinding & binding) const {
binding.buffer.bindBase(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
beginTransformFeedback(mode);
}
void ofShader::beginTransformFeedback(GLenum mode, const std::vector<TransformFeedbackBaseBinding> & bindings) const {
for (auto & binding : bindings) {
binding.buffer.bindBase(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
}
beginTransformFeedback(mode);
}
void ofShader::endTransformFeedback() const {
glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);
end();
}
void ofShader::endTransformFeedback(const TransformFeedbackRangeBinding & binding) const {
binding.buffer.unbindRange(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
endTransformFeedback();
}
void ofShader::endTransformFeedback(const std::vector<TransformFeedbackRangeBinding> & bindings) const {
for (auto & binding : bindings) {
binding.buffer.unbindRange(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
}
endTransformFeedback();
}
void ofShader::endTransformFeedback(const TransformFeedbackBaseBinding & binding) const {
binding.buffer.unbindBase(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
endTransformFeedback();
}
void ofShader::endTransformFeedback(const std::vector<TransformFeedbackBaseBinding> & bindings) const {
for (auto & binding : bindings) {
binding.buffer.unbindBase(GL_TRANSFORM_FEEDBACK_BUFFER, binding.index);
}
endTransformFeedback();
}
#endif
#if !defined(TARGET_OPENGLES) && defined(glDispatchCompute)
void ofShader::dispatchCompute(GLuint x, GLuint y, GLuint z) const{
glDispatchCompute(x,y,z);
}
#endif
void ofShader::setUniformTexture(const string & name, const ofBaseHasTexture& img, int textureLocation) const{
setUniformTexture(name, img.getTexture(), textureLocation);
}
void ofShader::setUniformTexture(const string & name, int textureTarget, GLint textureID, int textureLocation) const{
if(bLoaded) {
glActiveTexture(GL_TEXTURE0 + textureLocation);
if (!ofIsGLProgrammableRenderer()){
glEnable(textureTarget);
glBindTexture(textureTarget, textureID);
glDisable(textureTarget);
} else {
glBindTexture(textureTarget, textureID);
}
setUniform1i(name, textureLocation);
glActiveTexture(GL_TEXTURE0);
}
}
void ofShader::setUniformTexture(const string & name, const ofTexture& tex, int textureLocation) const{
if(bLoaded) {
ofTextureData texData = tex.getTextureData();
glActiveTexture(GL_TEXTURE0 + textureLocation);
if (!ofIsGLProgrammableRenderer()){
glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, texData.textureID);
#ifndef TARGET_OPENGLES
if (texData.bufferId != 0) {
glTexBuffer(GL_TEXTURE_BUFFER, texData.glInternalFormat, texData.bufferId);
}
#endif
glDisable(texData.textureTarget);
} else {
glBindTexture(texData.textureTarget, texData.textureID);
#ifndef TARGET_OPENGLES
if (texData.bufferId != 0) {
glTexBuffer(GL_TEXTURE_BUFFER, texData.glInternalFormat, texData.bufferId);
}
#endif
}
setUniform1i(name, textureLocation);
glActiveTexture(GL_TEXTURE0);
}
}
void ofShader::setUniform1i(const string & name, int v1) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform1i(loc, v1);
}
}
void ofShader::setUniform2i(const string & name, int v1, int v2) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform2i(loc, v1, v2);
}
}
void ofShader::setUniform3i(const string & name, int v1, int v2, int v3) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform3i(loc, v1, v2, v3);
}
}
void ofShader::setUniform4i(const string & name, int v1, int v2, int v3, int v4) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform4i(loc, v1, v2, v3, v4);
}
}
void ofShader::setUniform1f(const string & name, float v1) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform1f(loc, v1);
}
}
void ofShader::setUniform2f(const string & name, float v1, float v2) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform2f(loc, v1, v2);
}
}
void ofShader::setUniform3f(const string & name, float v1, float v2, float v3) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform3f(loc, v1, v2, v3);
}
}
void ofShader::setUniform4f(const string & name, float v1, float v2, float v3, float v4) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform4f(loc, v1, v2, v3, v4);
}
}
void ofShader::setUniform2f(const string & name, const glm::vec2 & v) const{
setUniform2f(name,v.x,v.y);
}
void ofShader::setUniform3f(const string & name, const glm::vec3 & v) const{
setUniform3f(name,v.x,v.y,v.z);
}
void ofShader::setUniform4f(const string & name, const glm::vec4 & v) const{
setUniform4f(name,v.x,v.y,v.z,v.w);
}
void ofShader::setUniform4f(const string & name, const ofFloatColor & v) const{
setUniform4f(name,v.r,v.g,v.b,v.a);
}
void ofShader::setUniform1iv(const string & name, const int* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform1iv(loc, count, v);
}
}
void ofShader::setUniform2iv(const string & name, const int* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform2iv(loc, count, v);
}
}
void ofShader::setUniform3iv(const string & name, const int* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform3iv(loc, count, v);
}
}
void ofShader::setUniform4iv(const string & name, const int* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform4iv(loc, count, v);
}
}
void ofShader::setUniform1fv(const string & name, const float* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform1fv(loc, count, v);
}
}
void ofShader::setUniform2fv(const string & name, const float* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform2fv(loc, count, v);
}
}
void ofShader::setUniform3fv(const string & name, const float* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform3fv(loc, count, v);
}
}
void ofShader::setUniform4fv(const string & name, const float* v, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniform4fv(loc, count, v);
}
}
void ofShader::setUniforms(const ofParameterGroup & parameters) const{
for(std::size_t i=0;i<parameters.size();i++){
if(parameters[i].type()==typeid(ofParameter<int>).name()){
setUniform1i(parameters[i].getEscapedName(),parameters[i].cast<int>());
}else if(parameters[i].type()==typeid(ofParameter<float>).name()){
setUniform1f(parameters[i].getEscapedName(),parameters[i].cast<float>());
}else if(parameters[i].type()==typeid(ofParameter<glm::vec2>).name()){
setUniform2f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec2>());
}else if(parameters[i].type()==typeid(ofParameter<glm::vec3>).name()){
setUniform3f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec3>());
}else if(parameters[i].type()==typeid(ofParameter<glm::vec4>).name()){
setUniform4f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec4>());
}else if(parameters[i].type()==typeid(ofParameter<ofVec2f>).name()){
setUniform2f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec2>());
}else if(parameters[i].type()==typeid(ofParameter<ofVec3f>).name()){
setUniform3f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec3>());
}else if(parameters[i].type()==typeid(ofParameter<ofVec4f>).name()){
setUniform4f(parameters[i].getEscapedName(),parameters[i].cast<glm::vec4>());
}else if(parameters[i].type()==typeid(ofParameterGroup).name()){
setUniforms((ofParameterGroup&)parameters[i]);
}
}
}
void ofShader::setUniformMatrix3f(const string & name, const glm::mat3 & m, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniformMatrix3fv(loc, count, GL_FALSE, glm::value_ptr(m));
}
}
void ofShader::setUniformMatrix4f(const string & name, const glm::mat4 & m, int count) const{
if(bLoaded) {
int loc = getUniformLocation(name);
if (loc != -1) glUniformMatrix4fv(loc, count, GL_FALSE, glm::value_ptr(m));
}
}
#ifndef TARGET_OPENGLES
void ofShader::setAttribute1s(GLint location, short v1) const{
if(bLoaded)
glVertexAttrib1s(location, v1);
}
void ofShader::setAttribute2s(GLint location, short v1, short v2) const{
if(bLoaded)
glVertexAttrib2s(location, v1, v2);
}
void ofShader::setAttribute3s(GLint location, short v1, short v2, short v3) const{
if(bLoaded)
glVertexAttrib3s(location, v1, v2, v3);
}
void ofShader::setAttribute4s(GLint location, short v1, short v2, short v3, short v4) const{
if(bLoaded)
glVertexAttrib4s(location, v1, v2, v3, v4);
}
#endif
void ofShader::setAttribute1f(GLint location, float v1) const{
if(bLoaded)
glVertexAttrib1f(location, v1);
}
void ofShader::setAttribute2f(GLint location, float v1, float v2) const{
if(bLoaded)
glVertexAttrib2f(location, v1, v2);
}
void ofShader::setAttribute3f(GLint location, float v1, float v2, float v3) const{
if(bLoaded)
glVertexAttrib3f(location, v1, v2, v3);
}
void ofShader::setAttribute4f(GLint location, float v1, float v2, float v3, float v4) const{
if(bLoaded)
glVertexAttrib4f(location, v1, v2, v3, v4);
}
void ofShader::setAttribute1fv(const string & name, const float* v, GLsizei stride) const{
if(bLoaded){
GLint location = getAttributeLocation(name);
if (location != -1) {
glVertexAttribPointer(location, 1, GL_FLOAT, GL_FALSE, stride, v);
glEnableVertexAttribArray(location);
}
}
}
void ofShader::setAttribute2fv(const string & name, const float* v, GLsizei stride) const{
if(bLoaded){
GLint location = getAttributeLocation(name);
if (location != -1) {
glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, stride, v);
glEnableVertexAttribArray(location);
}
}
}
void ofShader::setAttribute3fv(const string & name, const float* v, GLsizei stride) const{
if(bLoaded){
GLint location = getAttributeLocation(name);
if (location != -1) {
glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, stride, v);
glEnableVertexAttribArray(location);
}
}
}
void ofShader::setAttribute4fv(const string & name, const float* v, GLsizei stride) const{
if(bLoaded){
GLint location = getAttributeLocation(name);
if (location != -1) {
glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, stride, v);
glEnableVertexAttribArray(location);
}
}
}
#ifndef TARGET_OPENGLES
void ofShader::setAttribute1d(GLint location, double v1) const{
if(bLoaded)
glVertexAttrib1d(location, v1);
}
void ofShader::setAttribute2d(GLint location, double v1, double v2) const{
if(bLoaded)
glVertexAttrib2d(location, v1, v2);
}
void ofShader::setAttribute3d(GLint location, double v1, double v2, double v3) const{
if(bLoaded)
glVertexAttrib3d(location, v1, v2, v3);
}
void ofShader::setAttribute4d(GLint location, double v1, double v2, double v3, double v4) const{
if(bLoaded)
glVertexAttrib4d(location, v1, v2, v3, v4);
}
#endif
GLint ofShader::getAttributeLocation(const string & name) const{
return glGetAttribLocation(program, name.c_str());
}
GLint ofShader::getUniformLocation(const string & name) const{
if(!bLoaded) return -1;
auto it = uniformsCache.find(name);
if (it == uniformsCache.end()){
return -1;
} else {
return it->second;
}
}
#ifndef TARGET_OPENGLES
#ifdef GLEW_ARB_uniform_buffer_object
GLint ofShader::getUniformBlockIndex(const string & name) const{
if(!bLoaded) return -1;
if(GLEW_ARB_uniform_buffer_object) {
auto it = uniformBlocksCache.find(name);
if (it == uniformBlocksCache.end()){
return -1;
} else {
return it->second;
}
} else {
ofLogError("ofShader::getUniformBlockIndex") << "Sorry, it looks like you can't run 'ARB_uniform_buffer_object'";
return -1;
}
}
GLint ofShader::getUniformBlockBinding( const string & name ) const{
if(!bLoaded) return -1;
if(GLEW_ARB_uniform_buffer_object) {
GLint index = getUniformBlockIndex(name);
if (index == -1) return -1;
GLint blockBinding;
glGetActiveUniformBlockiv(program, index, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
return blockBinding;
} else {
ofLogError("ofShader::getUniformBlockBinding") << "Sorry, it looks like you can't run 'ARB_uniform_buffer_object'";
return -1;
}
}
void ofShader::printActiveUniformBlocks() const{
if(GLEW_ARB_uniform_buffer_object) {
GLint numUniformBlocks = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
ofLogNotice("ofShader") << numUniformBlocks << " uniform blocks";
GLint uniformBlockMaxLength = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformBlockMaxLength);
GLchar* uniformBlockName = new GLchar[uniformBlockMaxLength];
stringstream line;
for(GLint i = 0; i < numUniformBlocks; i++) {
GLsizei length;
GLint blockBinding;
GLsizei blockDataSize;
glGetActiveUniformBlockName(program, i, uniformBlockMaxLength, &length, uniformBlockName );
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding );
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize );
line << " [" << i << "] ";
for(int j = 0; j < length; j++) {
line << uniformBlockName[j];
}
line << " @ index " << getUniformBlockIndex( uniformBlockName ) << ", binding point " << blockBinding << ", size " << blockDataSize << " bytes";
ofLogNotice("ofShader") << line.str();
line.str("");
}
delete [] uniformBlockName;
} else {
ofLogError("ofShader::printActiveUniformBlocks") << "Sorry, it looks like you can't run 'ARB_uniform_buffer_object'";
}
}
void ofShader::bindUniformBlock(GLuint binding, const string & name) const{
if(bLoaded){
if(GLEW_ARB_uniform_buffer_object) {
GLint index = getUniformBlockIndex(name);
if (index != -1) {
glUniformBlockBinding( program, index, binding );
}
} else {
ofLogError("ofShader::bindUniformBlock") << "Sorry, it looks like you can't run 'ARB_uniform_buffer_object'";
}
}
}
#endif
#endif
void ofShader::printActiveUniforms() const{
GLint numUniforms = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
ofLogNotice("ofShader") << numUniforms << " uniforms";
GLint uniformMaxLength = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxLength);
GLint count = -1;
GLenum type = 0;
GLchar* uniformName = new GLchar[uniformMaxLength];
for(GLint i = 0; i < numUniforms; i++) {
stringstream line;
GLsizei length;
GLint location;
glGetActiveUniform(program, i, uniformMaxLength, &length, &count, &type, uniformName);
location = glGetUniformLocation(program, uniformName);
if (location == -1) continue;
line << "[" << location << "] ";
for(int j = 0; j < length; j++) {
line << uniformName[j];
}
line << " @ index " << location;
ofLogNotice("ofShader") << line.str();
}
delete[] uniformName;
}
void ofShader::printActiveAttributes() const{
GLint numAttributes = 0;
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numAttributes);
ofLogNotice("ofShader") << numAttributes << " attributes";
GLint attributeMaxLength = 0;
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attributeMaxLength);
GLint count = -1;
GLenum type = 0;
GLchar* attributeName = new GLchar[attributeMaxLength];
stringstream line;
for(GLint i = 0; i < numAttributes; i++) {
GLsizei length;
glGetActiveAttrib(program, i, attributeMaxLength, &length, &count, &type, attributeName);
line << " [" << i << "] ";
for(int j = 0; j < length; j++) {
line << attributeName[j];
}
line << " @ index " << getAttributeLocation(attributeName);
ofLogNotice("ofShader") << line.str();
line.str("");
}
delete [] attributeName;
}
GLuint ofShader::getProgram() const{
return program;
}
GLuint ofShader::getShader(GLenum type) const{
auto shader = shaders.find(type);
if(shader!=shaders.end()){
return shader->second.id;
}else{
return 0;
}
}
bool ofShader::operator==(const ofShader & other) const{
return other.program==program;
}
bool ofShader::operator!=(const ofShader & other) const{
return other.program!=program;
}
string ofShader::nameForType(GLenum type){
switch(type) {
case GL_VERTEX_SHADER: return "GL_VERTEX_SHADER";
case GL_FRAGMENT_SHADER: return "GL_FRAGMENT_SHADER";
#ifndef TARGET_OPENGLES
case GL_GEOMETRY_SHADER_EXT: return "GL_GEOMETRY_SHADER_EXT";
#ifdef glDispatchCompute
case GL_COMPUTE_SHADER: return "GL_COMPUTE_SHADER";
#endif
#endif
default: return "UNKNOWN SHADER TYPE";
}
}
Comments