Skip to content
Permalink
master
Go to file
Latest commit 688ef70 Jul 2, 2020 History
2 contributors

Users who have contributed to this file

@vallentin @charlesgriffiths
1339 lines (1001 sloc) 34.8 KB
// Repository: https://github.com/vallentin/glText
// License: https://github.com/vallentin/glText/blob/master/LICENSE.md
//
// Date Created: September 24, 2013
// Last Modified: July 02, 2020
// In one C or C++ file, define GLT_IMPLEMENTATION prior to inclusion to create the implementation.
// #define GLT_IMPLEMENTATION
// #include "gltext.h"
// Copyright (c) 2013-2018 Christian Vallentin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
// Refrain from using any exposed macros, functions
// or structs prefixed with an underscore. As these
// are only intended for internal purposes. Which
// additionally means they can be removed, renamed
// or changed between minor updates without notice.
#ifndef GL_TEXT_H
#define GL_TEXT_H
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(__gl_h_) && !defined(__glcorearb_h_)
# error OpenGL header must be included prior to including glText header
#endif
#include <stdlib.h> /* malloc(), calloc(), free() */
#include <string.h> /* memset(), memcpy(), strlen() */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t, uint64_t */
#if (defined(_DEBUG) || defined(DEBUG)) && !defined(GLT_DEBUG)
# define GLT_DEBUG 1
#endif
#ifdef GLT_DEBUG
# include <assert.h> /* assert() */
# define _GLT_ASSERT(expression) assert(expression)
#else
# define _GLT_ASSERT(expression)
#endif
#ifdef GLT_DEBUG_PRINT
# include <stdio.h> /* printf */
#endif
#define _GLT_STRINGIFY(str) #str
#define _GLT_STRINGIFY_TOKEN(str) _GLT_STRINGIFY(str)
#define GLT_STRINGIFY_VERSION(major, minor, patch) _GLT_STRINGIFY(major) "." _GLT_STRINGIFY(minor) "." _GLT_STRINGIFY(patch)
#define GLT_NAME "glText"
#define GLT_VERSION_MAJOR 1
#define GLT_VERSION_MINOR 1
#define GLT_VERSION_PATCH 6
#define GLT_VERSION GLT_STRINGIFY_VERSION(GLT_VERSION_MAJOR, GLT_VERSION_MINOR, GLT_VERSION_PATCH)
#define GLT_NAME_VERSION GLT_NAME " " GLT_VERSION
#define GLT_NULL 0
#define GLT_NULL_HANDLE 0
#ifdef GLT_IMPORTS
# define GLT_API extern
#else
# ifndef GLT_STATIC
# define GLT_STATIC
# endif
# define GLT_API static
#endif
#define GLT_LEFT 0
#define GLT_TOP 0
#define GLT_CENTER 1
#define GLT_RIGHT 2
#define GLT_BOTTOM 2
static GLboolean gltInitialized = GL_FALSE;
typedef struct GLTtext GLTtext;
GLT_API GLboolean gltInit(void);
GLT_API void gltTerminate(void);
GLT_API GLTtext* gltCreateText(void);
GLT_API void gltDeleteText(GLTtext *text);
#define gltDestroyText gltDeleteText
GLT_API GLboolean gltSetText(GLTtext *text, const char *string);
GLT_API const char* gltGetText(GLTtext *text);
GLT_API void gltViewport(GLsizei width, GLsizei height);
GLT_API void gltBeginDraw();
GLT_API void gltEndDraw();
GLT_API void gltDrawText(GLTtext *text, const GLfloat mvp[16]);
GLT_API void gltDrawText2D(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale);
GLT_API void gltDrawText2DAligned(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale, int horizontalAlignment, int verticalAlignment);
GLT_API void gltDrawText3D(GLTtext *text, GLfloat x, GLfloat y, GLfloat z, GLfloat scale, GLfloat view[16], GLfloat projection[16]);
GLT_API void gltColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
GLT_API void gltGetColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a);
GLT_API GLfloat gltGetLineHeight(GLfloat scale);
GLT_API GLfloat gltGetTextWidth(const GLTtext *text, GLfloat scale);
GLT_API GLfloat gltGetTextHeight(const GLTtext *text, GLfloat scale);
GLT_API GLboolean gltIsCharacterSupported(const char c);
GLT_API GLint gltCountSupportedCharacters(const char *str);
GLT_API GLboolean gltIsCharacterDrawable(const char c);
GLT_API GLint gltCountDrawableCharacters(const char *str);
GLT_API GLint gltCountNewLines(const char *str);
// After this point everything you'll see is the
// implementation and all the internal stuff.
// Use it at your own discretion, but be warned
// that everything can have changed in the next
// commit, so be careful relying on any of it.
#ifdef GLT_IMPLEMENTATION
#define _GLT_TEXT2D_POSITION_LOCATION 0
#define _GLT_TEXT2D_TEXCOORD_LOCATION 1
#define _GLT_TEXT2D_POSITION_SIZE 2
#define _GLT_TEXT2D_TEXCOORD_SIZE 2
#define _GLT_TEXT2D_VERTEX_SIZE (_GLT_TEXT2D_POSITION_SIZE + _GLT_TEXT2D_TEXCOORD_SIZE)
#define _GLT_TEXT2D_POSITION_OFFSET 0
#define _GLT_TEXT2D_TEXCOORD_OFFSET _GLT_TEXT2D_POSITION_SIZE
#define _GLT_MAT4_INDEX(row, column) ((row) + (column) * 4)
static const char *_gltFontGlyphCharacters = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`*#=[]\"";
#define _gltFontGlyphCount 83
#define _gltFontGlyphMinChar ' '
#define _gltFontGlyphMaxChar 'z'
static const int _gltFontGlyphHeight = 17; // Line height
typedef struct _GLTglyph {
char c;
GLint x, y;
GLint w, h;
GLfloat u1, v1;
GLfloat u2, v2;
GLboolean drawable;
} _GLTglyph;
typedef struct _GLTglyphdata {
uint32_t x, y;
uint32_t w, h;
uint32_t marginLeft, marginTop;
uint32_t marginRight, marginBottom;
uint16_t dataWidth, dataHeight;
} _GLTglyphdata;
static _GLTglyph _gltFontGlyphs[_gltFontGlyphCount];
#define _gltFontGlyphLength (_gltFontGlyphMaxChar - _gltFontGlyphMinChar + 1)
static _GLTglyph _gltFontGlyphs2[_gltFontGlyphLength];
static GLuint _gltText2DShader = GLT_NULL_HANDLE;
static GLuint _gltText2DFontTexture = GLT_NULL_HANDLE;
static GLint _gltText2DShaderMVPUniformLocation = -1;
static GLint _gltText2DShaderColorUniformLocation = -1;
static GLfloat _gltText2DProjectionMatrix[16];
struct GLTtext {
char *_text;
GLsizei _textLength;
GLboolean _dirty;
GLsizei vertexCount;
GLfloat *_vertices;
GLuint _vao;
GLuint _vbo;
};
GLT_API void _gltGetViewportSize(GLint *width, GLint *height);
GLT_API void _gltMat4Mult(const GLfloat lhs[16], const GLfloat rhs[16], GLfloat result[16]);
GLT_API void _gltUpdateBuffers(GLTtext *text);
GLT_API GLboolean _gltCreateText2DShader(void);
GLT_API GLboolean _gltCreateText2DFontTexture(void);
GLT_API GLTtext* gltCreateText(void)
{
GLTtext *text = (GLTtext*)calloc(1, sizeof(GLTtext));
_GLT_ASSERT(text);
if (!text)
return GLT_NULL;
glGenVertexArrays(1, &text->_vao);
glGenBuffers(1, &text->_vbo);
_GLT_ASSERT(text->_vao);
_GLT_ASSERT(text->_vbo);
if (!text->_vao || !text->_vbo)
{
gltDeleteText(text);
return GLT_NULL;
}
glBindVertexArray(text->_vao);
glBindBuffer(GL_ARRAY_BUFFER, text->_vbo);
glEnableVertexAttribArray(_GLT_TEXT2D_POSITION_LOCATION);
glVertexAttribPointer(_GLT_TEXT2D_POSITION_LOCATION, _GLT_TEXT2D_POSITION_SIZE, GL_FLOAT, GL_FALSE, (_GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat)), (const void*)(_GLT_TEXT2D_POSITION_OFFSET * sizeof(GLfloat)));
glEnableVertexAttribArray(_GLT_TEXT2D_TEXCOORD_LOCATION);
glVertexAttribPointer(_GLT_TEXT2D_TEXCOORD_LOCATION, _GLT_TEXT2D_TEXCOORD_SIZE, GL_FLOAT, GL_FALSE, (_GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat)), (const void*)(_GLT_TEXT2D_TEXCOORD_OFFSET * sizeof(GLfloat)));
glBindVertexArray(0);
return text;
}
GLT_API void gltDeleteText(GLTtext *text)
{
if (!text)
return;
if (text->_vao)
{
glDeleteVertexArrays(1, &text->_vao);
text->_vao = GLT_NULL_HANDLE;
}
if (text->_vbo)
{
glDeleteBuffers(1, &text->_vbo);
text->_vbo = GLT_NULL_HANDLE;
}
if (text->_text)
free(text->_text);
if (text->_vertices)
free(text->_vertices);
free(text);
}
GLT_API GLboolean gltSetText(GLTtext *text, const char *string)
{
if (!text)
return GL_FALSE;
int strLength = 0;
if (string)
strLength = strlen(string);
if (strLength)
{
if (text->_text)
{
if (strcmp(string, text->_text) == 0)
return GL_TRUE;
free(text->_text);
text->_text = GLT_NULL;
}
text->_text = (char*)malloc((strLength + 1) * sizeof(char));
if (text->_text)
{
memcpy(text->_text, string, (strLength + 1) * sizeof(char));
text->_textLength = strLength;
text->_dirty = GL_TRUE;
return GL_TRUE;
}
}
else
{
if (text->_text)
{
free(text->_text);
text->_text = GLT_NULL;
}
else
{
return GL_TRUE;
}
text->_textLength = 0;
text->_dirty = GL_TRUE;
return GL_TRUE;
}
return GL_FALSE;
}
GLT_API const char* gltGetText(GLTtext *text)
{
if (text && text->_text)
return text->_text;
return "\0";
}
GLT_API void gltViewport(GLsizei width, GLsizei height)
{
_GLT_ASSERT(width > 0);
_GLT_ASSERT(height > 0);
const GLfloat left = 0.0f;
const GLfloat right = (GLfloat)width;
const GLfloat bottom = (GLfloat)height;
const GLfloat top = 0.0f;
const GLfloat zNear = -1.0f;
const GLfloat zFar = 1.0f;
const GLfloat projection[16] = {
(2.0f / (right - left)), 0.0f, 0.0f, 0.0f,
0.0f, (2.0f / (top - bottom)), 0.0f, 0.0f,
0.0f, 0.0f, (-2.0f / (zFar - zNear)), 0.0f,
-((right + left) / (right - left)),
-((top + bottom) / (top - bottom)),
-((zFar + zNear) / (zFar - zNear)),
1.0f,
};
memcpy(_gltText2DProjectionMatrix, projection, 16 * sizeof(GLfloat));
}
GLT_API void gltBeginDraw()
{
glUseProgram(_gltText2DShader);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _gltText2DFontTexture);
}
GLT_API void gltEndDraw()
{
}
#define _gltDrawText() \
glUniformMatrix4fv(_gltText2DShaderMVPUniformLocation, 1, GL_FALSE, mvp); \
\
glBindVertexArray(text->_vao); \
glDrawArrays(GL_TRIANGLES, 0, text->vertexCount);
GLT_API void gltDrawText(GLTtext *text, const GLfloat mvp[16])
{
if (!text)
return;
if (text->_dirty)
_gltUpdateBuffers(text);
if (!text->vertexCount)
return;
_gltDrawText();
}
GLT_API void gltDrawText2D(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale)
{
if (!text)
return;
if (text->_dirty)
_gltUpdateBuffers(text);
if (!text->vertexCount)
return;
#ifndef GLT_MANUAL_VIEWPORT
GLint viewportWidth, viewportHeight;
_gltGetViewportSize(&viewportWidth, &viewportHeight);
gltViewport(viewportWidth, viewportHeight);
#endif
const GLfloat model[16] = {
scale, 0.0f, 0.0f, 0.0f,
0.0f, scale, 0.0f, 0.0f,
0.0f, 0.0f, scale, 0.0f,
x, y, 0.0f, 1.0f,
};
GLfloat mvp[16];
_gltMat4Mult(_gltText2DProjectionMatrix, model, mvp);
_gltDrawText();
}
GLT_API void gltDrawText2DAligned(GLTtext *text, GLfloat x, GLfloat y, GLfloat scale, int horizontalAlignment, int verticalAlignment)
{
if (!text)
return;
if (text->_dirty)
_gltUpdateBuffers(text);
if (!text->vertexCount)
return;
if (horizontalAlignment == GLT_CENTER)
x -= gltGetTextWidth(text, scale) * 0.5f;
else if (horizontalAlignment == GLT_RIGHT)
x -= gltGetTextWidth(text, scale);
if (verticalAlignment == GLT_CENTER)
y -= gltGetTextHeight(text, scale) * 0.5f;
else if (verticalAlignment == GLT_RIGHT)
y -= gltGetTextHeight(text, scale);
gltDrawText2D(text, x, y, scale);
}
GLT_API void gltDrawText3D(GLTtext *text, GLfloat x, GLfloat y, GLfloat z, GLfloat scale, GLfloat view[16], GLfloat projection[16])
{
if (!text)
return;
if (text->_dirty)
_gltUpdateBuffers(text);
if (!text->vertexCount)
return;
const GLfloat model[16] = {
scale, 0.0f, 0.0f, 0.0f,
0.0f, -scale, 0.0f, 0.0f,
0.0f, 0.0f, scale, 0.0f,
x, y + (GLfloat)_gltFontGlyphHeight * scale, z, 1.0f,
};
GLfloat mvp[16];
GLfloat vp[16];
_gltMat4Mult(projection, view, vp);
_gltMat4Mult(vp, model, mvp);
_gltDrawText();
}
GLT_API void gltColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
glUniform4f(_gltText2DShaderColorUniformLocation, r, g, b, a);
}
GLT_API void gltGetColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a)
{
GLfloat color[4];
glGetUniformfv(_gltText2DShader, _gltText2DShaderColorUniformLocation, color);
if (r) (*r) = color[0];
if (g) (*g) = color[1];
if (b) (*b) = color[2];
if (a) (*a) = color[3];
}
GLT_API GLfloat gltGetLineHeight(GLfloat scale)
{
return (GLfloat)_gltFontGlyphHeight * scale;
}
GLT_API GLfloat gltGetTextWidth(const GLTtext *text, GLfloat scale)
{
if (!text || !text->_text)
return 0.0f;
GLfloat maxWidth = 0.0f;
GLfloat width = 0.0f;
_GLTglyph glyph;
char c;
int i;
for (i = 0; i < text->_textLength; i++)
{
c = text->_text[i];
if ((c == '\n') || (c == '\r'))
{
if (width > maxWidth)
maxWidth = width;
width = 0.0f;
continue;
}
if (!gltIsCharacterSupported(c))
{
#ifdef GLT_UNKNOWN_CHARACTER
c = GLT_UNKNOWN_CHARACTER;
if (!gltIsCharacterSupported(c))
continue;
#else
continue;
#endif
}
glyph = _gltFontGlyphs2[c - _gltFontGlyphMinChar];
width += (GLfloat)glyph.w;
}
if (width > maxWidth)
maxWidth = width;
return maxWidth * scale;
}
GLT_API GLfloat gltGetTextHeight(const GLTtext *text, GLfloat scale)
{
if (!text || !text->_text)
return 0.0f;
return (GLfloat)(gltCountNewLines(text->_text) + 1) * gltGetLineHeight(scale);
}
GLT_API GLboolean gltIsCharacterSupported(const char c)
{
if (c == '\t') return GL_TRUE;
if (c == '\n') return GL_TRUE;
if (c == '\r') return GL_TRUE;
int i;
for (i = 0; i < _gltFontGlyphCount; i++)
{
if (_gltFontGlyphCharacters[i] == c)
return GL_TRUE;
}
return GL_FALSE;
}
GLT_API GLint gltCountSupportedCharacters(const char *str)
{
if (!str)
return 0;
GLint count = 0;
while ((*str) != '\0')
{
if (gltIsCharacterSupported(*str))
count++;
str++;
}
return count;
}
GLT_API GLboolean gltIsCharacterDrawable(const char c)
{
if (c < _gltFontGlyphMinChar) return GL_FALSE;
if (c > _gltFontGlyphMaxChar) return GL_FALSE;
if (_gltFontGlyphs2[c - _gltFontGlyphMinChar].drawable)
return GL_TRUE;
return GL_FALSE;
}
GLT_API GLint gltCountDrawableCharacters(const char *str)
{
if (!str)
return 0;
GLint count = 0;
while ((*str) != '\0')
{
if (gltIsCharacterDrawable(*str))
count++;
str++;
}
return count;
}
GLT_API GLint gltCountNewLines(const char *str)
{
GLint count = 0;
while ((str = strchr(str, '\n')) != NULL)
{
count++;
str++;
}
return count;
}
GLT_API void _gltGetViewportSize(GLint *width, GLint *height)
{
GLint dimensions[4];
glGetIntegerv(GL_VIEWPORT, dimensions);
if (width) (*width) = dimensions[2];
if (height) (*height) = dimensions[3];
}
GLT_API void _gltMat4Mult(const GLfloat lhs[16], const GLfloat rhs[16], GLfloat result[16])
{
int c, r, i;
for (c = 0; c < 4; c++)
{
for (r = 0; r < 4; r++)
{
result[_GLT_MAT4_INDEX(r, c)] = 0.0f;
for (i = 0; i < 4; i++)
result[_GLT_MAT4_INDEX(r, c)] += lhs[_GLT_MAT4_INDEX(r, i)] * rhs[_GLT_MAT4_INDEX(i, c)];
}
}
}
GLT_API void _gltUpdateBuffers(GLTtext *text)
{
if (!text || !text->_dirty)
return;
if (text->_vertices)
{
text->vertexCount = 0;
free(text->_vertices);
text->_vertices = GLT_NULL;
}
if (!text->_text || !text->_textLength)
{
text->_dirty = GL_FALSE;
return;
}
const GLsizei countDrawable = gltCountDrawableCharacters(text->_text);
if (!countDrawable)
{
text->_dirty = GL_FALSE;
return;
}
const GLsizei vertexCount = countDrawable * 2 * 3; // 3 vertices in a triangle, 2 triangles in a quad
const GLsizei vertexSize = _GLT_TEXT2D_VERTEX_SIZE;
GLfloat *vertices = (GLfloat*)malloc(vertexCount * vertexSize * sizeof(GLfloat));
if (!vertices)
return;
GLsizei vertexElementIndex = 0;
GLfloat glyphX = 0.0f;
GLfloat glyphY = 0.0f;
GLfloat glyphWidth;
const GLfloat glyphHeight = (GLfloat)_gltFontGlyphHeight;
const GLfloat glyphAdvanceX = 0.0f;
const GLfloat glyphAdvanceY = 0.0f;
_GLTglyph glyph;
char c;
int i;
for (i = 0; i < text->_textLength; i++)
{
c = text->_text[i];
if (c == '\n')
{
glyphX = 0.0f;
glyphY += glyphHeight + glyphAdvanceY;
continue;
}
else if (c == '\r')
{
glyphX = 0.0f;
continue;
}
if (!gltIsCharacterSupported(c))
{
#ifdef GLT_UNKNOWN_CHARACTER
c = GLT_UNKNOWN_CHARACTER;
if (!gltIsCharacterSupported(c))
continue;
#else
continue;
#endif
}
glyph = _gltFontGlyphs2[c - _gltFontGlyphMinChar];
glyphWidth = (GLfloat)glyph.w;
if (glyph.drawable)
{
vertices[vertexElementIndex++] = glyphX;
vertices[vertexElementIndex++] = glyphY;
vertices[vertexElementIndex++] = glyph.u1;
vertices[vertexElementIndex++] = glyph.v1;
vertices[vertexElementIndex++] = glyphX + glyphWidth;
vertices[vertexElementIndex++] = glyphY + glyphHeight;
vertices[vertexElementIndex++] = glyph.u2;
vertices[vertexElementIndex++] = glyph.v2;
vertices[vertexElementIndex++] = glyphX + glyphWidth;
vertices[vertexElementIndex++] = glyphY;
vertices[vertexElementIndex++] = glyph.u2;
vertices[vertexElementIndex++] = glyph.v1;
vertices[vertexElementIndex++] = glyphX;
vertices[vertexElementIndex++] = glyphY;
vertices[vertexElementIndex++] = glyph.u1;
vertices[vertexElementIndex++] = glyph.v1;
vertices[vertexElementIndex++] = glyphX;
vertices[vertexElementIndex++] = glyphY + glyphHeight;
vertices[vertexElementIndex++] = glyph.u1;
vertices[vertexElementIndex++] = glyph.v2;
vertices[vertexElementIndex++] = glyphX + glyphWidth;
vertices[vertexElementIndex++] = glyphY + glyphHeight;
vertices[vertexElementIndex++] = glyph.u2;
vertices[vertexElementIndex++] = glyph.v2;
}
glyphX += glyphWidth + glyphAdvanceX;
}
text->vertexCount = vertexCount;
text->_vertices = vertices;
glBindBuffer(GL_ARRAY_BUFFER, text->_vbo);
glBufferData(GL_ARRAY_BUFFER, vertexCount * _GLT_TEXT2D_VERTEX_SIZE * sizeof(GLfloat), vertices, GL_DYNAMIC_DRAW);
text->_dirty = GL_FALSE;
}
GLT_API GLboolean gltInit(void)
{
if (gltInitialized)
return GL_TRUE;
if (!_gltCreateText2DShader())
return GL_FALSE;
if (!_gltCreateText2DFontTexture())
return GL_FALSE;
gltInitialized = GL_TRUE;
return GL_TRUE;
}
GLT_API void gltTerminate(void)
{
if (_gltText2DShader != GLT_NULL_HANDLE)
{
glDeleteProgram(_gltText2DShader);
_gltText2DShader = GLT_NULL_HANDLE;
}
if (_gltText2DFontTexture != GLT_NULL_HANDLE)
{
glDeleteTextures(1, &_gltText2DFontTexture);
_gltText2DFontTexture = GLT_NULL_HANDLE;
}
gltInitialized = GL_FALSE;
}
static const GLchar* _gltText2DVertexShaderSource =
"#version 330 core\n"
"\n"
"in vec2 position;\n"
"in vec2 texCoord;\n"
"\n"
"uniform mat4 mvp;\n"
"\n"
"out vec2 fTexCoord;\n"
"\n"
"void main()\n"
"{\n"
" fTexCoord = texCoord;\n"
" \n"
" gl_Position = mvp * vec4(position, 0.0, 1.0);\n"
"}\n";
static const GLchar* _gltText2DFragmentShaderSource =
"#version 330 core\n"
"\n"
"out vec4 fragColor;\n"
"\n"
"uniform sampler2D diffuse;\n"
"\n"
"uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n"
"\n"
"in vec2 fTexCoord;\n"
"\n"
"void main()\n"
"{\n"
" fragColor = texture(diffuse, fTexCoord) * color;\n"
"}\n";
GLT_API GLboolean _gltCreateText2DShader(void)
{
GLuint vertexShader, fragmentShader;
GLint compileStatus, linkStatus;
#ifdef GLT_DEBUG_PRINT
GLint infoLogLength;
GLsizei infoLogSize;
GLchar *infoLog;
#endif
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &_gltText2DVertexShaderSource, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus != GL_TRUE)
{
#ifdef GLT_DEBUG_PRINT
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLength);
// If no information log exists 0 is returned or 1 for a
// string only containing the null termination char.
if (infoLogLength > 1)
{
infoLogSize = infoLogLength * sizeof(GLchar);
infoLog = (GLchar*)malloc(infoLogSize);
glGetShaderInfoLog(vertexShader, infoLogSize, NULL, infoLog);
printf("Vertex Shader #%u <Info Log>:\n%s\n", vertexShader, infoLog);
free(infoLog);
}
#endif
glDeleteShader(vertexShader);
gltTerminate();
#ifdef GLT_DEBUG
_GLT_ASSERT(compileStatus == GL_TRUE);
return GL_FALSE;
#else
return GL_FALSE;
#endif
}
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &_gltText2DFragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus != GL_TRUE)
{
#ifdef GLT_DEBUG_PRINT
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLength);
// If no information log exists 0 is returned or 1 for a
// string only containing the null termination char.
if (infoLogLength > 1)
{
infoLogSize = infoLogLength * sizeof(GLchar);
infoLog = (GLchar*)malloc(infoLogSize);
glGetShaderInfoLog(fragmentShader, infoLogSize, NULL, infoLog);
printf("Fragment Shader #%u <Info Log>:\n%s\n", fragmentShader, infoLog);
free(infoLog);
}
#endif
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
gltTerminate();
#ifdef GLT_DEBUG
_GLT_ASSERT(compileStatus == GL_TRUE);
return GL_FALSE;
#else
return GL_FALSE;
#endif
}
_gltText2DShader = glCreateProgram();
glAttachShader(_gltText2DShader, vertexShader);
glAttachShader(_gltText2DShader, fragmentShader);
glBindAttribLocation(_gltText2DShader, _GLT_TEXT2D_POSITION_LOCATION, "position");
glBindAttribLocation(_gltText2DShader, _GLT_TEXT2D_TEXCOORD_LOCATION, "texCoord");
glBindFragDataLocation(_gltText2DShader, 0, "fragColor");
glLinkProgram(_gltText2DShader);
glDetachShader(_gltText2DShader, vertexShader);
glDeleteShader(vertexShader);
glDetachShader(_gltText2DShader, fragmentShader);
glDeleteShader(fragmentShader);
glGetProgramiv(_gltText2DShader, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE)
{
#ifdef GLT_DEBUG_PRINT
glGetProgramiv(_gltText2DShader, GL_INFO_LOG_LENGTH, &infoLogLength);
// If no information log exists 0 is returned or 1 for a
// string only containing the null termination char.
if (infoLogLength > 1)
{
infoLogSize = infoLogLength * sizeof(GLchar);
infoLog = (GLchar*)malloc(infoLogSize);
glGetProgramInfoLog(_gltText2DShader, infoLogSize, NULL, infoLog);
printf("Program #%u <Info Log>:\n%s\n", _gltText2DShader, infoLog);
free(infoLog);
}
#endif
gltTerminate();
#ifdef GLT_DEBUG
_GLT_ASSERT(linkStatus == GL_TRUE);
return GL_FALSE;
#else
return GL_FALSE;
#endif
}
glUseProgram(_gltText2DShader);
_gltText2DShaderMVPUniformLocation = glGetUniformLocation(_gltText2DShader, "mvp");
_gltText2DShaderColorUniformLocation = glGetUniformLocation(_gltText2DShader, "color");
glUniform1i(glGetUniformLocation(_gltText2DShader, "diffuse"), 0);
glUseProgram(0);
return GL_TRUE;
}
static const uint64_t _gltFontGlyphRects[_gltFontGlyphCount] = {
0x1100040000, 0x304090004, 0x30209000D, 0x304090016, 0x30209001F, 0x304090028, 0x302090031, 0x409003A,
0x302090043, 0x30109004C, 0x1080055, 0x30209005D, 0x302090066, 0x3040A006F, 0x304090079, 0x304090082,
0x409008B, 0x4090094, 0x30409009D, 0x3040900A6, 0x3020900AF, 0x3040900B8, 0x3040900C1, 0x3040A00CA,
0x3040900D4, 0x40A00DD, 0x3040900E7, 0x3020900F0, 0x3020900F9, 0x302090102, 0x30209010B, 0x302090114,
0x30209011D, 0x302090126, 0x30209012F, 0x302070138, 0x30209013F, 0x302090148, 0x302090151, 0x3020A015A,
0x3020A0164, 0x30209016E, 0x302090177, 0x102090180, 0x302090189, 0x302090192, 0x30209019B, 0x3020901A4,
0x3020901AD, 0x3020A01B6, 0x3020901C0, 0x3020901C9, 0x3020901D2, 0x3020901DB, 0x3020901E4, 0x3020901ED,
0x3020901F6, 0x3020A01FF, 0x302090209, 0x302090212, 0x30209021B, 0x302090224, 0x30209022D, 0x309060236,
0x10906023C, 0x302070242, 0x302090249, 0x706090252, 0x50409025B, 0x202090264, 0x10207026D, 0x102070274,
0x30406027B, 0x104060281, 0x2010B0287, 0x3020A0292, 0xB0007029C, 0x5040A02AA, 0x3020A02B4, 0x6050902BE,
0x20702C7, 0x20702CE, 0xB010902D5,
};
#define _GLT_FONT_GLYPH_DATA_TYPE uint64_t
#define _GLT_FONT_PIXEL_SIZE_BITS 2
#define _gltFontGlyphDataCount 387
static const _GLT_FONT_GLYPH_DATA_TYPE _gltFontGlyphData[_gltFontGlyphDataCount] = {
0x551695416A901554, 0x569695A5A56AA55A, 0x555554155545AA9, 0x916AA41569005A40, 0xA5A569695A5A5696, 0x51555556AA569695, 0x696916A941554155, 0x69155A55569555A5,
0x15541555456A9569, 0xA9569545A4005500, 0x569695A5A569695A, 0x5545AA9569695A5A, 0x916A941554555415, 0x55A56AA95A5A5696, 0x40555416A9555695, 0x55A45AA505550155,
0xA55AAA455A555691, 0x169005A45569155, 0xA945554015400554, 0x569695A5A569695A, 0x9545AA9569695A5A, 0x4555555AA95A5556, 0x55A4016900154555, 0xA569695A5A45AA90,
0x69695A5A569695A5, 0x9001541555455555, 0x5AA4155505A4016, 0xA40169005A501695, 0x155555AAA4569505, 0x5A405A4015405555, 0x5A505A545AA45554, 0x5A405A405A405A40,
0x555556A95A555A40, 0x569005A400551554, 0x9569A569695A5A45, 0xA5A56969169A556A, 0xA405555555155555, 0x169005A50169505A, 0x9505A40169005A40, 0x5555155555AAA456,
0x95A66916AA905555, 0x695A6695A6695A66, 0x154555555A5695A6, 0x5696916AA4155555, 0x9695A5A569695A5A, 0x45555155555A5A56, 0xA5A5696916A94155, 0x9569695A5A569695,
0x155515541555456A, 0x695A5A5696916AA4, 0x56AA569695A5A569, 0x5540169155A55569, 0x56AA515550015400, 0x9695A5A569695A5A, 0x5A5516AA55A5A56, 0x500155005A401695,
0xA56A695A5A455555, 0x169015A55569556, 0x54005500155005A4, 0x555A555695AA9455, 0xAA569555A5505AA5, 0x15415551555556, 0x5A55AAA455A50169, 0x40169005A4556915,
0x550155505AA5055A, 0xA569695A5A455555, 0x69695A5A569695A5, 0x555554155545AA95, 0x5A5A569695A5A455, 0xA515AA55A5A56969, 0x1545505500555055, 0x95A6695A6695A569,
0xA4569A55A6695A66, 0x5551555015554569, 0x456A9569695A5A45, 0xA5A5696916A95569, 0x4155545555155555, 0xA45A5A45A5A45A5A, 0xA945A5A45A5A45A5, 0x56A915A555695056,
0x4555501554055551, 0x6945695169555AAA, 0x55AAA45569156955, 0x5A50055055551555, 0xA569695A5A45AA50, 0x69695A5A56AA95A5, 0x555555155555A5A5, 0x5A5A5696916AA415,
0x5A5696956AA56969, 0x1555556AA569695A, 0x96916A9415541555, 0x9555A555695A5A56, 0x6A9569695A5A4556, 0xA405551554155545, 0x69695A5A45A6905A, 0x695A5A569695A5A5,
0x5550555555AA55A, 0x5A555695AAA45555, 0x4556916AA4156955, 0x5555AAA45569155A, 0x95AAA45555555515, 0x6AA41569555A5556, 0xA40169155A455691, 0x1554005500155005,
0x695A5A5696916A94, 0x5A5A56A69555A555, 0x54155545AA956969, 0x569695A5A4555555, 0x9695AAA569695A5A, 0x55A5A569695A5A56, 0x55AA455555551555, 0x5A416905A456915A,
0x515555AA45A51690, 0x169005A400550055, 0x9555A40169005A40, 0x456A9569695A5A56, 0xA5A4555515541555, 0xA55A69569A569695, 0x6969169A45A6915A, 0x555555155555A5A5,
0x5A40169005A400, 0x5A40169005A40169, 0x155555AAA4556900, 0x695A569154555555, 0x6695A6695A9A95A5, 0xA5695A5695A6695A, 0x55154555555A5695, 0x95A5695A56915455,
0x695AA695A6A95A5A, 0x5695A5695A5695A9, 0x155455154555555A, 0x695A5A5696916A94, 0x5A5A569695A5A569, 0x541555456A956969, 0x5696916AA4155515, 0x56956AA569695A5A,
0x5005A40169155A55, 0x6A94155400550015, 0xA569695A5A569691, 0x69695A5A569695A5, 0x5A5415A5456A95, 0x16AA415555500155, 0xAA569695A5A56969, 0x569695A5A55A6956,
0x5545555155555A5A, 0x5555A5696916A941, 0xA5545A5005A5155A, 0x41555456A9569695, 0x56955AAA45555155, 0x9005A40169055A51, 0x5A40169005A4016, 0x5A45555055001550,
0x569695A5A569695A, 0x9695A5A569695A5A, 0x515541555456A956, 0xA5A569695A5A4555, 0xA569695A5A569695, 0x555055A515AA55A5, 0x95A5691545505500, 0x695A6695A5695A56,
0x9A4569A55A6695A6, 0x555015554169A456, 0x9569695A5A455551, 0x5A6515A515694566, 0x555A5A569695A5A4, 0x5A5A455555555155, 0xA9569695A5A56969, 0x169015A41569456,
0x55505500155005A4, 0x5A55169555AAA45, 0x55A455A555A515A5, 0x5155555AAA455690, 0x696916A941554555, 0xA95A5A56A695A9A5, 0x56A9569695A6A569, 0x9401540155415554,
0x5A5516AA45A9516, 0xA40169005A401695, 0x4154005540169005, 0xA5A5696916A94155, 0x9556945695169555, 0x55555AAA45569156, 0x6916A94155455551, 0x56A5169555A5A569,
0xA9569695A5A56955, 0x15415541555456, 0x4169A4055A4005A4, 0xA916969169A5169A, 0x50056954569555AA, 0x5AAA455551540015, 0xAA41569555A55569, 0x55A555A551695516,
0x55005550555555AA, 0x915694569416A401, 0xA5A569695A5A45AA, 0x41555456A9569695, 0x69555AAA45555155, 0x9415A415A5056951, 0x169015A40569056, 0xA941554015400554,
0x569A95A5A5696916, 0x9695A5A56A6956A9, 0x415541555456A956, 0xA5A5696916A94155, 0x516AA55A5A569695, 0x155415A915694569, 0x555A95A915505540, 0x5A55A95A91555545,
0x1694154154555569, 0xA456956A95AA56A9, 0x55416905A415515, 0x696916A941554154, 0x9055A515A555A5A5, 0x5A4016900554056, 0xAA45555055001550, 0x5505555155555A,
0x6955AAA4569505A4, 0x5500155055A515, 0x690169405A400550, 0x90569415A415A505, 0x569015A415A5056, 0x6941540015400554, 0xA456915A55A55691, 0x16905A505A416905,
0x6901555405541694, 0x16905A505A416941, 0x6955A45A516905A4, 0xA455415415545695, 0x6A45555515556A56, 0x56A45555515556A5, 0xA56A45555515556A, 0x5505515555A56956,
0x690569A4016A5001, 0x4056954169A9459A, 0x416A690156941569, 0x15A9505A695169A6, 0x4015505540055540, 0x94169A4169A405A9, 0x5A56A9A4555A415A, 0x555169A955A5A55A,
0x6945A90555555415, 0x1555154055416941, 0x56AAA456A545A690, 0x40555515A69156A5, 0x6945A69015550555, 0xA6915A6956AAA45A, 0x5A6956AAA45A6955, 0x455540555515A691,
0xAA9555556AA91555, 0xA915555554555556, 0x416905A556955A56, 0x5A416905A416905A, 0x555515555AA45690, 0x6905A516955AA455, 0x416905A416905A41, 0x55556A95A556905A,
0xA5A5696915555554, 0x5555155555,
};
GLT_API GLboolean _gltCreateText2DFontTexture(void)
{
if (gltInitialized)
return GL_TRUE;
memset(_gltFontGlyphs, 0, _gltFontGlyphCount * sizeof(_GLTglyph));
memset(_gltFontGlyphs2, 0, _gltFontGlyphLength * sizeof(_GLTglyph));
GLsizei texWidth = 0;
GLsizei texHeight = 0;
GLsizei drawableGlyphCount = 0;
_GLTglyphdata *glyphsData = (_GLTglyphdata*)calloc(_gltFontGlyphCount, sizeof(_GLTglyphdata));
uint64_t glyphPacked;
uint32_t glyphMarginPacked;
uint16_t glyphX, glyphY, glyphWidth, glyphHeight;
uint16_t glyphMarginLeft, glyphMarginTop, glyphMarginRight, glyphMarginBottom;
uint16_t glyphDataWidth, glyphDataHeight;
glyphMarginLeft = 0;
glyphMarginRight = 0;
_GLTglyph *glyph;
_GLTglyphdata *glyphData;
char c;
int i;
int x, y;
for (i = 0; i < _gltFontGlyphCount; i++)
{
c = _gltFontGlyphCharacters[i];
glyphPacked = _gltFontGlyphRects[i];
glyphX = (glyphPacked >> (uint64_t)(8 * 0)) & 0xFFFF;
glyphWidth = (glyphPacked >> (uint64_t)(8 * 2)) & 0xFF;
glyphY = 0;
glyphHeight = _gltFontGlyphHeight;
glyphMarginPacked = (glyphPacked >> (uint64_t)(8 * 3)) & 0xFFFF;
glyphMarginTop = (glyphMarginPacked >> (uint16_t)(0)) & 0xFF;
glyphMarginBottom = (glyphMarginPacked >> (uint16_t)(8)) & 0xFF;
glyphDataWidth = glyphWidth;
glyphDataHeight = glyphHeight - (glyphMarginTop + glyphMarginBottom);
glyph = &_gltFontGlyphs[i];
glyph->c = c;
glyph->x = glyphX;
glyph->y = glyphY;
glyph->w = glyphWidth;
glyph->h = glyphHeight;
glyphData = &glyphsData[i];
glyphData->x = glyphX;
glyphData->y = glyphY;
glyphData->w = glyphWidth;
glyphData->h = glyphHeight;
glyphData->marginLeft = glyphMarginLeft;
glyphData->marginTop = glyphMarginTop;
glyphData->marginRight = glyphMarginRight;
glyphData->marginBottom = glyphMarginBottom;
glyphData->dataWidth = glyphDataWidth;
glyphData->dataHeight = glyphDataHeight;
glyph->drawable = GL_FALSE;
if ((glyphDataWidth > 0) && (glyphDataHeight > 0))
glyph->drawable = GL_TRUE;
if (glyph->drawable)
{
drawableGlyphCount++;
texWidth += (GLsizei)glyphWidth;
if (texHeight < glyphHeight)
texHeight = glyphHeight;
}
}
const GLsizei textureGlyphPadding = 1; // amount of pixels added around the whole bitmap texture
const GLsizei textureGlyphSpacing = 1; // amount of pixels added between each glyph on the final bitmap texture
texWidth += textureGlyphSpacing * (drawableGlyphCount - 1);
texWidth += textureGlyphPadding * 2;
texHeight += textureGlyphPadding * 2;
const GLsizei texAreaSize = texWidth * texHeight;
const GLsizei texPixelComponents = 4; // R, G, B, A
GLubyte *texData = (GLubyte*)malloc(texAreaSize * texPixelComponents * sizeof(GLubyte));
GLsizei texPixelIndex;
for (texPixelIndex = 0; texPixelIndex < (texAreaSize * texPixelComponents); texPixelIndex++)
texData[texPixelIndex] = 0;
#define _GLT_TEX_PIXEL_INDEX(x, y) ((y) * texWidth * texPixelComponents + (x) * texPixelComponents)
#define _GLT_TEX_SET_PIXEL(x, y, r, g, b, a) { \
texPixelIndex = _GLT_TEX_PIXEL_INDEX(x, y); \
texData[texPixelIndex + 0] = r; \
texData[texPixelIndex + 1] = g; \
texData[texPixelIndex + 2] = b; \
texData[texPixelIndex + 3] = a; \
}
const int glyphDataTypeSizeBits = sizeof(_GLT_FONT_GLYPH_DATA_TYPE) * 8; // 8 bits in a byte
int data0Index = 0;
int data1Index = 0;
int bit0Index = 0;
int bit1Index = 1;
char c0 = '0';
char c1 = '0';
GLuint r, g, b, a;
GLfloat u1, v1;
GLfloat u2, v2;
GLsizei texX = 0;
GLsizei texY = 0;
texX += textureGlyphPadding;
for (i = 0; i < _gltFontGlyphCount; i++)
{
glyph = &_gltFontGlyphs[i];
glyphData = &glyphsData[i];
if (!glyph->drawable)
continue;
c = glyph->c;
glyphX = glyph->x;
glyphY = glyph->y;
glyphWidth = glyph->w;
glyphHeight = glyph->h;
glyphMarginLeft = glyphData->marginLeft;
glyphMarginTop = glyphData->marginTop;
glyphMarginRight = glyphData->marginRight;
glyphMarginBottom = glyphData->marginBottom;
glyphDataWidth = glyphData->dataWidth;
glyphDataHeight = glyphData->dataHeight;
texY = textureGlyphPadding;
u1 = (GLfloat)texX / (GLfloat)texWidth;
v1 = (GLfloat)texY / (GLfloat)texHeight;
u2 = (GLfloat)glyphWidth / (GLfloat)texWidth;
v2 = (GLfloat)glyphHeight / (GLfloat)texHeight;
glyph->u1 = u1;
glyph->v1 = v1;
glyph->u2 = u1 + u2;
glyph->v2 = v1 + v2;
texX += glyphMarginLeft;
texY += glyphMarginTop;
for (y = 0; y < glyphDataHeight; y++)
{
for (x = 0; x < glyphDataWidth; x++)
{
c0 = (_gltFontGlyphData[data0Index] >> bit0Index) & 1;
c1 = (_gltFontGlyphData[data1Index] >> bit1Index) & 1;
if ((c0 == 0) && (c1 == 0))
{
r = 0;
g = 0;
b = 0;
a = 0;
}
else if ((c0 == 0) && (c1 == 1))
{
r = 255;
g = 255;
b = 255;
a = 255;
}
else if ((c0 == 1) && (c1 == 0))
{
r = 0;
g = 0;
b = 0;
a = 255;
}
_GLT_TEX_SET_PIXEL(texX + x, texY + y, r, g, b, a);
bit0Index += _GLT_FONT_PIXEL_SIZE_BITS;
bit1Index += _GLT_FONT_PIXEL_SIZE_BITS;
if (bit0Index >= glyphDataTypeSizeBits)
{
bit0Index = bit0Index % glyphDataTypeSizeBits;
data0Index++;
}
if (bit1Index >= glyphDataTypeSizeBits)
{
bit1Index = bit1Index % glyphDataTypeSizeBits;
data1Index++;
}
}
}
texX += glyphDataWidth;
texY += glyphDataHeight;
texX += glyphMarginRight;
texX += textureGlyphSpacing;
}
for (i = 0; i < _gltFontGlyphCount; i++)
{
glyph = &_gltFontGlyphs[i];
_gltFontGlyphs2[glyph->c - _gltFontGlyphMinChar] = *glyph;
}
#undef _GLT_TEX_PIXEL_INDEX
#undef _GLT_TEX_SET_PIXEL
glGenTextures(1, &_gltText2DFontTexture);
glBindTexture(GL_TEXTURE_2D, _gltText2DFontTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
free(texData);
free(glyphsData);
return GL_TRUE;
}
#endif
#ifdef __cplusplus
}
#endif
#endif
You can’t perform that action at this time.