Addsd the text renderer and internal font (Open-Sans.ttf)

Some graphics testing code added to the core game loop
Gui_Panel_Refactor
Joeyrp 4 years ago
parent 6afa6d8505
commit 7919f34d01

@ -31,6 +31,8 @@ set(LUNARIUM_SRC
"src/graphics/glGraphics.cpp" "src/graphics/glGraphics.cpp"
"src/graphics/glShader.cpp" "src/graphics/glShader.cpp"
"src/graphics/image.cpp" "src/graphics/image.cpp"
"src/graphics/glText.cpp"
"src/graphics/internalFont.cpp"
) )
# add the executable # add the executable

@ -7,7 +7,8 @@ Graphics:
☐ Dear ImGui class with basic initialization ☐ Dear ImGui class with basic initialization
✔ Decide on a font/text rendering system @done (9/7/2021, 1:39:53 PM) ✔ Decide on a font/text rendering system @done (9/7/2021, 1:39:53 PM)
✔ Add FreeType to the project @done (9/7/2021, 2:23:13 PM) ✔ Add FreeType to the project @done (9/7/2021, 2:23:13 PM)
☐ Add a new class for font loading/management and text rendering ✔ Add a new class for font loading/management and text rendering @done (9/7/2021, 3:57:08 PM)
☐ Make the text renderer smarter about breaking up words on multiple lines @low
Input: Input:
☐ Port over the Element2D input system and adjust it to use glfw ☐ Port over the Element2D input system and adjust it to use glfw

@ -176,6 +176,11 @@ namespace lunarium
// Render // Render
mpGraphics->BeginDraw(); mpGraphics->BeginDraw();
// DEBUG: Graphics tests
mpGraphics->DrawFilledEllipse(glm::vec2(600, 300), glm::vec2(100, 150), Color(1.0f, 0.0f, 1.0f, 1.0f), 100);
mpGraphics->DrawString("This is a test of the text renderer!", Rectangle(100, 200, 500, 300),
Color(0.0f, 1.0f, 1.0f, 1.0f), 0.5f, mpGraphics->DefaultFont());
mpGraphics->EndDraw(); mpGraphics->EndDraw();
} }
} }

@ -0,0 +1,36 @@
/******************************************************************************
* File - InternalFont.h
* Author - Joey Pollack
* Date - 2020/01/08 (y/m/d)
* Mod Date - 2021/09/07 (y/m/d)
* Description - Generates a font file (from OpenSans-Regular.ttf)
*
******************************************************************************/
#include "InternalFont.h"
#include "InternalFontData.h"
#include <utils/logger.h>
#include <fstream>
namespace lunarium
{
bool GenerateFontFileAt(const char * filename)
{
std::ofstream ofs(filename, std::ios_base::binary);
if (!ofs.is_open())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Could not generate the default font file at: %s", filename);
return false;
}
ofs.write((const char*)FontData, DataSize);
ofs.close();
ofs.clear();
return true;
}
}

@ -0,0 +1,18 @@
/******************************************************************************
* File - InternalFont.h
* Author - Joey Pollack
* Date - 2020/01/08 (y/m/d)
* Mod Date - 2020/01/08 (y/m/d)
* Description - Generates a font file (from OpenSans-Regular.ttf)
*
******************************************************************************/
#ifndef INTERNAL_FONT_H_
#define INTERNAL_FONT_H_
namespace lunarium
{
bool GenerateFontFileAt(const char* filename);
}
#endif // INTERNAL_FONT_H_

File diff suppressed because it is too large Load Diff

