Skip to content

Instantly share code, notes, and snippets.

@Lightnet
Created September 27, 2025 18:15
Show Gist options
  • Save Lightnet/b2715c32c549b0c830a18922cf502c01 to your computer and use it in GitHub Desktop.
Save Lightnet/b2715c32c549b0c830a18922cf502c01 to your computer and use it in GitHub Desktop.

Sample test for getting the font working as cimgui conflict with font type from imgui.

rlgl_font.c main point.

raylib 5.5 cimgui

// due to conflict with the cimgui stb_truetype it has be sandboxed to use font
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#include "font_loader.h"
#include <stdlib.h>
#include <stdio.h>
// Define CustomFont internally with stbtt_packedchar
typedef struct {
unsigned int textureId;
stbtt_packedchar glyphs[95]; // ASCII 32-126
float size;
int atlasWidth, atlasHeight;
} InternalCustomFont;
CustomFont LoadCustomFont(const char *fileName, float fontSize) {
CustomFont font = {0};
font.size = fontSize;
// Load TTF file data
unsigned int fileSize;
unsigned char *fileData = LoadFileData(fileName, &fileSize);
if (fileData == NULL) {
printf("Failed to load font file: %s\n", fileName);
return font;
}
stbtt_fontinfo fontInfo;
if (!stbtt_InitFont(&fontInfo, fileData, 0)) {
UnloadFileData(fileData);
printf("Failed to initialize font\n");
return font;
}
// Atlas setup
font.atlasWidth = 512;
font.atlasHeight = 512;
unsigned char *bitmap = (unsigned char *)calloc(font.atlasWidth * font.atlasHeight, sizeof(unsigned char));
stbtt_pack_context packContext;
stbtt_PackBegin(&packContext, bitmap, font.atlasWidth, font.atlasHeight, 0, 1, NULL);
stbtt_PackSetOversampling(&packContext, 2, 2);
// Pack ASCII chars 32-126
stbtt_pack_range range = {0};
range.font_size = fontSize;
range.first_unicode_codepoint_in_range = 32;
range.num_chars = 95;
range.chardata_for_range = (stbtt_packedchar*)malloc(sizeof(stbtt_packedchar) * 95);
stbtt_PackFontRanges(&packContext, fileData, 0, &range, 1);
stbtt_PackEnd(&packContext);
// Convert grayscale bitmap to RGBA
unsigned char *rgbaData = (unsigned char *)malloc(font.atlasWidth * font.atlasHeight * 4);
for (int i = 0; i < font.atlasWidth * font.atlasHeight; i++) {
rgbaData[i * 4 + 0] = 255; // R
rgbaData[i * 4 + 1] = 255; // G
rgbaData[i * 4 + 2] = 255; // B
rgbaData[i * 4 + 3] = bitmap[i]; // A
}
free(bitmap);
// Upload to GPU
font.textureId = rlLoadTexture(rgbaData, font.atlasWidth, font.atlasHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
free(rgbaData);
UnloadFileData(fileData);
// Store glyphs
font.glyphs = range.chardata_for_range;
// Set texture filter
rlTextureParameters(font.textureId, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_BILINEAR);
rlTextureParameters(font.textureId, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_BILINEAR);
return font;
}
void UnloadCustomFont(CustomFont font) {
rlUnloadTexture(font.textureId);
free(font.glyphs); // Free allocated glyph data
}
void DrawCustomText(CustomFont font, const char *text, float posX, float posY, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
stbtt_packedchar* glyphs = (stbtt_packedchar*)font.glyphs;
rlSetTexture(font.textureId);
rlEnableColorBlend();
rlSetBlendMode(RL_BLEND_ALPHA);
rlPushMatrix();
// rlTranslatef(posX, posY, 0.0f);
// rlTranslatef(0.0f, 0.0f, 0.0f);
// float x = 0.0f;
float x = posX;// position 2d
for (int i = 0; text[i] != '\0'; i++) {
if (text[i] >= 32 && text[i] < 127) {
int index = text[i] - 32;
stbtt_aligned_quad quad;
stbtt_GetPackedQuad(glyphs, font.atlasWidth, font.atlasHeight, index, &x, &posY, &quad, 1);
rlBegin(RL_QUADS);
rlColor4ub(r, g, b, a);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(quad.s0, quad.t0); rlVertex2f(quad.x0, quad.y0); // Top-left
rlTexCoord2f(quad.s0, quad.t1); rlVertex2f(quad.x0, quad.y1); // Bottom-left
rlTexCoord2f(quad.s1, quad.t1); rlVertex2f(quad.x1, quad.y1); // Bottom-right
rlTexCoord2f(quad.s1, quad.t0); rlVertex2f(quad.x1, quad.y0); // Top-right
rlEnd();
}
}
// rlTranslatef(100.0f, 0.0f, 0.0f);
rlPopMatrix();
rlSetTexture(0);
}
/*
void DrawCustomText(CustomFont font, const char *text, float posX, float posY, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
stbtt_packedchar* glyphs = (stbtt_packedchar*)font.glyphs;
rlSetTexture(font.textureId);
rlEnableColorBlend();
rlSetBlendMode(RL_BLEND_ALPHA);
rlPushMatrix();
rlTranslatef(posX, posY, 0.0f);
float x = 0.0f;
for (int i = 0; text[i] != '\0'; i++) {
if (text[i] >= 32 && text[i] < 127) {
int index = text[i] - 32;
stbtt_aligned_quad quad;
stbtt_GetPackedQuad(glyphs, font.atlasWidth, font.atlasHeight, index, &x, &posY, &quad, 1);
rlBegin(RL_QUADS);
rlColor4ub(r, g, b, a);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(quad.s0, quad.t0); rlVertex2f(quad.x0, quad.y0);
rlTexCoord2f(quad.s1, quad.t0); rlVertex2f(quad.x1, quad.y0);
rlTexCoord2f(quad.s1, quad.t1); rlVertex2f(quad.x1, quad.y1);
rlTexCoord2f(quad.s0, quad.t1); rlVertex2f(quad.x0, quad.y1);
rlEnd();
}
}
rlPopMatrix();
rlSetTexture(0);
}
*/
// due to conflict with the cimgui stb_truetype it has be sandboxed to use font
#ifndef FONT_LOADER_H
#define FONT_LOADER_H
#define RLGL_STANDALONE
#include "rlgl.h"
#include "raylib.h"
// CustomFont struct for font data
typedef struct {
unsigned int textureId; // OpenGL texture ID from rlLoadTexture
void* glyphs; // Opaque pointer to hide stbtt_packedchar
float size; // Font size (pixel height)
int atlasWidth, atlasHeight; // Atlas dimensions
} CustomFont;
// Function declarations
CustomFont LoadCustomFont(const char *fileName, float fontSize);
void UnloadCustomFont(CustomFont font);
void DrawCustomText(CustomFont font, const char *text, float posX, float posY, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
#endif
//===============================================
// Base simple setup test for raylib and cimgui.
// single file test
//===============================================
// #ifndef STB_RECT_PACK_IMPLEMENTATION
// #define STB_RECT_PACK_IMPLEMENTATION
// #include "stb_rect_pack.h"
// #endif
// #ifndef STB_TRUETYPE_IMPLEMENTATION
// #define STB_TRUETYPE_IMPLEMENTATION
// #include "stb_truetype.h"
// #endif
// #include "stb_rect_pack.h"
// #include "imstb_rectpack.h" // Provides stbrp_* declarations // from imgui
// #include "imstb_truetype.h" // Provides stbtt_* declarations (packing uses stbrp_*) // imgui
#include "cimgui.h"
#include "cimgui_impl.h"
// #define RLGL_IMPLEMENTATION
#define RLGL_STANDALONE
#include "raylib.h"
#include "rlgl.h"
#define RAYMATH_STATIC_INLINE
#include "raymath.h"
#include "font_loader.h"
#include <GLFW/glfw3.h>
// #include "utils.h" // Optional: for LoadFileData/UnloadFileData if you want raylib utils
#include <stdlib.h>
#include <stdio.h> // Required for: printf()
#include <math.h> // For fmodf
#define igGetIO igGetIO_Nil
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
static void ErrorCallback(int error, const char *description);
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
static void FramebufferSizeCallback(GLFWwindow* window, int width, int height); // For resize
int main() {
int screenWidth = 800;
int screenHeight = 450;
const char *glsl_version = "#version 130";
// Initialize GLFW
if (!glfwInit()) {
printf("Failed to initialize GLFW\n");
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_DEPTH_BITS, 16);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create a window
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "rlgl + ImGui + 3D Cube", NULL, NULL);
if (!window) {
printf("Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
glfwSetWindowPos(window, 200, 200);
glfwSetKeyCallback(window, KeyCallback);
glfwSetFramebufferSizeCallback(window, FramebufferSizeCallback); // Handle resize
// Make the OpenGL context current
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable VSync for smoothness
// Load OpenGL 3.3 supported extensions
rlLoadExtensions(glfwGetProcAddress);
// Initialize OpenGL context (states and resources)
rlglInit(screenWidth, screenHeight);
// Set clear color (do this after rlglInit)
rlClearColor(245, 245, 200, 255); // Light yellow background
// Enable depth test for 3D
rlEnableDepthTest();
// Camera setup
Camera camera = { 0 };
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point (cube)
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector
camera.fovy = 45.0f; // Field of view Y
// Set up 2D camera
Camera2D camera2d = { 0 };
camera2d.target = (Vector2){ 0.0f, 0.0f }; // Center of the camera
camera2d.offset = (Vector2){ 400.0f, 300.0f }; // Screen center (800/2, 600/2)
camera2d.rotation = 0.0f;
camera2d.zoom = 1.0f; // 1:1 scale for screen coordinates
Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; // Cube at center
float rotation = 0.0f; // For animation (updated by slider or auto)
// Load custom font
// CustomFont font = LoadCustomFont("Kenney Pixel.ttf", 12.0f); // Replace with actual font path
CustomFont font = LoadCustomFont("Kenney Pixel.ttf", 18.0f); // Replace with actual font path
if (font.textureId == 0) {
printf("Error: Font failed to load properly\n");
glfwSetWindowShouldClose(window, GL_TRUE);
}
// Setup ImGui
igCreateContext(NULL);
ImGuiIO *ioptr = igGetIO();
ioptr->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGuiStyle* style = igGetStyle();
// Optional: float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor());
// ImGuiStyle_ScaleAllSizes(style, main_scale);
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
igStyleColorsDark(NULL);
GLint frontFace;
glGetIntegerv(GL_FRONT_FACE, &frontFace);
printf("FrontFace: %s\n", frontFace == GL_CW ? "GL_CW" : "GL_CCW");
// Disable backface culling to ensure the square is visible
// rlDisableBackfaceCulling();
// Main loop
while (!glfwWindowShouldClose(window)) {
float time = (float)glfwGetTime();
// Auto-rotate if not actively using slider (simple fallback; slider overrides)
if (igIsItemActive() == false) { // Check if slider is not being dragged
rotation = fmodf(time * 30.0f, 360.0f); // 30 degrees per second
}
glfwPollEvents();
// Get current size (for dynamic support)
glfwGetFramebufferSize(window, &screenWidth, &screenHeight);
// Clear early (color + depth for 3D)
rlClearScreenBuffers();
// // ImGui frame start
// ImGui_ImplOpenGL3_NewFrame();
// ImGui_ImplGlfw_NewFrame();
// igNewFrame();
// // Build ImGui UI
// igBegin("Hello, world!", NULL, 0);
// igText("This is some useful text.");
// igText("3D Cube should now rotate below!");
// igText("Current Rotation: %.1f degrees", rotation);
// if (igSliderFloat("Cube Y Rotation", &rotation, 0.0f, 360.0f, "%.0f degrees", 0)) {
// // Slider changed - rotation updates immediately
// }
// igEnd();
// // End ImGui frame (record lists)
// igRender();
// // Enable depth test for 3D
// rlEnableDepthTest();
// // 3D Rendering Setup
// float aspect = (float)screenWidth / (float)screenHeight;
// Matrix proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect, 0.1f, 1000.0f); // Perspective projection
// rlSetMatrixProjection(proj);
// // Compute view matrix from camera
// Matrix view = MatrixLookAt(camera.position, camera.target, camera.up);
// // Compute model matrix: rotation * translation
// Matrix rot = MatrixRotateY(rotation * DEG2RAD); // Rotate around Y
// Matrix trans = MatrixTranslate(cubePosition.x, cubePosition.y, cubePosition.z);
// Matrix model = MatrixMultiply(rot, trans);
// // Full model-view matrix (apply model to view)
// Matrix modelView = MatrixMultiply(model, view);
// // Set the full model-view directly (bypass stack)
// rlSetMatrixModelview(modelView);
// // Draw the cube (no push/pop or mult needed)
// // CustomDrawCube((Vector3){0.0f, 0.0f, 0.0f}); // At local origin, with model applied above
// DrawCube((Vector3){0.0f, 0.0f, 0.0f}, 1.0f, 1.0f, 1.0f, GRAY);
// rlDrawRenderBatchActive(); // Flush the batch
// 2D rendering setup
rlDisableDepthTest(); // Disable depth test for 2D
// rlDisableBackfaceCulling();
glEnable(GL_BLEND); // Enable blending (already done in your code)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Set orthographic projection
Matrix proj = MatrixOrtho(0.0f, (float)screenWidth, (float)screenHeight, 0.0f, -1.0f, 1.0f);
rlSetMatrixProjection(proj); // Allowed since it's in the main loop, not font rendering
// Set identity modelview
Matrix modelView = MatrixIdentity();
rlSetMatrixModelview(modelView); // Allowed since it's in the main loop
// Draw quad
// rlBegin(RL_QUADS);
// rlColor4ub(255, 0, 0, 255); // Red color
// rlVertex2f(100.0f, 100.0f); // Top-left
// rlVertex2f(300.0f, 100.0f); // Top-right
// rlVertex2f(300.0f, 200.0f); // Bottom-right
// rlVertex2f(100.0f, 200.0f); // Bottom-left
// rlEnd();
rlBegin(RL_QUADS);
rlColor4ub(255, 0, 0, 255); // Red color
rlVertex2f(100.0f, 100.0f); // Top-left
rlVertex2f(100.0f, 200.0f); // Bottom-left
rlVertex2f(300.0f, 200.0f); // Bottom-right
rlVertex2f(300.0f, 100.0f); // Top-right
rlEnd();
// Draw custom text
// DrawCustomText(font, "Custom Font Test", 10.0f, 10.0f, 100, 100, 100, 100);
DrawCustomText(font, "Custom Font Test", 100.0f, 100.0f, 255, 0, 255, 255); // Solid white
rlDrawRenderBatchActive(); // Flush the batch
rlDisableColorBlend(); // Disable blending
// Reset state for ImGui
// glUseProgram(0);
// Render ImGui (on top, 2D)
// ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
glfwSwapBuffers(window);
}
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
igDestroyContext(NULL);
rlglClose();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
//----------------------------------------------------------------------------------
// Module Functions Definitions
//----------------------------------------------------------------------------------
static void ErrorCallback(int error, const char *description) {
fprintf(stderr, "%s", description);
}
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
// Resize callback: Update viewport
static void FramebufferSizeCallback(GLFWwindow* window, int width, int height) {
rlViewport(0, 0, width, height);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment