citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

commit 222bb87fed0b423a5e171c5d581c6349357fdc62
parent 5e284f4b138ff09ec9ceba6c2571341130f9bdf5
Author: kyle <kyle@getaddrinfo.net>
Date:   Tue, 27 Oct 2015 21:05:01 -0600

viewer: get demo code working

Diffstat:
Dviewer/.viewer.cpp.swp | 0
Mviewer/Makefile | 19+++++++++++++++++--
Aviewer/shader_utils.cpp | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aviewer/shader_utils.h | 16++++++++++++++++
Aviewer/text.f.glsl | 7+++++++
Aviewer/text.v.glsl | 7+++++++
Mviewer/viewer.cpp | 235++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
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; }