@ -15,6 +15,7 @@ namespace lunarium
struct struct
{ {
friend class OglGraphics; friend class OglGraphics;
friend class glText;
private: private:
const char* DefaultShapeVertex = "#version 330 core\n\ const char* DefaultShapeVertex = "#version 330 core\n\
layout(location = 0) in vec4 vertex;\ layout(location = 0) in vec4 vertex;\
@ -65,30 +66,7 @@ namespace lunarium
color = vec4(spriteColor) * texture(image, TexCoords);\ color = vec4(spriteColor) * texture(image, TexCoords);\
}"; }";
const char* DefaultTextVertex = "#version 330 core\n\
layout(location = 0) in vec4 vertex; \
out vec2 TexCoords;\
\
uniform mat4 projection;\
\
void main()\
{\
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);\
TexCoords = vertex.zw;\
} ";
const char* DefaultTextFragment = "#version 330 core\n\
in vec2 TexCoords;\
out vec4 color;\
\
uniform sampler2D text;\
uniform vec4 textColor;\
\
void main()\
{\
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\
color = vec4(textColor) * sampled;\
}";
} OGLDefaultShaders; } OGLDefaultShaders;
} }

@ -10,6 +10,7 @@
#include "defaultShaders.h" #include "defaultShaders.h"
#include "window.h" #include "window.h"
#include "image.h" #include "image.h"
#include "internalFont.h"
#include <utils/logger.h> #include <utils/logger.h>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
@ -57,6 +58,10 @@ namespace lunarium
} }
mpWindow = pWindow; mpWindow = pWindow;
ResizeCanvas();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (enableDebugMessages) if (enableDebugMessages)
{ {
@ -65,9 +70,28 @@ namespace lunarium
// Initialize the 2D drawing systems // Initialize the 2D drawing systems
InitImageSystem(); InitImageSystem();
// InitTextSystem();
InitShapeSystem(); InitShapeSystem();
// Init text rendering
OpRes res = mText.Initialize();
if (Failed(res))
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Could not initialized the text renderer - %s", res.Description);
}
// Load the default internal font
const char* font = "OpenSans-Regular.ttf";
GenerateFontFileAt(font);
mDefaultFont = mText.LoadFont(font);
if (mDefaultFont < 0)
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::WARNING, "Unable to load the default font: %s", font);
}
else
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::INFO, "Successfully created default font: %s", font);
}
return OpRes::OK(); return OpRes::OK();
} }
@ -169,7 +193,6 @@ namespace lunarium
glBindVertexArray(0); glBindVertexArray(0);
} }
void OglGraphics::DrawFilledEllipse(glm::vec2 center, glm::vec2 radii, Color color, int resolution) void OglGraphics::DrawFilledEllipse(glm::vec2 center, glm::vec2 radii, Color color, int resolution)
{ {
mShapeShader.MakeActive(); mShapeShader.MakeActive();
@ -285,9 +308,10 @@ namespace lunarium
} }
void OglGraphics::DrawString(const char* string, Rectangle boundingArea, Color color, int font) void OglGraphics::DrawString(const char* string, Rectangle boundingArea, Color color, float scale, int font)
{ {
mText.DrawString(font, string, glm::vec2(boundingArea.Left, boundingArea.Top),
glm::vec2(boundingArea.right(), boundingArea.bottom()), color, scale, mProjection);
} }
// Takes raw image data and creates and Image class instance out of it. // Takes raw image data and creates and Image class instance out of it.
@ -306,7 +330,7 @@ namespace lunarium
// For weight, 400 is normal and 700 is bold // For weight, 400 is normal and 700 is bold
int OglGraphics::CreateNewFont(const char* fontName, float size, int weight) int OglGraphics::CreateNewFont(const char* fontName, float size, int weight)
{ {
return -1; return mText.LoadFont(fontName, size, weight);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

@ -12,6 +12,7 @@
#include "igraphics.h" #include "igraphics.h"
#include <glad/gl.h> #include <glad/gl.h>
#include "glShader.h" #include "glShader.h"
#include "glText.h"
#include <map> #include <map>
#include <string> #include <string>
@ -46,7 +47,7 @@ namespace lunarium
virtual void DrawFilledBox(glm::vec2 topLeft, glm::vec2 botRight, Color color); virtual void DrawFilledBox(glm::vec2 topLeft, glm::vec2 botRight, Color color);
virtual void DrawImage(Image& image, glm::vec2 topLeft, Color color); virtual void DrawImage(Image& image, glm::vec2 topLeft, Color color);
virtual void DrawImage(Image& image, Rectangle source, Rectangle destination, Color color); virtual void DrawImage(Image& image, Rectangle source, Rectangle destination, Color color);
virtual void DrawString(const char* string, Rectangle boundingArea, Color color, int font = 0); virtual void DrawString(const char* string, Rectangle boundingArea, Color color, float scale = 1.0f, int font = 0);
// Takes raw image data and creates and Image class instance out of it. // Takes raw image data and creates and Image class instance out of it.
// The raw data must be in one of the ImageFormats and it must be 1 byte per channel. // The raw data must be in one of the ImageFormats and it must be 1 byte per channel.
@ -61,6 +62,9 @@ namespace lunarium
private: // DATA private: // DATA
Window* mpWindow; Window* mpWindow;
glm::mat4 mProjection; glm::mat4 mProjection;
// TEXT
glText mText;
// Sprite Data // Sprite Data
unsigned int mImageVAO; unsigned int mImageVAO;

@ -0,0 +1,227 @@
/******************************************************************************
* File - glText.cpp
* Author - Joey Pollack
* Date - 2021/09/07 (y/m/d)
* Mod Date - 2021/09/07 (y/m/d)
* Description - Used to load and manage fonts. Renders text using freetype
* and openGL.
******************************************************************************/
#include "glText.h"
#include <utils/logger.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <ft2build.h>
#include FT_FREETYPE_H
namespace lunarium
{
int glText::LoadFont(const char* fontFile, float size, int weight)
{
if (!mTextShader.IsBuilt())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "Unable to load font because no text rendering shaders are loaded");
return -1;
}
FT_Library ft;
if (FT_Init_FreeType(&ft))
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "FREETYPE: Could not init FreeType Library");
return -1;
}
FT_Face face;
if (FT_New_Face(ft, fontFile, 0, &face))
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "FREETYPE: Failed to load font from file: %s", fontFile);
return -1;
}
FT_Set_Pixel_Sizes(face, 0, 48);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction
// NOTE: Testing code
float sizes[128] = { 0.0f };
mFonts.push_back(Font());
int fontIdx = (int)(mFonts.size() - 1);
for (GLubyte c = 0; c < 128; c++)
{
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "FREETYTPE: Failed to load Glyph %c from font file %s", c, fontFile);
continue;
}
// Generate texture
GLuint 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,
{ (float)face->glyph->bitmap.width, (float)face->glyph->bitmap.rows },
glm::vec2((float)face->glyph->bitmap_left, (float)face->glyph->bitmap_top),
(int)face->glyph->bitmap.rows - face->glyph->bitmap_top,
(unsigned int)face->glyph->advance.x
};
mFonts.back().CharSet.insert(std::pair<GLchar, Character>(c, character));
// Find Max (positive) Down Shift
if (character.DownShift > 0 && ((int)mFonts.back().MaxDownShift) < character.DownShift)
{
mFonts.back().MaxDownShift = character.DownShift;
}
// Determines the distance from the top down to the baseline
if (character.Size.Height == character.Bearing.y && mFonts.back().MaxHeight < character.Size.Height)
{
mFonts.back().MaxHeight = (unsigned int)character.Size.Height;
}
}
FT_Done_Face(face);
FT_Done_FreeType(ft);
return fontIdx;
}
OpRes glText::Initialize()
{
glGenVertexArrays(1, &mTextVAO);
glGenBuffers(1, &mTextVBO);
glBindVertexArray(mTextVAO);
glBindBuffer(GL_ARRAY_BUFFER, mTextVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Load text shaders
mTextShader.SetSource(glShader::TYPE_VERTEX, VertShader, false);
mTextShader.SetSource(glShader::TYPE_FRAGMENT, FragShader, false);
if (!mTextShader.BuildProgram())
{
return OpRes::Fail("Unable to build text shader program. Fonts will not load and text will not render.");
}
return OpRes::OK();
}
void glText::DrawString(int fontID, const char* text, glm::vec2 position, Color color, float scale, glm::mat4 projection)
{
// HACK: There might be a better way to specify no limit to the bottom right
DrawString(fontID, text, position, glm::vec2(9000000.0f, 9000000.0f), color, scale, projection);
}
void glText::DrawString(int fontID, const char* text, glm::vec2 topLeft, glm::vec2 botRight, Color color, float scale, glm::mat4 projection)
{
if (!mTextShader.IsBuilt())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "Unable to draw string because no text rendering shaders are loaded");
return;
}
if (fontID < 0 || fontID >= mFonts.size())
{
Logger::Log(LogCategory::GRAPHICS, LogLevel::ERROR, "Invalid font ID specified for DrawString: %d", fontID);
return;
}
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// NOTE: String drawing code based on code from: https://learnopengl.com/In-Practice/Text-Rendering
mTextShader.MakeActive();
mTextShader.SetUniformMatrix("projection", 1, glm::value_ptr(projection));
mTextShader.SetUniformf("textColor", { color.Red, color.Green, color.Blue, color.Alpha });
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(mTextVAO);
float lineSize = (mFonts[fontID].MaxHeight + mFonts[fontID].MaxDownShift) * scale;
float curX = topLeft.x;
float curY = topLeft.y;
// Iterate through all characters
int len = (int)strlen(text);
for (int c = 0; c < len; c++)
{
Character ch = mFonts[fontID].CharSet[text[c]];
GLfloat advance = (ch.Advance >> 6) * scale;
if (curX + advance > botRight.x)
{
curX = topLeft.x;
curY += lineSize;
}
if (curY + lineSize > botRight.y)
{
break;
}
GLfloat xpos = curX + ch.Bearing.x * scale;
GLfloat ypos = curY + (mFonts[fontID].MaxHeight - ch.Size.Height) * scale;
// Apply a down offset if the char should go below the baseline
ypos += (ch.DownShift * scale);
// NOTE: The following code does not work because it assumes that positive y is the upward direction
//
//GLfloat test_ypos = curY + (ch.Size.Height - ch.Bearing.Y) * scale;
//
GLfloat w = ch.Size.Width * scale;
GLfloat h = ch.Size.Height * scale;
// Update VBO for each character
GLfloat vertices[6][4] = {
{ xpos, ypos + h, 0.0, 1.0 },
{ xpos + w, ypos, 1.0, 0.0 },
{ xpos, ypos, 0.0, 0.0 },
{ xpos, ypos + h, 0.0, 1.0 },
{ xpos + w, ypos + h, 1.0, 1.0 },
{ xpos + w, ypos, 1.0, 0.0 }
};
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, mTextVBO);
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)
curX += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64)
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
}

