diff --git a/GLProgram.cc b/GLProgram.cc new file mode 100644 index 0000000..6338b48 --- /dev/null +++ b/GLProgram.cc @@ -0,0 +1,99 @@ +#include "GLProgram.h" +#include +#include + +using namespace std; + +GLProgram::GLProgram() +{ + m_id = 0; +} + +GLProgram::~GLProgram() +{ + if (m_id > 0) + { + glDeleteProgram(m_id); + } +} + +bool GLProgram::create(const char *v_source, const char *f_source, ...) +{ + va_list va; + va_start(va, f_source); + bool rv = createv(v_source, f_source, va); + va_end(va); + return rv; +} + +bool GLProgram::create(const uint8_t *v_source, const uint8_t *f_source, ...) +{ + va_list va; + va_start(va, f_source); + bool rv = createv((const char *) v_source, (const char *) f_source, va); + va_end(va); + return rv; +} + +bool GLProgram::createv(const char *v_source, const char *f_source, va_list va) +{ + if (v_source == NULL || f_source == NULL) + return false; + if (!m_v_shader.create(GL_VERTEX_SHADER, v_source)) + { + cerr << "Failed vertex shader source:" << endl << v_source << endl; + return false; + } + if (!m_f_shader.create(GL_FRAGMENT_SHADER, f_source)) + { + cerr << "Failed fragment shader source:" << endl << f_source << endl; + return false; + } + m_id = glCreateProgram(); + if (m_id <= 0) + { + cerr << "Error allocating GL program object" << endl; + return false; + } + glAttachShader(m_id, m_v_shader.get_id()); + glAttachShader(m_id, m_f_shader.get_id()); + + for (;;) + { + const char *attribute_name = va_arg(va, const char *); + if (attribute_name == NULL) + break; + GLuint attribute_index = va_arg(va, uint32_t); + glBindAttribLocation(m_id, attribute_index, attribute_name); + } + + glLinkProgram(m_id); + + GLint link_status; + glGetProgramiv(m_id, GL_LINK_STATUS, &link_status); + if (link_status != GL_TRUE) + { + GLint log_length; + glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) + { + char *log = new char[log_length]; + glGetProgramInfoLog(m_id, log_length, &log_length, log); + cerr << "Program log:" << endl << log << endl; + delete[] log; + } + return false; + } + + for (;;) + { + const char *uniform_name = va_arg(va, const char *); + if (uniform_name == NULL) + break; + GLint loc = glGetUniformLocation(m_id, uniform_name); + m_uniform_locations.push_back(loc); + m_uniform_location_names[uniform_name] = loc; + } + + return true; +} diff --git a/GLProgram.h b/GLProgram.h new file mode 100644 index 0000000..cdd2ea3 --- /dev/null +++ b/GLProgram.h @@ -0,0 +1,30 @@ +#ifndef GLPROGRAM_H +#define GLPROGRAM_H + +#include +#include "GLShader.h" +#include +#include +#include + +class GLProgram +{ + public: + GLProgram(); + ~GLProgram(); + bool create(const char *v_source, const char *f_source, ...); + bool create(const uint8_t *v_source, const uint8_t *f_source, ...); + bool createv(const char *v_source, const char *f_source, va_list va); + GLuint get_id() { return m_id; } + void use() { glUseProgram(m_id); } + GLint uniform(int i) { return m_uniform_locations[i]; } + GLint uniform(const std::string & s) { return m_uniform_location_names[s]; } + protected: + GLuint m_id; + GLShader m_v_shader; + GLShader m_f_shader; + std::vector m_uniform_locations; + std::map m_uniform_location_names; +}; + +#endif diff --git a/GLShader.cc b/GLShader.cc new file mode 100644 index 0000000..3f99a9f --- /dev/null +++ b/GLShader.cc @@ -0,0 +1,59 @@ +#include "GLShader.h" +#include + +using namespace std; + +GLShader::GLShader() +{ + m_id = 0; +} + +GLShader::~GLShader() +{ + if (m_id > 0) + { + glDeleteShader(m_id); + } +} + +bool GLShader::create(GLenum shaderType, const char *source) +{ + GLint status; + + m_id = glCreateShader(shaderType); + if (m_id > 0) + { + glShaderSource(m_id, 1, &source, NULL); + + glCompileShader(m_id); + + glGetShaderiv(m_id, GL_COMPILE_STATUS, &status); + if (status == GL_TRUE) + { + return true; + } + + GLint log_length; + cerr << "Error compiling "; + switch (shaderType) + { + case GL_VERTEX_SHADER: + cerr << "vertex"; + break; + case GL_FRAGMENT_SHADER: + cerr << "fragment"; + break; + } + cerr << " shader" << endl; + glGetShaderiv(m_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) + { + char * log = new char[log_length]; + glGetShaderInfoLog(m_id, log_length, &log_length, log); + cerr << "Shader Log:" << endl << log << endl; + delete[] log; + } + glDeleteShader(m_id); + } + return false; +} diff --git a/GLShader.h b/GLShader.h new file mode 100644 index 0000000..b7578c5 --- /dev/null +++ b/GLShader.h @@ -0,0 +1,18 @@ +#ifndef GLSHADER_H +#define GLSHADER_H + +#include "gl3w.h" + +class GLShader +{ + public: + GLShader(); + ~GLShader(); + bool create(GLenum shaderType, const char *source); + GLuint get_id() { return m_id; } + bool valid() { return m_id > 0; } + protected: + GLuint m_id; +}; + +#endif diff --git a/Makefile b/Makefile index 2b484f9..2376de0 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CFLAGS := $(shell freetype-config --cflags) $(shell sdl2-config --cflags) CXXFLAGS := $(CFLAGS) LDFLAGS := -l$(GL_LIB) -ldl -lfreetype $(shell sdl2-config --libs) $(shell freetype-config --libs) -freetype2gl3: gl3w.o freetype2gl3.o +freetype2gl3: gl3w.o freetype2gl3.o GLShader.o GLProgram.o $(CXX) -o $@ $^ $(LDFLAGS) .PHONY: clean diff --git a/freetype2gl3.cc b/freetype2gl3.cc index 22a4bcc..9249789 100644 --- a/freetype2gl3.cc +++ b/freetype2gl3.cc @@ -3,6 +3,7 @@ #include #include #include FT_FREETYPE_H +#include "GLProgram.h" using namespace std; @@ -11,15 +12,76 @@ using namespace std; static FT_Library ft; static FT_Face face; +static GLProgram program; +static GLint uniform_tex; + +const char vertex_shader[] = +"#version 120\n" +"\n" +"attribute vec4 coord;\n" +"\n" +"varying vec2 tex_coord_i;\n" +"\n" +"void main(void)\n" +"{\n" +" gl_Position = vec4(coord.xy, 0, 1);\n" +" tex_coord_i = coord.zw;\n" +"}\n"; + +const char fragment_shader[] = +"varying vec2 tex_coord_i;\n" +"\n" +"uniform sampler2D tex;\n" +"uniform vec4 color;\n" +"\n" +"void main(void)\n" +"{\n" +" gl_FragColor = vec4(1, 1, 1, texture2D(tex, tex_coord_i).a) * color;\n" +"}\n"; void init(void) { glClearColor (0.0, 0.0, 0.0, 0.0); + program.create(vertex_shader, fragment_shader, + "coord", 0, NULL, + "tex", NULL); + uniform_tex = program.uniform("tex"); + program.use(); } void display(SDL_Window * window) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (FT_Load_Char(face, 'h', FT_LOAD_RENDER) != 0) + { + cerr << "Failed to load character" << endl; + } +#if 0 + GLuint texture_id; + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_ALPHA, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 0, + GL_ALPHA, + GL_UNSIGNED_BYTE, + face->glyph->bitmap.buffer); + GLuint vbo; + glGenBuffers(1, &vbo); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); + glUniform1i(uniform_tex, 0); +#endif SDL_GL_SwapWindow(window); }