diff --git a/MSVC/build/ogl.vcxproj b/MSVC/build/ogl.vcxproj
index 8a79912c8b..39f2393d36 100644
--- a/MSVC/build/ogl.vcxproj
+++ b/MSVC/build/ogl.vcxproj
@@ -276,6 +276,7 @@
%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+
@@ -292,6 +293,7 @@
+
diff --git a/MSVC/build/ogl.vcxproj.filters b/MSVC/build/ogl.vcxproj.filters
index b4142034a9..10ca6ef813 100644
--- a/MSVC/build/ogl.vcxproj.filters
+++ b/MSVC/build/ogl.vcxproj.filters
@@ -25,6 +25,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -69,6 +72,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/include/Makefile.am b/include/Makefile.am
index 566a261e98..242becac51 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -105,6 +105,7 @@ noinst_HEADERS = \
ServerItem.h \
ServerList.h \
ServerListCache.h \
+ Shader.h \
ShotUpdate.h \
Singleton.h \
SphereObstacle.h \
diff --git a/include/Shader.h b/include/Shader.h
new file mode 100644
index 0000000000..f40709fe20
--- /dev/null
+++ b/include/Shader.h
@@ -0,0 +1,76 @@
+/* bzflag
+ * Copyright (c) 2019-2019 Tim Riker
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the license found in the file
+ * named COPYING that should have accompanied this file.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma once
+
+// Before everything
+#include "common.h"
+
+// Syste headers
+#include
+#include
+#include
+
+// Common headers
+#include "bzfgl.h"
+
+extern bool shaderDestroyed;
+
+class Shader
+{
+public:
+ Shader(const char *shaderName);
+ virtual ~Shader();
+
+// Called to initialize/destroy the shader
+ virtual void init();
+ virtual void destroy();
+
+ GLint getAttribLocation(const char *attribute_name);
+ GLint getUniformLocation(const char *uniform_name);
+ void push();
+ void pop();
+ void setUniform(GLint uniform, bool value);
+ void setUniform(GLint uniform, int value);
+ void setUniform(GLint uniform, float value);
+ void setUniform(GLint uniform, const glm::vec2 &value);
+ void setUniform(GLint uniform, const glm::vec3 &value);
+ void setUniform(GLint uniform, const glm::vec4 &value);
+ void setUniform(GLint uniform, int count, GLint *value);
+ void setUniform(GLint uniform, int count, const glm::vec3 value[]);
+ void setUniform(GLint uniform, int count, const glm::vec4 value[]);
+protected:
+ bool inited;
+private:
+// Those are callback for GL Context creation/free
+// Called by OpenGLGState::
+ static void initContext(void* data);
+ static void freeContext(void* data);
+
+ static std::deque programStack;
+
+ GLuint program;
+
+ GLuint create_shader(const char *filename, GLenum type);
+ void print_log(GLuint object);
+ char* file_read(const std::string &filename);
+ std::string vertexShader;
+ std::string fragmentShader;
+};
+
+// Local Variables: ***
+// mode: C++ ***
+// tab-width: 4 ***
+// c-basic-offset: 4 ***
+// indent-tabs-mode: nil ***
+// End: ***
+// ex: shiftwidth=4 tabstop=4
diff --git a/src/ogl/Makefile.am b/src/ogl/Makefile.am
index 1968e74d9d..6762dab4a0 100644
--- a/src/ogl/Makefile.am
+++ b/src/ogl/Makefile.am
@@ -23,6 +23,7 @@ libGLKit_la_SOURCES = \
OpenGLTexture.cxx \
OpenGLUtils.cxx \
RenderNode.cxx \
+ Shader.cxx \
Vertex_Chunk.cxx \
VBO_Element.h \
VBO_Element.cxx \
diff --git a/src/ogl/Shader.cxx b/src/ogl/Shader.cxx
new file mode 100644
index 0000000000..3e34522e1a
--- /dev/null
+++ b/src/ogl/Shader.cxx
@@ -0,0 +1,303 @@
+/* bzflag
+ * Copyright (c) 2019-2019 Tim Riker
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the license found in the file
+ * named COPYING that should have accompanied this file.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+// Interface
+#include "Shader.h"
+
+// System headers
+#include
+#include
+#include
+#include
+
+// Common header
+#include "ErrorHandler.h"
+#include "FileManager.h"
+#include "OpenGLGState.h"
+
+bool shaderDestroyed = false;
+
+std::deque Shader::programStack;
+Shader::Shader(const char *shaderName) : inited(false)
+{
+ vertexShader = std::string(shaderName) + ".vert";
+ fragmentShader = std::string(shaderName) + ".frag";
+ OpenGLGState::registerContextInitializer(freeContext, initContext, this);
+}
+
+Shader::~Shader()
+{
+ OpenGLGState::unregisterContextInitializer(freeContext, initContext, this);
+ shaderDestroyed = true;
+}
+
+void Shader::initContext(void*)
+{
+}
+
+void Shader::freeContext(void* data)
+{
+ static_cast(data)->destroy();
+}
+
+void Shader::init()
+{
+ GLuint vs, fs;
+ GLint link_ok = GL_FALSE;
+ vs = create_shader(vertexShader.c_str(), GL_VERTEX_SHADER);
+ fs = create_shader(fragmentShader.c_str(), GL_FRAGMENT_SHADER);
+ if (!vs && !fs)
+ {
+ printFatalError ("glsl file for shader %s and %s not found",
+ vertexShader.c_str(), fragmentShader.c_str());
+ exit(0);
+ }
+
+ program = glCreateProgram();
+ if (vs)
+ glAttachShader(program, vs);
+ if (fs)
+ glAttachShader(program, fs);
+ glLinkProgram(program);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
+ if (!link_ok)
+ {
+ std::cerr << "glLinkProgram:";
+ print_log(program);
+ exit(0);
+ }
+ inited = true;
+}
+
+void Shader::destroy()
+{
+ inited = false;
+ glDeleteProgram(program);
+}
+
+GLuint Shader::create_shader(const char* filename, GLenum type)
+{
+ GLchar* source = file_read(filename);
+ if (source == NULL)
+ return 0;
+
+ GLuint res = glCreateShader(type);
+
+ const GLchar* sources[] = { source };
+ glShaderSource(res, 1, sources, NULL);
+ free(source);
+
+ glCompileShader(res);
+ GLint compile_ok = GL_FALSE;
+ glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
+ if (compile_ok == GL_FALSE)
+ {
+ std::cerr << filename << ":";
+ print_log(res);
+ glDeleteShader(res);
+ return 0;
+ }
+
+ return res;
+}
+
+char* Shader::file_read(const std::string &filename)
+{
+ std::istream *rw = FILEMGR.createDataInStream(filename, true);
+ if (rw == NULL) return NULL;
+
+ // get the shader size
+ rw->seekg(0, std::ios::end);
+ std::streampos size = rw->tellg();
+ unsigned long res_size = (unsigned long)std::streamoff(size);
+
+ // load the shader
+ rw->seekg(0);
+
+ char* res = (char*)malloc(res_size + 1);
+
+ rw->read(res, res_size);
+ delete rw;
+ res[res_size] = '\0';
+ return res;
+}
+
+/*
+ * Display compilation errors from the OpenGL shader compiler
+ */
+void Shader::print_log(GLuint object)
+{
+ GLint log_length = 0;
+ if (glIsShader(object))
+ glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
+
+ else if (glIsProgram(object))
+ glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
+
+ else
+ {
+ std::cerr << "printlog: Not a shader or a program" << std::endl;
+ return;
+ }
+
+ char* log = (char*)malloc(log_length);
+
+ if (glIsShader(object))
+ glGetShaderInfoLog(object, log_length, NULL, log);
+ else if (glIsProgram(object))
+ glGetProgramInfoLog(object, log_length, NULL, log);
+
+ std::cerr << log;
+ free(log);
+}
+
+GLint Shader::getAttribLocation(const char *attribute_name)
+{
+ GLint attrib = glGetAttribLocation(program, attribute_name);
+ if (attrib == -1)
+ std::cerr << "Could not bind attribute " << attribute_name << std::endl;
+ return attrib;
+}
+
+GLint Shader::getUniformLocation(const char *uniform_name)
+{
+ GLint uniform = glGetUniformLocation(program, uniform_name);
+ if (uniform == -1)
+ std::cerr << "Could not bind uniform " << uniform_name << std::endl;
+ return uniform;
+}
+
+void Shader::setUniform(GLint uniform, bool value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform1i(uniform, value);
+}
+
+void Shader::setUniform(GLint uniform, float value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform1f(uniform, value);
+}
+
+void Shader::setUniform(GLint uniform, int value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform1i(uniform, value);
+}
+
+void Shader::setUniform(GLint uniform, const glm::vec2 &value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform2f(uniform, value[0], value[1]);
+}
+
+void Shader::setUniform(GLint uniform, const glm::vec3 &value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform3f(uniform, value[0], value[1], value[2]);
+}
+
+void Shader::setUniform(GLint uniform, const glm::vec4 &value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform4f(uniform, value[0], value[1], value[2], value[3]);
+}
+
+void Shader::setUniform(GLint uniform, int count, GLint *value)
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform1iv(uniform, count, value);
+}
+
+void Shader::setUniform(GLint uniform, int count, const glm::vec3 value[])
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform3fv(uniform, count, glm::value_ptr(value[0]));
+}
+
+void Shader::setUniform(GLint uniform, int count, const glm::vec4 value[])
+{
+ if (!inited)
+ return;
+ if (programStack.empty())
+ return;
+ if (program != programStack.back())
+ return;
+ glUniform4fv(uniform, count, glm::value_ptr(value[0]));
+}
+
+void Shader::push()
+{
+ if (!inited)
+ init();
+ programStack.push_back(program);
+ glUseProgram(program);
+}
+
+void Shader::pop()
+{
+ programStack.pop_back();
+ if (programStack.empty())
+ glUseProgram(0);
+ else
+ glUseProgram(programStack.back());
+}
+
+// Local Variables: ***
+// mode: C++ ***
+// tab-width: 4 ***
+// c-basic-offset: 4 ***
+// indent-tabs-mode: nil ***
+// End: ***
+// ex: shiftwidth=4 tabstop=4