2011-08-03 20:05:33 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
|
|
|
|
This file is part of Aquaria.
|
|
|
|
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
2013-05-10 20:22:35 +00:00
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
#include "Shader.h"
|
2013-05-11 22:05:57 +00:00
|
|
|
#include "algorithmx.h"
|
2016-07-09 02:18:40 +00:00
|
|
|
#include "RenderBase.h"
|
|
|
|
#include <sstream>
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2012-02-05 19:26:23 +00:00
|
|
|
bool Shader::_wasInited = false;
|
|
|
|
bool Shader::_useShaders = false;
|
|
|
|
|
|
|
|
void Shader::staticInit()
|
|
|
|
{
|
|
|
|
if (_wasInited)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_wasInited = true;
|
|
|
|
debugLog("Initializing shaders...");
|
|
|
|
|
2021-01-12 01:45:41 +00:00
|
|
|
bool use = true;
|
2012-02-05 19:26:23 +00:00
|
|
|
|
2022-04-07 00:38:39 +00:00
|
|
|
#ifndef BBGE_BUILD_OPENGL_STATIC
|
2021-01-12 01:45:41 +00:00
|
|
|
if( !glCreateProgramObjectARB || !glDeleteObjectARB || !glUseProgramObjectARB ||
|
|
|
|
!glCreateShaderObjectARB || !glCreateShaderObjectARB || !glCompileShaderARB ||
|
|
|
|
!glGetObjectParameterivARB || !glAttachObjectARB || !glGetInfoLogARB ||
|
|
|
|
!glLinkProgramARB || !glGetUniformLocationARB || !glGetActiveUniformARB ||
|
|
|
|
!glUniform1fvARB || !glUniform2fvARB || !glUniform3fvARB || !glUniform4fvARB ||
|
|
|
|
!glUniform1ivARB || !glUniform2ivARB || !glUniform3ivARB || !glUniform4ivARB)
|
2012-02-05 19:26:23 +00:00
|
|
|
{
|
2021-01-12 01:45:41 +00:00
|
|
|
glCreateProgramObjectARB = 0;
|
|
|
|
debugLog("One or more GL_ARB_shader_objects functions were not found");
|
|
|
|
use = false;
|
2012-02-05 19:26:23 +00:00
|
|
|
}
|
2021-01-02 01:14:15 +00:00
|
|
|
#endif
|
2012-02-05 19:26:23 +00:00
|
|
|
|
2021-01-12 01:45:41 +00:00
|
|
|
_useShaders = use;
|
|
|
|
if (use)
|
2012-02-05 19:26:23 +00:00
|
|
|
debugLog("Shader support enabled.");
|
|
|
|
else
|
|
|
|
debugLog("Shader support not enabled.");
|
|
|
|
}
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
Shader::Shader()
|
|
|
|
{
|
2013-05-11 22:05:57 +00:00
|
|
|
numUniforms = -1;
|
|
|
|
uniformsDirty = false;
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
g_programObj = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Shader::~Shader()
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
|
|
|
unload();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Shader::unload()
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2012-02-05 19:26:23 +00:00
|
|
|
if (!_useShaders)
|
|
|
|
return;
|
2011-08-03 20:05:33 +00:00
|
|
|
if (g_programObj)
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
2011-08-03 20:05:33 +00:00
|
|
|
glDeleteObjectARB( g_programObj );
|
2013-05-10 20:22:35 +00:00
|
|
|
g_programObj = 0;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
bool Shader::isLoaded() const
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2013-05-10 20:22:35 +00:00
|
|
|
return g_programObj != 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Shader::reload()
|
|
|
|
{
|
2013-10-24 23:44:34 +00:00
|
|
|
if (vertFile.size() || fragFile.size())
|
|
|
|
load(vertFile, fragFile);
|
|
|
|
else
|
|
|
|
loadSrc(vertSrc.c_str(), fragSrc.c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Shader::bind()
|
|
|
|
{
|
2012-02-05 19:26:23 +00:00
|
|
|
if (!_useShaders)
|
|
|
|
return;
|
2013-05-10 20:22:35 +00:00
|
|
|
glUseProgramObjectARB(g_programObj);
|
2013-05-11 22:05:57 +00:00
|
|
|
_flushUniforms();
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Shader::unbind()
|
|
|
|
{
|
2012-02-05 19:26:23 +00:00
|
|
|
if (!_useShaders)
|
|
|
|
return;
|
2013-05-10 20:22:35 +00:00
|
|
|
glUseProgramObjectARB(0);
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
unsigned int Shader::_compileShader(int type, const char *src, char *errbuf, size_t errbufsize)
|
|
|
|
{
|
|
|
|
GLint compiled = 0;
|
|
|
|
GLhandleARB handle = glCreateShaderObjectARB(type);
|
2013-06-15 00:38:49 +00:00
|
|
|
if(!handle)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Failed to create shader object of type " << type;
|
|
|
|
debugLog(os.str());
|
|
|
|
return 0;
|
|
|
|
}
|
2013-05-10 20:22:35 +00:00
|
|
|
|
|
|
|
glShaderSourceARB( handle, 1, &src, NULL );
|
|
|
|
glCompileShaderARB( handle);
|
|
|
|
|
|
|
|
glGetObjectParameterivARB(handle, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);
|
|
|
|
glGetInfoLogARB(handle, errbufsize, NULL, errbuf);
|
|
|
|
if(!compiled)
|
|
|
|
{
|
|
|
|
glDeleteObjectARB(handle);
|
|
|
|
handle = 0;
|
|
|
|
}
|
|
|
|
GLint err = glGetError();
|
|
|
|
if(err != GL_NO_ERROR)
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Shader::_compileShader: Unexpected error " << err;
|
|
|
|
errorLog(os.str());
|
|
|
|
}
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
void Shader::load(const std::string &file, const std::string &fragFile)
|
|
|
|
{
|
2012-02-05 19:26:23 +00:00
|
|
|
staticInit();
|
|
|
|
if(!_useShaders)
|
|
|
|
return;
|
|
|
|
|
|
|
|
debugLog("Shader::load("+file+", "+fragFile+")");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
this->vertFile = file;
|
|
|
|
this->fragFile = fragFile;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2022-03-31 19:03:40 +00:00
|
|
|
char *vertCode = file.length() ? readFile(file.c_str()) : NULL;
|
|
|
|
char *fragCode = fragFile.length() ? readFile(fragFile.c_str()) : NULL;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
loadSrc(vertCode, fragCode);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
delete [] vertCode;
|
|
|
|
delete [] fragCode;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
void Shader::loadSrc(const char *vertCode, const char *fragCode)
|
|
|
|
{
|
2013-05-11 22:05:57 +00:00
|
|
|
staticInit();
|
2013-05-10 20:22:35 +00:00
|
|
|
unload();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
if(!_useShaders)
|
|
|
|
return;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
char str[4096];
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
GLhandleARB vertexShader = 0;
|
|
|
|
GLhandleARB fragmentShader = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
//
|
|
|
|
// Create the vertex shader...
|
|
|
|
//
|
2013-05-11 22:05:57 +00:00
|
|
|
if(vertCode && *vertCode && !(vertexShader = _compileShader(GL_VERTEX_SHADER_ARB, vertCode, str, sizeof(str))))
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Vertex Shader Compile Error [" << vertFile << "]:\n" << str;
|
|
|
|
errorLog(os.str());
|
|
|
|
return;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
//
|
|
|
|
// Create the fragment shader...
|
|
|
|
//
|
2013-05-11 22:05:57 +00:00
|
|
|
if(fragCode && *fragCode && !(fragmentShader = _compileShader(GL_FRAGMENT_SHADER_ARB, fragCode, str, sizeof(str))))
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Fragment Shader Compile Error [" << fragFile << "]:\n" << str;
|
|
|
|
errorLog(os.str());
|
|
|
|
return;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
//
|
|
|
|
// Create a program object and attach the two compiled shaders...
|
|
|
|
//
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
g_programObj = glCreateProgramObjectARB();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
if (!(g_programObj && (vertexShader || fragmentShader)))
|
|
|
|
{
|
|
|
|
errorLog("programObj / vertexShader / fragmentShader problem");
|
|
|
|
unload();
|
|
|
|
return;
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
//
|
|
|
|
// Link the program object and print out the info log...
|
|
|
|
//
|
|
|
|
if(vertexShader)
|
|
|
|
glAttachObjectARB( g_programObj, vertexShader );
|
|
|
|
if(fragmentShader)
|
|
|
|
glAttachObjectARB( g_programObj, fragmentShader );
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
glLinkProgramARB( g_programObj );
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
// Shader objects will be deleted as soon as the program object is deleted
|
|
|
|
if(vertexShader)
|
|
|
|
glDeleteObjectARB(vertexShader);
|
|
|
|
if(fragmentShader)
|
|
|
|
glDeleteObjectARB(fragmentShader);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
GLint bLinked;
|
|
|
|
glGetObjectParameterivARB( g_programObj, GL_OBJECT_LINK_STATUS_ARB, &bLinked );
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
2013-05-10 20:22:35 +00:00
|
|
|
if(!bLinked)
|
2011-08-03 20:05:33 +00:00
|
|
|
{
|
2013-05-10 20:22:35 +00:00
|
|
|
glGetInfoLogARB( g_programObj, sizeof(str), NULL, str );
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "Shader Linking Error: " << str;
|
|
|
|
errorLog(os.str());
|
|
|
|
unload();
|
|
|
|
return;
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
2013-05-10 20:22:35 +00:00
|
|
|
|
2013-10-24 23:44:34 +00:00
|
|
|
vertSrc = vertCode ? vertCode : "";
|
|
|
|
fragSrc = fragCode ? fragCode : "";
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
_queryUniforms();
|
2013-05-10 20:22:35 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
void Shader::_setUniform(Uniform *u)
|
|
|
|
{
|
|
|
|
switch(u->type)
|
|
|
|
{
|
2017-01-12 21:51:46 +00:00
|
|
|
case GL_FLOAT: glUniform1fvARB(u->location, 1, u->data.f.f); break;
|
|
|
|
case GL_FLOAT_VEC2_ARB: glUniform2fvARB(u->location, 1, u->data.f.f); break;
|
|
|
|
case GL_FLOAT_VEC3_ARB: glUniform3fvARB(u->location, 1, u->data.f.f); break;
|
|
|
|
case GL_FLOAT_VEC4_ARB: glUniform4fvARB(u->location, 1, u->data.f.f); break;
|
|
|
|
case GL_INT: glUniform1ivARB(u->location, 1, u->data.i.i); break;
|
|
|
|
case GL_INT_VEC2_ARB: glUniform2ivARB(u->location, 1, u->data.i.i); break;
|
|
|
|
case GL_INT_VEC3_ARB: glUniform3ivARB(u->location, 1, u->data.i.i); break;
|
|
|
|
case GL_INT_VEC4_ARB: glUniform4ivARB(u->location, 1, u->data.i.i); break;
|
2013-05-11 22:05:57 +00:00
|
|
|
}
|
|
|
|
u->dirty = false;
|
|
|
|
}
|
2013-05-10 20:22:35 +00:00
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
void Shader::_flushUniforms()
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
2013-05-11 22:05:57 +00:00
|
|
|
if(!uniformsDirty)
|
|
|
|
return;
|
|
|
|
uniformsDirty = false;
|
|
|
|
|
|
|
|
for(size_t i = 0; i < uniforms.size(); ++i)
|
|
|
|
{
|
|
|
|
Uniform &u = uniforms[i];
|
|
|
|
if(u.dirty)
|
|
|
|
_setUniform(&u);
|
|
|
|
}
|
2013-05-10 20:22:35 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
// for sorting
|
|
|
|
bool Shader::_sortUniform(const Uniform& a, const char *bname)
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
2013-05-11 22:05:57 +00:00
|
|
|
return strcmp(a.name, bname) < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Shader::Uniform::operator< (const Uniform& b) const
|
|
|
|
{
|
|
|
|
return Shader::_sortUniform(*this, &b.name[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Shader::_queryUniforms()
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
glGetObjectParameterivARB(g_programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB , (GLint*)&numUniforms);
|
2013-05-11 22:05:57 +00:00
|
|
|
|
2017-01-14 19:10:42 +00:00
|
|
|
if (numUniforms == 0 || numUniforms == -1)
|
2013-10-24 23:44:34 +00:00
|
|
|
{
|
|
|
|
uniforms.clear();
|
2013-05-10 20:22:35 +00:00
|
|
|
return;
|
2013-10-24 23:44:34 +00:00
|
|
|
}
|
2013-05-11 22:05:57 +00:00
|
|
|
|
|
|
|
uniforms.reserve(numUniforms);
|
2013-10-24 23:44:34 +00:00
|
|
|
size_t total = 0;
|
2013-05-11 22:05:57 +00:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < numUniforms; ++i)
|
|
|
|
{
|
|
|
|
Uniform u;
|
|
|
|
GLint size = 0;
|
|
|
|
GLenum type = 0;
|
|
|
|
glGetActiveUniformARB(g_programObj, i, sizeof(u.name), NULL, &size, &type, &u.name[0]);
|
|
|
|
if(!type || !size)
|
|
|
|
continue;
|
|
|
|
u.location = glGetUniformLocationARB(g_programObj, u.name);
|
|
|
|
if(u.location == -1)
|
|
|
|
continue;
|
2013-10-24 23:44:34 +00:00
|
|
|
|
|
|
|
bool add = total >= uniforms.size();
|
|
|
|
if(add || type != u.type) // keep data intact on reload
|
|
|
|
memset(&u.data, 0, sizeof(u.data));
|
|
|
|
u.dirty = true;
|
2013-05-11 22:05:57 +00:00
|
|
|
u.type = type;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
2013-10-24 23:44:34 +00:00
|
|
|
if(add)
|
|
|
|
uniforms.push_back(u);
|
|
|
|
else
|
|
|
|
uniforms[total] = u;
|
2013-05-11 22:05:57 +00:00
|
|
|
|
2013-10-24 23:44:34 +00:00
|
|
|
++total;
|
2013-05-11 22:05:57 +00:00
|
|
|
}
|
|
|
|
|
2013-10-24 23:44:34 +00:00
|
|
|
uniforms.resize(total);
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
// sort to be able to do binary search later
|
|
|
|
std::sort(uniforms.begin(), uniforms.end());
|
2013-10-24 23:44:34 +00:00
|
|
|
|
|
|
|
uniformsDirty = true;
|
2013-05-11 22:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int Shader::_getUniformIndex(const char *name)
|
|
|
|
{
|
|
|
|
// binary search
|
|
|
|
UniformVec::iterator it = stdx_fg::lower_bound(uniforms.begin(), uniforms.end(), name, _sortUniform);
|
2013-05-14 23:56:50 +00:00
|
|
|
// because lower_bound returns the first element that compares less, it might not be the correct one
|
|
|
|
if(it != uniforms.end() && strcmp(it->name, name))
|
|
|
|
return -1;
|
2013-05-11 22:05:57 +00:00
|
|
|
return int(it - uniforms.begin());
|
2011-08-03 20:05:33 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
void Shader::setInt(const char *name, int x, int y /* = 0 */, int z /* = 0 */, int w /* = 0 */)
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
2017-01-14 19:10:42 +00:00
|
|
|
if(!g_programObj || numUniforms == 0 || numUniforms == -1)
|
2013-05-10 20:22:35 +00:00
|
|
|
return;
|
2013-05-11 22:05:57 +00:00
|
|
|
int idx = _getUniformIndex(name);
|
|
|
|
if(unsigned(idx) >= uniforms.size())
|
|
|
|
return;
|
|
|
|
Uniform& u = uniforms[idx];
|
2017-01-12 21:51:46 +00:00
|
|
|
u.data.i.i[0] = x;
|
|
|
|
u.data.i.i[1] = y;
|
|
|
|
u.data.i.i[2] = z;
|
|
|
|
u.data.i.i[3] = w;
|
2013-05-11 22:05:57 +00:00
|
|
|
u.dirty = true;
|
|
|
|
uniformsDirty = true;
|
2013-05-10 20:22:35 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 22:05:57 +00:00
|
|
|
void Shader::setFloat(const char *name, float x, float y /* = 0 */, float z /* = 0 */, float w /* = 0 */)
|
2013-05-10 20:22:35 +00:00
|
|
|
{
|
2017-01-14 19:10:42 +00:00
|
|
|
if(!g_programObj || numUniforms == 0 || numUniforms == -1)
|
2013-05-10 20:22:35 +00:00
|
|
|
return;
|
2013-05-11 22:05:57 +00:00
|
|
|
int idx = _getUniformIndex(name);
|
|
|
|
if(unsigned(idx) >= uniforms.size())
|
|
|
|
return;
|
|
|
|
Uniform& u = uniforms[idx];
|
2017-01-12 21:51:46 +00:00
|
|
|
u.data.f.f[0] = x;
|
|
|
|
u.data.f.f[1] = y;
|
|
|
|
u.data.f.f[2] = z;
|
|
|
|
u.data.f.f[3] = w;
|
2013-05-11 22:05:57 +00:00
|
|
|
u.dirty = true;
|
|
|
|
uniformsDirty = true;
|
2013-05-10 20:22:35 +00:00
|
|
|
}
|