I am trying to make a function in my game engine to draw text with stb_truetype , The problem is that when rendering it , for some reason there’s black boxes behind the text and even when the color hex is white it is red for some reason , I have tried everything but can’t get the color hex to fix or the black boxes to go away
Here’s the functions of my engine at the moment:
#include "../include/engine.h"
#include "../include/glad/glad.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../include/GLFW/glfw3.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "../include/stb_truetype.h"
// External window
GLFWwindow* window;
// Global font variable for cleanup
Font globalFont;
// 2D drawing state
bool is2DDrawingEnabled = false;
// Create a window
GLFWwindow* CreateWindow(const char* title, int width, int height) {
if (!glfwInit()) {
fprintf(stderr, "Failed to initialize GLFW\n");
return NULL;
}
// Set OpenGL version to 2.1
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
window = glfwCreateWindow(width, height, title, NULL, NULL);
if (!window) {
fprintf(stderr, "Failed to create GLFW window\n");
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); // Make sure glad supports OpenGL 2.1
glViewport(0, 0, width, height);
return window;
}
// Set background color using hex color codes like "#RRGGBB"
void SetBackColor(const char* hexColor) {
if (hexColor[0] == '#') {
unsigned int hexValue = (unsigned int)strtol(hexColor + 1, NULL, 16);
float r = ((hexValue >> 16) & 0xFF) / 255.0f;
float g = ((hexValue >> 8) & 0xFF) / 255.0f;
float b = (hexValue & 0xFF) / 255.0f;
glClearColor(r, g, b, 1.0f);
} else {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
}
// Load font function with error handling
Font LoadFont(const char* filename, int fontSize) {
Font font = { 0 };
// Load the font file
FILE* fontFile = fopen(filename, "rb");
if (!fontFile) {
fprintf(stderr, "Failed to open font file: %s\n", filename);
return font;
}
// Read font file into memory
fseek(fontFile, 0, SEEK_END);
long fileSize = ftell(fontFile);
fseek(fontFile, 0, SEEK_SET);
unsigned char* ttfBuffer = (unsigned char*)malloc(fileSize);
if (fread(ttfBuffer, 1, fileSize, fontFile) != fileSize) {
fprintf(stderr, "Failed to read font file: %s\n", filename);
fclose(fontFile);
free(ttfBuffer);
return font;
}
fclose(fontFile);
// Create bitmap for font
font.bitmap = (unsigned char*)malloc(512 * 512);
stbtt_bakedchar* bakedChars = (stbtt_bakedchar*)malloc(sizeof(stbtt_bakedchar) * 96);
if (stbtt_BakeFontBitmap(ttfBuffer, 0, (float)fontSize, font.bitmap, 512, 512, 32, 96, bakedChars) <= 0) {
fprintf(stderr, "Failed to bake font bitmap for: %s\n", filename);
free(ttfBuffer);
free(font.bitmap);
free(bakedChars);
return font;
}
// Generate texture
glGenTextures(1, &font.textureID);
glBindTexture(GL_TEXTURE_2D, font.textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 512, 0, GL_RED, GL_UNSIGNED_BYTE, font.bitmap);
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Store baked chars in font structure
font.glyphs = bakedChars; // Store the glyphs directly in the Font struct
// Free temporary buffers
free(ttfBuffer);
font.baseSize = fontSize;
// Store global font
globalFont = font;
return font;
}
// Function to start 2D drawing
void Start2DDrawing() {
if (!is2DDrawingEnabled) {
glEnable(GL_TEXTURE_2D); // Enable 2D texturing
glEnable(GL_BLEND); // Enable blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Set blend function
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
is2DDrawingEnabled = true;
}
}
void End2DDrawing() {
if (is2DDrawingEnabled) {
glDisable(GL_TEXTURE_2D);
is2DDrawingEnabled = false;
}
}
// Function to draw text
void DrawText(Font font, const char* text, int posX, int posY, const char* hexColor) {
glBindTexture(GL_TEXTURE_2D, font.textureID);
// Set color based on hex value
if (hexColor[0] == '#') {
unsigned int hexValue = (unsigned int)strtol(hexColor + 1, NULL, 16);
float r = ((hexValue >> 16) & 0xFF) / 255.0f;
float g = ((hexValue >> 8) & 0xFF) / 255.0f;
float b = (hexValue & 0xFF) / 255.0f;
glColor4f(r, g, b, 1.0f); // Set color from hex
} else {
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Default to white if not a valid hex
}
float x = (float)posX;
float y = (float)posY;
for (const char *c = text; *c; c++) {
if (*c < 32 || *c > 126) continue; // Skip unsupported characters
stbtt_aligned_quad q;
stbtt_GetBakedQuad(font.glyphs, 512, 512, *c - 32, &x, &y, &q, 1);
// Draw the quad for the character
glBegin(GL_QUADS);
glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, q.y0);
glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, q.y0);
glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, q.y1);
glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, q.y1);
glEnd();
}
}
bool WindowShouldEnd(GLFWwindow* window) {
return glfwWindowShouldClose(window);
}
void HandleEvents(GLFWwindow* window) {
glfwPollEvents();
glfwSwapBuffers(window);
}
void ClearScreen() {
glClear(GL_COLOR_BUFFER_BIT);
}
void error_callback(int error, const char* description) {
fprintf(stderr, "Error: %s\n", description);
}
// Cleanup
void Cleanup() {
// Clean up the global font
glDeleteTextures(1, &globalFont.textureID);
free(globalFont.bitmap);
free(globalFont.glyphs); // Free baked glyphs
glfwDestroyWindow(window);
glfwTerminate();
}
The font things in engine.h:
// Color structure
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} Color;
// Font structure to hold font data
typedef struct Glyph {
stbtt_bakedchar bakedChar; // Represents the baked character data from stb_truetype
float advanceX; // The amount to advance the cursor after rendering this glyph
float offsetX; // The offset for rendering this glyph
} Glyph;
typedef struct Font {
GLuint textureID; // Texture ID for the font atlas
unsigned char *bitmap; // Bitmap data for the font atlas
int baseSize; // Base font size (used for scaling)
stbtt_bakedchar* glyphs; // Pointer to an array of baked characters for the font
} Font;
this is the main app code and there is nothing wrong with it i guess:
#include "../include/engine.h"
int main() {
window = CreateWindow("Test Window", 800, 600); // Initialize the window
if (!window) return -1; // Exit if window creation failed
globalFont = LoadFont("assets/AfacadFlux.ttf", 48); // Load the font
if (globalFont.textureID == 0) return -1; // Exit if font loading failed
while (!WindowShouldEnd(window)) {
ClearScreen();
SetBackColor("#222222");
Start2DDrawing();
Color white = {255, 255, 255, 255}; // White color
DrawText(globalFont, "Hello, World!", 100, 100, "#FFFFFF"); // Draw text
End2DDrawing();
HandleEvents(window);
}
Cleanup(); // Clean up resources
return 0;
}
This is the preview of the problem i am facing:
Preview
What I tried to do:
Adding a function to render text in my engine
What I expected:
Transparent Text with working color hex
You need to sign in to view this answers
Leave feedback about this