Files
OpenEngine/application/include/overlay.hpp
2026-02-03 19:27:34 +01:00

253 lines
8.9 KiB
C++

#ifndef OVERLAY_HPP
#define OVERLAY_HPP
/*
"""
Portions of this software are copyright © 2025 The FreeType
Project (https://freetype.org). All rights reserved.
"""
*/
#include <open_engine.hpp>
#include "shmup.hpp"
#include <glm/ext/matrix_clip_space.hpp>
#include "freetype/fttypes.h"
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include <glad/glad.h>
#include <map>
struct Character {
unsigned int texture_id; // ID handle of the glyph texture
glm::ivec2 size; // Size of glyph
glm::ivec2 bearing; // Offset from baseline to left/top of glyph
FT_Long advance; // Offset to advance to next glyph
};
class Overlay : public OpenEngine::Layer
{
public:
Overlay(OpenEngine::Ref<Shmup>& shmup)
: game_layer(shmup)
{};
~Overlay() {};
void RenderText(std::string text, float x, float y, float scale, glm::vec3 color)
{
// activate corresponding render state
text_shader->Bind();
text_shader->SetVec3("textColor", glm::vec3(color.x, color.y, color.z));
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
// iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = characters[*c];
float xpos = x + ch.bearing.x * scale;
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
float w = ch.size.x * scale;
float h = ch.size.y * scale;
// update VBO for each character
float vertices[6][4] = {
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos, ypos, 0.0f, 1.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos + w, ypos + h, 1.0f, 0.0f }
};
// render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
// update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
glm::vec2 GetTextSize(const std::string& text, float scale)
{
float width = 0.0f;
float height = 0.0f;
for (char c : text)
{
Character ch = characters[c];
width += (ch.advance >> 6) * scale; // Accumulate total width
// Track the maximum height
float char_height = ch.size.y * scale;
if (char_height > height)
height = char_height;
}
return glm::vec2(width, height);
}
void RenderTextCentered(std::string text, float center_x, float center_y, float scale, glm::vec3 color)
{
glm::vec2 text_size = GetTextSize(text, scale);
// Calculate top-left position to make text centered
float x = center_x - (text_size.x / 2.0f);
float y = center_y - (text_size.y / 2.0f);
RenderText(text, x, y, scale, color);
}
private:
void OnAttach() override
{
OE_INFO("Loading Freetype...");
int error;
FT_Library ft;
error = FT_Init_FreeType(&ft);
if (error != 0) {
OE_ERROR("FREETYPE: Could not init FreeType Library");
return;
}
FT_Face face;
error = FT_New_Face(ft, "/usr/share/fonts/TTF/JetBrainsMono-Regular.ttf", 0, &face);
if (error != 0) {
OE_ERROR("FREETYPE: Failed to load font");
return;
}
FT_Set_Pixel_Sizes(face, 0, 48);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++)
{
// load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
OE_ERROR("FREETYTPE: Failed to load Glyph: {}", c);
continue;
}
// generate texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now store character for later use
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x
};
characters.insert(std::pair<char, Character>(c, character));
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
FT_Done_Face(face);
FT_Done_FreeType(ft);
text_shader = OpenEngine::Shader::Create("assets/shaders/text.glsl");
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
};
void OnUpdate() override
{
text_shader->Bind();
int width = OpenEngine::Application::Get().GetWindow().GetWidth();
int height = OpenEngine::Application::Get().GetWindow().GetHeight();
projection = glm::ortho(0.0f, static_cast<float>(width),
0.0f, static_cast<float>(height));
text_shader->SetMat4("u_ViewProjection", projection);
float x = height - 40;
float y = 0;
switch (game_layer->GetGameState()) {
case GameState::GameOver:
{
auto& window = OpenEngine::Application::Get().GetWindow();
int width = window.GetWidth();
int height = window.GetHeight();
float x = width / 2.0f;
float y = height / 2.0f;
RenderTextCentered("Game Over!", x, y, 1.0f, glm::vec3(0.8f, 0.2f, 0.3f));
break;
}
case GameState::Paused:
RenderTextCentered("Paused", x, y, 1.0f, glm::vec3(0.8f, 0.2f, 0.3f));
break;
case GameState::Playing:
break;
}
std::string s_score = "Score: " + std::to_string(game_layer->GetScore());
std::string s_lives = std::to_string(game_layer->GetLives()) + " lives";
RenderText(s_score, 10, OpenEngine::Application::Get().GetWindow().GetHeight() - 50, 1.0f, {0.8f, 0.2f, 0.3f});
RenderText(
s_lives,
OpenEngine::Application::Get().GetWindow().GetWidth() - 220,
OpenEngine::Application::Get().GetWindow().GetHeight() - 50,
1.0f,
{0.8f, 0.2f, 0.3f});
};
void OnEvent(OpenEngine::Event& event) override
{
};
private:
std::map<char, Character> characters;
OpenEngine::Ref<OpenEngine::Shader> text_shader;
glm::mat4 projection;
OpenEngine::Ref<Shmup> game_layer;
unsigned int VAO, VBO;
};
#endif // OVERLAY_HPP