Files
2018-07-17 14:15:37 +02:00

926 lines
25 KiB
C++

#include "render/shader.h"
#include "render/gl_shader.h"
#include "compat/gl.h"
#include "compat/misc.h"
#include "str_util.h"
#include "files.h"
#include "main/logging.h"
#include "main/references.h"
extern unsigned frameNumber;
namespace render {
void preProcessShader(std::string& shader) {
std::string output;
size_t start = 0;
while(start < shader.size()) {
size_t pos = shader.find('#', start);
if(pos == std::string::npos)
break;
if(pos != start)
output += shader.substr(start, pos-start);
if(shader.size() - pos > 3 && shader[pos+1] == '{' && shader[pos+2] == '{') {
size_t endpos = shader.find("}}", pos, 2);
if(endpos == std::string::npos)
break;
std::string value = shader.substr(pos+3, endpos-pos-3);
std::string replace;
if(value == "level:extreme") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 4) ? "true" : "false";
}
else if(value == "level:high") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 3) ? "true" : "false";
}
else if(value == "level:medium") {
auto* sl = devices.settings.engine.getSetting("iShaderLevel");
if(sl)
output += (sl->getInteger() >= 2) ? "true" : "false";
}
else if(value == "fallback") {
auto* fallback = devices.settings.engine.getSetting("bShaderFallback");
if(fallback)
output += (fallback->getBool()) ? "true" : "false";
}
else {
auto* setting = devices.settings.engine.getSetting(value.c_str());
if(!setting)
setting = devices.settings.mod.getSetting(value.c_str());
if(setting) {
switch(setting->type) {
case GT_Bool:
output += (setting->getBool()) ? "true" : "false";
break;
case GT_Integer:
case GT_Enum:
output += toString<int>(setting->getInteger());
break;
case GT_Double:
output += toString<double>(setting->getDouble());
break;
case GT_String:
output += *setting->getString();
break;
}
}
else {
error("ERROR: Could not find shader variable %s.", value.c_str());
}
}
start = endpos + 2;
}
else if(shader.size() - pos > 11 && shader.compare(pos, 10, "#include \"") == 0) {
size_t endpos = shader.find("\"", pos+10, 1);
if(endpos == std::string::npos)
break;
std::string value = shader.substr(pos+10, endpos-pos-10);
std::string fname = devices.mods.resolve(value);
if(!fileExists(fname)) {
error("ERROR COMPILING SHADER: Could not find include file %s. (Resolved to %s)", value.c_str(), fname.c_str());
}
else {
std::string includeContents = getFileContents(fname);
preProcessShader(includeContents);
output += includeContents;
}
start = endpos + 1;
}
else {
output += "#";
start = pos + 1;
}
}
if(start < shader.size())
output += shader.substr(start, shader.size()-start);
shader = output;
}
class GLShader;
class GLShaderProgram : public ShaderProgram {
public:
GLuint vertex_shader, fragment_shader, program;
std::string vertex_file, fragment_file;
std::unordered_map<GLuint, unsigned> cached_uniforms;
std::vector<float> mem_buffer;
const GLShader* lastShader;
GLShaderProgram(const std::string& vFile, const std::string& fFile)
: vertex_file(vFile), fragment_file(fFile), program(0), lastShader(0)
{
}
~GLShaderProgram() {
reset();
}
void reset() {
if(program != 0) {
glDeleteProgram(program);
program = 0;
}
mem_buffer.clear();
cached_uniforms.clear();
}
unsigned cacheUniform(GLuint position, size_t size) {
auto it = cached_uniforms.find(position);
if(it != cached_uniforms.end())
return it->second;
unsigned pos = (unsigned)mem_buffer.size();
cached_uniforms[position] = pos;
for(size_t i = 0; i < size; ++i)
mem_buffer.push_back(0);
return pos;
}
GLuint compileShader(GLenum type, const std::string& fname, const std::string& source) {
GLuint shader;
//Compile
const GLchar* str_ptr = (const GLchar*)source.c_str();
GLint str_len = (GLint)source.size();
shader = glCreateShader(type);
glShaderSource(shader, 1, &str_ptr, &str_len);
glCompileShader(shader);
//Check compile value
GLint shader_ok;
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
if(!shader_ok) {
error("Failed to compile shader: %s", fname.c_str());
GLint log_length;
char* log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
log = (char*)malloc(log_length);
glGetShaderInfoLog(shader, log_length, NULL, log);
error("%s", log);
free(log);
glDeleteShader(shader);
return 0;
}
else if(getLogLevel() == LL_Info) {
//Print warnings in verbose
GLint log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
//We're always provided at least an empty string, just ignore very small logs
if(log_length > 8) {
char* log;
log = (char*)malloc(log_length);
glGetShaderInfoLog(shader, log_length, NULL, log);
error("Shader '%s' has warnings:\n%s", fname.c_str(), log);
free(log);
}
}
return shader;
}
int compile() override {
reset();
//Compile vertex shader
{
auto contents = getFileContents(vertex_file);
preProcessShader(contents);
if(contents.empty()) {
error("Could not open vertex shader, or the file was empty");
return 1;
}
vertex_shader = compileShader(GL_VERTEX_SHADER, vertex_file, contents);
if(!vertex_shader)
return 1;
}
//Compile fragment shader
{
auto contents = getFileContents(fragment_file);
preProcessShader(contents);
fragment_shader = compileShader(GL_FRAGMENT_SHADER, fragment_file, contents);
if(!fragment_shader) {
if(contents.empty())
error("Could not open fragment shader, or the file was empty");
glDeleteShader(vertex_shader);
return 2;
}
}
//Link entire program
program = glCreateProgram();
glBindAttribLocation(program, 0, "in_vertex");
glBindAttribLocation(program, 2, "in_normal");
glBindAttribLocation(program, 1, "in_uv");
glBindAttribLocation(program, 3, "in_tangent");
glBindAttribLocation(program, 4, "in_color");
glBindAttribLocation(program, 5, "in_uv2");
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
//Make sure it linked correctly
GLint status;
glGetProgramiv(program,GL_LINK_STATUS,&status);
if(status == GL_FALSE) {
error("Linking failed:");
GLint log_length;
char* log;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
log = (char*)malloc(log_length);
glGetProgramInfoLog(program, log_length, NULL, log);
error("%s", log);
free(log);
glDeleteProgram(program);
program = 0;
return 3;
}
return 0;
}
};
class GLShader : public Shader {
public:
mutable unsigned lastBoundFrame;
std::vector<std::string> uniform_names;
std::vector<GLuint> uniform_locations;
std::vector<unsigned> uniform_vars;
std::vector<unsigned> uniform_cache;
GLShader()
{
program = 0;
constant = true;
dynamicFloats = 0;
lastBoundFrame = 0xffffffff;
}
~GLShader() {
reset();
}
void reset() {
uniform_locations.clear();
uniform_cache.clear();
uniform_vars.clear();
}
virtual int compile() {
reset();
if(!program) {
error("No associated program");
return 4;
}
auto glProgram = (GLShaderProgram*)program;
if(glProgram->program == 0)
return 4;
bool missingVars = false;
for(unsigned i = 0; i < uniform_names.size(); ++i) {
GLuint location = glGetUniformLocation(glProgram->program, uniform_names[i].c_str());
if(location == (GLuint)-1) {
//NOTE: This is no longer considered an error, because dynamic shader levels
//are going to be causing uniforms to be missing all the time.
//
//error("Unable to find uniform '%s'", uniform_names[i].c_str());
//missingVars = true;
}
else {
uniform_locations.push_back(location);
uniform_vars.push_back(i);
}
}
for(unsigned i = 0; i < uniform_locations.size(); ++i) {
auto& v = vars[uniform_vars[i]];
size_t size = 0;
switch(v.type) {
case VT_int: case VT_float: size = 1; break;
case VT_int2: case VT_float2: size = 2; break;
case VT_int3: case VT_float3: size = 3; break;
case VT_int4: case VT_float4: size = 4; break;
case VT_mat3: size = 9; break;
}
size *= v.count;
uniform_cache.push_back(glProgram->cacheUniform(uniform_locations[i], size));
}
if(missingVars)
return 4;
else
return 0;
}
virtual void addVariable(const std::string& name, const Variable& var) {
uniform_names.push_back(name);
Shader::addVariable(name, var);
}
virtual void bind(float* dynamic) const {
if(!program) {
glUseProgram(0);
return;
}
auto glProgram = (GLShaderProgram*)program;
if(auto programID = glProgram->program) {
glUseProgram(programID);
}
else {
glUseProgram(0);
return;
}
const bool bindConst = lastBoundFrame != frameNumber || glProgram->lastShader != this;
lastBoundFrame = frameNumber;
glProgram->lastShader = this;
if(dynamic) {
float* floats = dynamic;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(!bindConst && var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&glProgram->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_int2:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_int3:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_int4:
if(var.constant) {
if(var._intcall)
var._intcall(var._ints, var.count, var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_float:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_float2:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_float3:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_float4:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_mat3:
if(var.constant) {
if(var._floatcall)
var._floatcall(var._floats, var.count, var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
}
else if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, floats);
memcpy(uniformMem, floats, var.count * 36);
}
floats += var.count * 9;
}
NO_DEFAULT
}
}
}
else {
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(!bindConst && var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&glProgram->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
break;
case VT_int2:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
break;
case VT_int3:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
break;
case VT_int4:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
break;
case VT_float:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
break;
case VT_float2:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
break;
case VT_float3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
break;
case VT_float4:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
break;
case VT_mat3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
break;
NO_DEFAULT
}
}
}
}
void updateDynamicVars() const {
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&((GLShaderProgram*)program)->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 4);
}
break;
case VT_int2:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 8);
}
break;
case VT_int3:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 12);
}
break;
case VT_int4:
if(var._intcall)
var._intcall(var._ints,var.count,var._args);
if(memcmp(uniformMem, var._ints, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, var._ints);
memcpy(uniformMem, var._ints, var.count * 16);
}
break;
case VT_float:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 4);
}
break;
case VT_float2:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 8);
}
break;
case VT_float3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 12);
}
break;
case VT_float4:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, var._floats);
memcpy(uniformMem, var._floats, var.count * 16);
}
break;
case VT_mat3:
if(var._floatcall)
var._floatcall(var._floats,var.count,var._args);
if(memcmp(uniformMem, var._floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, var._floats);
memcpy(uniformMem, var._floats, var.count * 36);
}
break;
NO_DEFAULT
}
}
}
void saveDynamicVars(float* buffer) const {
float* floats = buffer;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count;
}
break;
case VT_int2:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 2;
}
break;
case VT_int3:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 3;
}
break;
case VT_int4:
if(var._intcall) {
var._intcall((int*)floats,var.count,var._args);
floats += var.count * 4;
}
break;
case VT_float:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count;
}
break;
case VT_float2:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 2;
}
break;
case VT_float3:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 3;
}
break;
case VT_float4:
if(var._floatcall) {
var._floatcall(floats,var.count,var._args);
floats += var.count * 4;
}
break;
case VT_mat3:
if(var._floatcall) {
var._floatcall(floats,var.constant,var._args);
floats += var.constant * 9;
}
NO_DEFAULT
}
}
}
void loadDynamicVars(float* buffer) const {
float* floats = buffer;
for(size_t i = 0, cnt = uniform_locations.size(); i < cnt; ++i) {
const Shader::Variable& var = vars[uniform_vars[i]];
if(var.constant)
continue;
GLuint uniform = uniform_locations[i];
void* uniformMem = (void*)&((GLShaderProgram*)program)->mem_buffer[uniform_cache[i]];
switch(var.type) {
case VT_invalid:
break;
case VT_int:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_int2:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_int3:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_int4:
if(var._intcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4iv(uniform, var.count, (int*)floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_float:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 4) != 0) {
glUniform1fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 4);
}
floats += var.count;
}
break;
case VT_float2:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 8) != 0) {
glUniform2fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 8);
}
floats += var.count * 2;
}
break;
case VT_float3:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 12) != 0) {
glUniform3fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 12);
}
floats += var.count * 3;
}
break;
case VT_float4:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 16) != 0) {
glUniform4fv(uniform, var.count, floats);
memcpy(uniformMem, floats, var.count * 16);
}
floats += var.count * 4;
}
break;
case VT_mat3:
if(var._floatcall) {
if(memcmp(uniformMem, floats, var.count * 36) != 0) {
glUniformMatrix3fv(uniform, var.count, false, floats);
memcpy(uniformMem, floats, var.count * 36);
}
floats += var.count * 9;
}
break;
NO_DEFAULT
}
}
}
};
Shader* createGLShader() {
return new GLShader();
}
ShaderProgram* createGLShaderProgram(const char* vertex_shader, const char* fragment_shader) {
return new GLShaderProgram(vertex_shader, fragment_shader);
}
};