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); | |
| } |