@ -0,0 +1,90 @@
/******************************************************************************
* File - glText.h
* Author - Joey Pollack
* Date - 2021/09/07 (y/m/d)
* Mod Date - 2021/09/07 (y/m/d)
* Description - Used to load and manage fonts. Renders text using freetype
* and openGL.
******************************************************************************/
#ifndef GL_TEXT_H_
#define GL_TEXT_H_
#include <map>
#include <vector>
#include <glad/gl.h>
#include "glShader.h"
#include <utils/opRes.h>
#include <utils/types.h>
#include <glm/glm.hpp>
namespace lunarium
{
class glText
{
public:
OpRes Initialize();
int LoadFont(const char* fontFile, float size = 12.0f, int weight = 400);
void DrawString(int fontID, const char* text, glm::vec2 position, Color color, float scale, glm::mat4 projection);
void DrawString(int fontID, const char* text, glm::vec2 topLeft, glm::vec2 botRight, Color color, float scale, glm::mat4 projection);
private:
struct Character
{
unsigned int TextureID; // ID handle of the glyph texture
Sizef Size; // Size of glyph
glm::vec2 Bearing; // Offset from baseline to left/top of glyph
int DownShift; // Size/Amount of glyph below the baseline
unsigned int Advance; // Offset to advance to next glyph
};
struct Font
{
// The tallest character that does not go below the base line
// This is also the distance from the string Y position to the baseline
unsigned int MaxHeight;
// This is the max distance a char will go below the baseline
unsigned int MaxDownShift;
std::map<char, Character> CharSet;
Font() : MaxHeight(0), MaxDownShift(0)
{}
};
std::vector<Font> mFonts;
GLuint mTextVAO;
GLuint mTextVBO;
glShader mTextShader;
private: // SHADER CODE
const char* VertShader = "#version 450 core\n\
layout(location = 0) in vec4 vertex; \
out vec2 TexCoords;\
\
uniform mat4 projection;\
\
void main()\
{\
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);\
TexCoords = vertex.zw;\
} ";
const char* FragShader = "#version 450 core\n\
in vec2 TexCoords;\
out vec4 color;\
\
uniform sampler2D text;\
uniform vec4 textColor;\
\
void main()\
{\
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\
color = vec4(textColor) * sampled;\
}";
};
}
#endif // GL_TEXT_H_

