commit 222bb87fed0b423a5e171c5d581c6349357fdc62
parent 5e284f4b138ff09ec9ceba6c2571341130f9bdf5
Author: kyle <kyle@getaddrinfo.net>
Date: Tue, 27 Oct 2015 21:05:01 -0600
viewer: get demo code working
Diffstat:
7 files changed, 438 insertions(+), 59 deletions(-)
diff --git a/viewer/.viewer.cpp.swp b/viewer/.viewer.cpp.swp
Binary files differ.
diff --git a/viewer/Makefile b/viewer/Makefile
@@ -1,4 +1,19 @@
-CXXFLAGS += -I/usr/X11R6/include -I/usr/X11R6/include/freetype2
+CXXFLAGS += -I/usr/X11R6/include
+CXXFLAGS += -I/usr/X11R6/include/freetype2
CXXFLAGS += -I/usr/X11R6/include/libdrm
+CXXFLAGS += -I/usr/local/include
+LDLIBS += -L/usr/local/lib -L/usr/X11R6/lib -lGL -lGLU -lGLEW
+LDLIBS += -lfreetype -lz -lglut
-viewer: viewer.o
+BIN = viewer
+SRCS = viewer.cpp shader_utils.cpp
+OBJS = $(SRCS:cpp=o)
+
+$(BIN): $(OBJS)
+ $(CXX) -o $(BIN) $(LDLIBS) $(OBJS)
+
+clean:
+ rm $(OBJS) $(BIN)
+
+depend:
+ mkdep $(CXXFLAGS) $(SRCS)
diff --git a/viewer/shader_utils.cpp b/viewer/shader_utils.cpp
@@ -0,0 +1,213 @@
+/**
+ * From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming
+ * This file is in the public domain.
+ * Contributors: Sylvain Beucler
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+
+/**
+ * Store all the file's contents in memory, useful to pass shaders
+ * source code to OpenGL
+ */
+char* file_read(const char* filename)
+{
+ FILE* in = fopen(filename, "rb");
+ if (in == NULL) return NULL;
+
+ int res_size = BUFSIZ;
+ char* res = (char*)malloc(res_size);
+ int nb_read_total = 0;
+
+ while (!feof(in) && !ferror(in)) {
+ if (nb_read_total + BUFSIZ > res_size) {
+ if (res_size > 10*1024*1024) break;
+ res_size = res_size * 2;
+ res = (char*)realloc(res, res_size);
+ }
+ char* p_res = res + nb_read_total;
+ nb_read_total += fread(p_res, 1, BUFSIZ, in);
+ }
+
+ fclose(in);
+ res = (char*)realloc(res, nb_read_total + 1);
+ res[nb_read_total] = '\0';
+ return res;
+}
+
+/**
+ * Display compilation errors from the OpenGL shader compiler
+ */
+void 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 {
+ fprintf(stderr, "printlog: Not a shader or a program\n");
+ 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);
+
+ fprintf(stderr, "%s", log);
+ free(log);
+}
+
+/**
+ * Compile the shader from file 'filename', with error handling
+ */
+GLuint create_shader(const char* filename, GLenum type)
+{
+ const GLchar* source = file_read(filename);
+ if (source == NULL) {
+ fprintf(stderr, "Error opening %s: ", filename); perror("");
+ return 0;
+ }
+ GLuint res = glCreateShader(type);
+ const GLchar* sources[] = {
+ // Define GLSL version
+#ifdef GL_ES_VERSION_2_0
+ "#version 100\n" // OpenGL ES 2.0
+#else
+ "#version 120\n" // OpenGL 2.1
+#endif
+ ,
+ // GLES2 precision specifiers
+#ifdef GL_ES_VERSION_2_0
+ // Define default float precision for fragment shaders:
+ (type == GL_FRAGMENT_SHADER) ?
+ "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
+ "precision highp float; \n"
+ "#else \n"
+ "precision mediump float; \n"
+ "#endif \n"
+ : ""
+ // Note: OpenGL ES automatically defines this:
+ // #define GL_ES
+#else
+ // Ignore GLES 2 precision specifiers:
+ "#define lowp \n"
+ "#define mediump\n"
+ "#define highp \n"
+#endif
+ ,
+ source };
+ glShaderSource(res, 3, sources, NULL);
+ free((void*)source);
+
+ glCompileShader(res);
+ GLint compile_ok = GL_FALSE;
+ glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
+ if (compile_ok == GL_FALSE) {
+ fprintf(stderr, "%s:", filename);
+ print_log(res);
+ glDeleteShader(res);
+ return 0;
+ }
+
+ return res;
+}
+
+GLuint create_program(const char *vertexfile, const char *fragmentfile) {
+ GLuint program = glCreateProgram();
+ GLuint shader;
+
+ if(vertexfile) {
+ shader = create_shader(vertexfile, GL_VERTEX_SHADER);
+ if(!shader)
+ return 0;
+ glAttachShader(program, shader);
+ }
+
+ if(fragmentfile) {
+ shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
+ if(!shader)
+ return 0;
+ glAttachShader(program, shader);
+ }
+
+ glLinkProgram(program);
+ GLint link_ok = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
+ if (!link_ok) {
+ fprintf(stderr, "glLinkProgram:");
+ print_log(program);
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ return program;
+}
+
+#ifdef GL_GEOMETRY_SHADER
+GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
+ GLuint program = glCreateProgram();
+ GLuint shader;
+
+ if(vertexfile) {
+ shader = create_shader(vertexfile, GL_VERTEX_SHADER);
+ if(!shader)
+ return 0;
+ glAttachShader(program, shader);
+ }
+
+ if(geometryfile) {
+ shader = create_shader(geometryfile, GL_GEOMETRY_SHADER);
+ if(!shader)
+ return 0;
+ glAttachShader(program, shader);
+
+ glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, input);
+ glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, output);
+ glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, vertices);
+ }
+
+ if(fragmentfile) {
+ shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
+ if(!shader)
+ return 0;
+ glAttachShader(program, shader);
+ }
+
+ glLinkProgram(program);
+ GLint link_ok = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
+ if (!link_ok) {
+ fprintf(stderr, "glLinkProgram:");
+ print_log(program);
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ return program;
+}
+#else
+GLuint create_gs_program(const char *vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices) {
+ fprintf(stderr, "Missing support for geometry shaders.\n");
+ return 0;
+}
+#endif
+
+GLint get_attrib(GLuint program, const char *name) {
+ GLint attribute = glGetAttribLocation(program, name);
+ if(attribute == -1)
+ fprintf(stderr, "Could not bind attribute %s\n", name);
+ return attribute;
+}
+
+GLint get_uniform(GLuint program, const char *name) {
+ GLint uniform = glGetUniformLocation(program, name);
+ if(uniform == -1)
+ fprintf(stderr, "Could not bind uniform %s\n", name);
+ return uniform;
+}
diff --git a/viewer/shader_utils.h b/viewer/shader_utils.h
@@ -0,0 +1,16 @@
+/**
+ * From the OpenGL Programming wikibook: http://en.wikibooks.org/wiki/OpenGL_Programming
+ * This file is in the public domain.
+ * Contributors: Sylvain Beucler
+ */
+#ifndef _CREATE_SHADER_H
+#define _CREATE_SHADER_H
+#include <GL/glew.h>
+char* file_read(const char* filename);
+void print_log(GLuint object);
+GLuint create_shader(const char* filename, GLenum type);
+GLuint create_program(const char* vertexfile, const char *fragmentfile);
+GLuint create_gs_program(const char* vertexfile, const char *geometryfile, const char *fragmentfile, GLint input, GLint output, GLint vertices);
+GLint get_attrib(GLuint program, const char *name);
+GLint get_uniform(GLuint program, const char *name);
+#endif
diff --git a/viewer/text.f.glsl b/viewer/text.f.glsl
@@ -0,0 +1,7 @@
+varying vec2 texpos;
+uniform sampler2D tex;
+uniform vec4 color;
+
+void main(void) {
+ gl_FragColor = vec4(1, 1, 1, texture2D(tex, texpos).a) * color;
+}
diff --git a/viewer/text.v.glsl b/viewer/text.v.glsl
@@ -0,0 +1,7 @@
+attribute vec4 coord;
+varying vec2 texpos;
+
+void main(void) {
+ gl_Position = vec4(coord.xy, 0, 1);
+ texpos = coord.zw;
+}
diff --git a/viewer/viewer.cpp b/viewer/viewer.cpp
@@ -1,99 +1,220 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <GL/glew.h>
+#include <GL/freeglut.h>
+
+#define GLM_FORCE_RADIANS
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
#include <ft2build.h>
#include FT_FREETYPE_H
-#include <GL/gl.h>
-#include <GL/glext.h>
-#include <GLES3/gl31.h>
-#include <err.h>
+#include "shader_utils.h"
-class Text {
-public:
- Text();
- int draw_source_file(const char **lines);
+GLuint program;
+GLint attribute_coord;
+GLint uniform_tex;
+GLint uniform_color;
-private:
- FT_Library ft;
- FT_Face face;
- FT_GlyphSlot g;
-
- void render_text(const char *, float x, float y, float sx, float sy);
+struct point {
+ GLfloat x;
+ GLfloat y;
+ GLfloat s;
+ GLfloat t;
};
-int
-main(void)
-{
+GLuint vbo;
+
+FT_Library ft;
+FT_Face face;
+
+const char *fontfilename;
+
+int init_resources() {
+ /* Initialize the FreeType2 library */
+ if (FT_Init_FreeType(&ft)) {
+ fprintf(stderr, "Could not init freetype library\n");
+ return 0;
+ }
+
+ /* Load a font */
+ if (FT_New_Face(ft, fontfilename, 0, &face)) {
+ fprintf(stderr, "Could not open font %s\n", fontfilename);
+ return 0;
+ }
+
+ program = create_program("text.v.glsl", "text.f.glsl");
+ if(program == 0)
+ return 0;
+
+ attribute_coord = get_attrib(program, "coord");
+ uniform_tex = get_uniform(program, "tex");
+ uniform_color = get_uniform(program, "color");
+
+ if(attribute_coord == -1 || uniform_tex == -1 || uniform_color == -1)
+ return 0;
+
+ // Create the vertex buffer object
+ glGenBuffers(1, &vbo);
+
+ return 1;
+}
+
+/**
+ * Render text using the currently loaded font and currently set font size.
+ * Rendering starts at coordinates (x, y), z is always 0.
+ * The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy).
+ */
+void render_text(const char *text, float x, float y, float sx, float sy) {
+ const char *p;
+ FT_GlyphSlot g = face->glyph;
+
+ /* Create a texture that will be used to hold one "glyph" */
GLuint tex;
+
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glUniform1i(uniform_tex, 0);
+ /* We require 1 byte alignment when uploading texture data */
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ /* Clamping to edges is important to prevent artifacts when scaling */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ /* Linear filtering usually looks best for text */
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);
-
- GLuint vbo;
- glGenBuffers(1, &vbo);
+ /* Set up the VBO for our vertex data */
glEnableVertexAttribArray(attribute_coord);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);
- Text text;
- text.draw_source_file(NULL);
-}
-
-Text::Text()
-{
- if (FT_Init_FreeType(&ft))
- err(1, "Could not init freetype library\n");
-
- if (FT_New_Face(ft, "DejaVuSans.ttf", 0, &face))
- err(1, "Could not open font\n");
-
- FT_Set_Pixel_Sizes(face, 0, 48);
- g = face->glyph;
-}
-
-void
-Text::render_text(const char *text, float x, float y, float sx, float sy)
-{
- const char *p;
-
+ /* Loop through all characters */
for (p = text; *p; p++) {
+ /* Try to load and render the character */
if (FT_Load_Char(face, *p, FT_LOAD_RENDER))
continue;
- glTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_RED,
- g->bitmap.width,
- g->bitmap.rows,
- 0,
- GL_RED,
- GL_UNSIGNED_BYTE,
- g->bitmap.buffer
- );
+ /* Upload the "bitmap", which contains an 8-bit grayscale image, as an alpha texture */
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
+ /* Calculate the vertex and texture coordinates */
float x2 = x + g->bitmap_left * sx;
float y2 = -y - g->bitmap_top * sy;
float w = g->bitmap.width * sx;
float h = g->bitmap.rows * sy;
- GLfloat box[4][4] = {
- {x2, -y2, 0, 0},
- {x2 + w, -y2, 1, 0},
- {x2, -y2 - h, 0, 1},
- {x2 + w, -y2 - h, 1, 1},
+ point box[4] = {
+ {x2, -y2, 0, 0},
+ {x2 + w, -y2, 1, 0},
+ {x2, -y2 - h, 0, 1},
+ {x2 + w, -y2 - h, 1, 1},
};
+ /* Draw the character on the screen */
glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ /* Advance the cursor to the start of the next character */
x += (g->advance.x >> 6) * sx;
y += (g->advance.y >> 6) * sy;
}
+
+ glDisableVertexAttribArray(attribute_coord);
+ glDeleteTextures(1, &tex);
+}
+
+void display() {
+ float sx = 2.0 / glutGet(GLUT_WINDOW_WIDTH);
+ float sy = 2.0 / glutGet(GLUT_WINDOW_HEIGHT);
+
+ glUseProgram(program);
+
+ /* White background */
+ glClearColor(1, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* Enable blending, necessary for our alpha texture */
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ GLfloat black[4] = { 0, 0, 0, 1 };
+ GLfloat red[4] = { 1, 0, 0, 1 };
+ GLfloat transparent_green[4] = { 0, 1, 0, 0.5 };
+
+ /* Set font size to 48 pixels, color to black */
+ FT_Set_Pixel_Sizes(face, 0, 48);
+ glUniform4fv(uniform_color, 1, black);
+
+ /* Effects of alignment */
+ render_text("The Quick Brown Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 50 * sy, sx, sy);
+ render_text("The Misaligned Fox Jumps Over The Lazy Dog", -1 + 8.5 * sx, 1 - 100.5 * sy, sx, sy);
+
+ /* Scaling the texture versus changing the font size */
+ render_text("The Small Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 175 * sy, sx * 0.5, sy * 0.5);
+ FT_Set_Pixel_Sizes(face, 0, 24);
+ render_text("The Small Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 200 * sy, sx, sy);
+ FT_Set_Pixel_Sizes(face, 0, 48);
+ render_text("The Tiny Texture Scaled Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 235 * sy, sx * 0.25, sy * 0.25);
+ FT_Set_Pixel_Sizes(face, 0, 12);
+ render_text("The Tiny Font Sized Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 250 * sy, sx, sy);
+ FT_Set_Pixel_Sizes(face, 0, 48);
+
+ /* Colors and transparency */
+ render_text("The Solid Black Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 430 * sy, sx, sy);
+
+ glUniform4fv(uniform_color, 1, red);
+ render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 330 * sy, sx, sy);
+ render_text("The Solid Red Fox Jumps Over The Lazy Dog", -1 + 28 * sx, 1 - 450 * sy, sx, sy);
+
+ glUniform4fv(uniform_color, 1, transparent_green);
+ render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 8 * sx, 1 - 380 * sy, sx, sy);
+ render_text("The Transparent Green Fox Jumps Over The Lazy Dog", -1 + 18 * sx, 1 - 440 * sy, sx, sy);
+
+ glutSwapBuffers();
+}
+
+void free_resources() {
+ glDeleteProgram(program);
+}
+
+int main(int argc, char *argv[]) {
+ glutInit(&argc, argv);
+ glutInitContextVersion(2,0);
+ glutInitDisplayMode(GLUT_RGB);
+ glutInitWindowSize(1600, 1200);
+ glutCreateWindow("Basic Text");
+
+ if (argc > 1)
+ fontfilename = argv[1];
+ else
+ fontfilename = "DejaVuSansMono.ttf";
+
+ GLenum glew_status = glewInit();
+
+ if (GLEW_OK != glew_status) {
+ fprintf(stderr, "Error: %s\n", glewGetErrorString(glew_status));
+ return 1;
+ }
+
+ if (!GLEW_VERSION_2_0) {
+ fprintf(stderr, "No support for OpenGL 2.0 found\n");
+ return 1;
+ }
+
+ if (init_resources()) {
+ glutDisplayFunc(display);
+ glutMainLoop();
+ }
+
+ free_resources();
+ return 0;
}