#include #include #include #include #include #define GLM_FORCE_RADIANS #include #include #include #include #include FT_FREETYPE_H #include "../common/shader_utils.h" #include "fps.h" static GLuint textprogram; static GLint attribute_coord; static GLint uniform_tex; static GLint uniform_color; struct point { GLfloat x; GLfloat y; GLfloat s; GLfloat t; }; static GLuint vbo; static FT_Library ft; static FT_Face face; const char *fontfilename = "FreeSans.ttf"; int fps_init() { /* 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; } textprogram = create_program("text.v.glsl", "text.f.glsl"); if(textprogram == 0) return 0; attribute_coord = get_attrib(textprogram, "coord"); uniform_tex = get_uniform(textprogram, "tex"); uniform_color = get_uniform(textprogram, "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). */ static 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); int oldpolymode; glGetIntegerv(GL_POLYGON_MODE, &oldpolymode); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); /* 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); /* 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); /* 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; /* 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; 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); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } void fps_show() { /* static int last_time = 0; int current_time = glutGet(GLUT_ELAPSED_TIME); float fps = 1000 / (float(current_time - last_time)); last_time = current_time; */ static int fcount = 0; static int start_time = 0; // Start of the current test interval static float fps = 0; int current_time = glutGet(GLUT_ELAPSED_TIME); if(current_time - start_time <= 1000) fcount++; else { // Actually recalculate fps fps = (fcount * 1000.0) / (current_time - start_time); start_time = current_time; fcount = 0; } float sx = 2.0 / glutGet(GLUT_WINDOW_WIDTH); float sy = 2.0 / glutGet(GLUT_WINDOW_HEIGHT); glUseProgram(textprogram); /* 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 }; /* Set font size to 48 pixels, color to black */ FT_Set_Pixel_Sizes(face, 0, 48); glUniform4fv(uniform_color, 1, black); /* Effects of alignment */ static char message[128]; sprintf(message, "FPS: %f", fps); render_text(message, -1 + 8 * sx, 1 - 50 * sy, sx, sy); } void fps_free() { glDeleteProgram(textprogram); }