@ -55,14 +55,14 @@ namespace lunarium
virtual void DrawFilledBox(glm::vec2 topLeft, glm::vec2 botRight, Color color) = 0; virtual void DrawFilledBox(glm::vec2 topLeft, glm::vec2 botRight, Color color) = 0;
virtual void DrawImage(Image& image, glm::vec2 topLeft, Color color) = 0; virtual void DrawImage(Image& image, glm::vec2 topLeft, Color color) = 0;
virtual void DrawImage(Image& image, Rectangle source, Rectangle destination, Color color) = 0; virtual void DrawImage(Image& image, Rectangle source, Rectangle destination, Color color) = 0;
virtual void DrawString(const char* string, Rectangle boundingArea, Color color, int font = 0) = 0; virtual void DrawString(const char* string, Rectangle boundingArea, Color color, float scale = 1.0f, int font = 0) = 0;
// Takes raw image data and creates and Image class instance out of it. // Takes raw image data and creates and Image class instance out of it.
// The raw data must be in one of the ImageFormats and it must be 1 byte per channel. // The raw data must be in one of the ImageFormats and it must be 1 byte per channel.
virtual Image* CreateImage(const unsigned char* pData, int width, int height, ImageFormat format) = 0; virtual Image* CreateImage(const unsigned char* pData, int width, int height, ImageFormat format) = 0;
// Fonts // Fonts
int DefaultFont() const; virtual int DefaultFont() const = 0;
// For weight, 400 is normal and 700 is bold // For weight, 400 is normal and 700 is bold
virtual int CreateNewFont(const char* fontName, float size = 12.0f, int weight = 400) = 0; virtual int CreateNewFont(const char* fontName, float size = 12.0f, int weight = 400) = 0;

@ -37,7 +37,8 @@ int main(int argc, char** argv)
std::filesystem::current_path(path); std::filesystem::current_path(path);
// All log messages will go to stdout // All log messages will go to stdout
lunarium::Logger::GetInstance()->AddListener(new lunarium::StandardListener); lunarium::Logger::GetInstance()->AddListener(new lunarium::StandardListener(
lunarium::LogLevel::ANY & ~lunarium::LogLevel::OGL_DEBUG, lunarium::LogCategory::ANY));
lunarium::Core& core = lunarium::Core::GetInstance(); lunarium::Core& core = lunarium::Core::GetInstance();
core.Initialize(argc, argv); core.Initialize(argc, argv);

Loading…
Cancel
Save