diff --git a/assets/font/AUTHORS b/assets/font/AUTHORS new file mode 100644 index 0000000..23f7966 --- /dev/null +++ b/assets/font/AUTHORS @@ -0,0 +1,16 @@ +AUTHORS + +Current Contributors (sorted alphabetically): + + - Vishal Vijayraghavan + Project Owner/ Maintainer (Current) + Red Hat, Inc. + +Previous Contributors + - Pravin Satpute + Project Owner/ Maintainer + Red Hat, Inc. + + - Steve Matteson + Original Designer + Ascender, Inc. diff --git a/assets/font/LICENSE b/assets/font/LICENSE new file mode 100644 index 0000000..aba73e8 --- /dev/null +++ b/assets/font/LICENSE @@ -0,0 +1,102 @@ +Digitized data copyright (c) 2010 Google Corporation + with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. + with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +PREAMBLE The goals of the Open Font License (OFL) are to stimulate +worldwide development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. +The fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + + + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. +This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting ? in part or in whole ? +any of the components of the Original Version, by changing formats or +by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components,in + Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the + corresponding Copyright Holder. This restriction only applies to the + primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5) The Font Software, modified or unmodified, in part or in whole, must + be distributed entirely under this license, and must not be distributed + under any other license. The requirement for fonts to remain under + this license does not apply to any document created using the Font + Software. + + + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + + + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. + diff --git a/assets/font/LiberationMono-Bold.ttf b/assets/font/LiberationMono-Bold.ttf new file mode 100644 index 0000000..2e46737 Binary files /dev/null and b/assets/font/LiberationMono-Bold.ttf differ diff --git a/assets/font/LiberationMono-BoldItalic.ttf b/assets/font/LiberationMono-BoldItalic.ttf new file mode 100644 index 0000000..d1f46d7 Binary files /dev/null and b/assets/font/LiberationMono-BoldItalic.ttf differ diff --git a/assets/font/LiberationMono-Italic.ttf b/assets/font/LiberationMono-Italic.ttf new file mode 100644 index 0000000..954c394 Binary files /dev/null and b/assets/font/LiberationMono-Italic.ttf differ diff --git a/assets/font/LiberationMono-Regular.ttf b/assets/font/LiberationMono-Regular.ttf new file mode 100644 index 0000000..e774859 Binary files /dev/null and b/assets/font/LiberationMono-Regular.ttf differ diff --git a/assets/font/LiberationSans-Bold.ttf b/assets/font/LiberationSans-Bold.ttf new file mode 100644 index 0000000..dc5d57f Binary files /dev/null and b/assets/font/LiberationSans-Bold.ttf differ diff --git a/assets/font/LiberationSans-BoldItalic.ttf b/assets/font/LiberationSans-BoldItalic.ttf new file mode 100644 index 0000000..158488a Binary files /dev/null and b/assets/font/LiberationSans-BoldItalic.ttf differ diff --git a/assets/font/LiberationSans-Italic.ttf b/assets/font/LiberationSans-Italic.ttf new file mode 100644 index 0000000..25970d9 Binary files /dev/null and b/assets/font/LiberationSans-Italic.ttf differ diff --git a/assets/font/LiberationSans-Regular.ttf b/assets/font/LiberationSans-Regular.ttf new file mode 100644 index 0000000..e633985 Binary files /dev/null and b/assets/font/LiberationSans-Regular.ttf differ diff --git a/assets/font/LiberationSerif-Bold.ttf b/assets/font/LiberationSerif-Bold.ttf new file mode 100644 index 0000000..3c7c55b Binary files /dev/null and b/assets/font/LiberationSerif-Bold.ttf differ diff --git a/assets/font/LiberationSerif-BoldItalic.ttf b/assets/font/LiberationSerif-BoldItalic.ttf new file mode 100644 index 0000000..6b35d9f Binary files /dev/null and b/assets/font/LiberationSerif-BoldItalic.ttf differ diff --git a/assets/font/LiberationSerif-Italic.ttf b/assets/font/LiberationSerif-Italic.ttf new file mode 100644 index 0000000..54d5164 Binary files /dev/null and b/assets/font/LiberationSerif-Italic.ttf differ diff --git a/assets/font/LiberationSerif-Regular.ttf b/assets/font/LiberationSerif-Regular.ttf new file mode 100644 index 0000000..5e5550c Binary files /dev/null and b/assets/font/LiberationSerif-Regular.ttf differ diff --git a/assets/shaders/debug/debugfont.fs b/assets/shaders/debug/debugfont.fs new file mode 100644 index 0000000..4eedf4f --- /dev/null +++ b/assets/shaders/debug/debugfont.fs @@ -0,0 +1,22 @@ +#version 330 core + +out vec4 FragColor; +in vec3 vPos; +in vec2 vTexCoord; + +uniform sampler2D fontTex; +uniform int charIndex; + +// Main + +void main() { + int x = (charIndex-32) % 16; + int y = (charIndex-32) / 16; + + float fx = float(x) / 16; + float fy = float(y) / 8; + + vec4 color = texture(fontTex, vec2(fx, fy) + vTexCoord * vec2(1.f/32, 1.f/16)); + FragColor = vec3(color) == vec3(0, 0, 0) ? vec4(0, 0, 0, 0) : color; +// FragColor = color; +} \ No newline at end of file diff --git a/assets/shaders/debug/debugfont.vs b/assets/shaders/debug/debugfont.vs new file mode 100644 index 0000000..b6c200f --- /dev/null +++ b/assets/shaders/debug/debugfont.vs @@ -0,0 +1,12 @@ +#version 330 core +in vec3 aPos; +in vec2 aTexCoord; + +out vec3 vPos; +out vec2 vTexCoord; +void main() +{ + gl_Position = vec4(aPos, 1.0); + vPos = aPos; + vTexCoord = aTexCoord; +} diff --git a/assets/shaders/font.fs b/assets/shaders/font.fs index 4eedf4f..14488a9 100644 --- a/assets/shaders/font.fs +++ b/assets/shaders/font.fs @@ -1,22 +1,14 @@ +// https://learnopengl.com/In-Practice/Text-Rendering + #version 330 core +in vec2 TexCoords; +out vec4 color; -out vec4 FragColor; -in vec3 vPos; -in vec2 vTexCoord; +uniform sampler2D text; +uniform vec3 textColor; -uniform sampler2D fontTex; -uniform int charIndex; - -// Main - -void main() { - int x = (charIndex-32) % 16; - int y = (charIndex-32) / 16; - - float fx = float(x) / 16; - float fy = float(y) / 8; - - vec4 color = texture(fontTex, vec2(fx, fy) + vTexCoord * vec2(1.f/32, 1.f/16)); - FragColor = vec3(color) == vec3(0, 0, 0) ? vec4(0, 0, 0, 0) : color; -// FragColor = color; -} \ No newline at end of file +void main() +{ + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); + color = vec4(textColor, 1.0) * sampled; +} \ No newline at end of file diff --git a/assets/shaders/font.vs b/assets/shaders/font.vs index b6c200f..df4facd 100644 --- a/assets/shaders/font.vs +++ b/assets/shaders/font.vs @@ -1,12 +1,13 @@ -#version 330 core -in vec3 aPos; -in vec2 aTexCoord; +// https://learnopengl.com/In-Practice/Text-Rendering + +#version 330 core +layout (location = 0) in vec4 vertex; // +out vec2 TexCoords; + +uniform mat4 projection; -out vec3 vPos; -out vec2 vTexCoord; void main() { - gl_Position = vec4(aPos, 1.0); - vPos = aPos; - vTexCoord = aTexCoord; -} + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + TexCoords = vertex.zw; +} \ No newline at end of file diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 45aa188..11f26f7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -7,6 +7,7 @@ find_package(OpenGL) find_package(glm CONFIG REQUIRED) find_package(ReactPhysics3D REQUIRED) find_package(pugixml 1.15 REQUIRED) +find_package(Freetype) find_package(Stb REQUIRED) include_directories(${Stb_INCLUDE_DIR}) @@ -44,7 +45,7 @@ list(APPEND SOURCES ${AUTOGEN_OUTS}) add_library(openblocks STATIC ${SOURCES}) set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks") target_link_directories(openblocks PUBLIC ${LUAJIT_LIBRARY_DIRS}) -target_link_libraries(openblocks ${GLEW_LIBRARIES} ${LUAJIT_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml) +target_link_libraries(openblocks ${GLEW_LIBRARIES} ${LUAJIT_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml Freetype::Freetype) target_include_directories(openblocks PUBLIC "src" "../include" ${LUAJIT_INCLUDE_DIRS}) add_dependencies(openblocks autogen_build autogen) diff --git a/core/src/rendering/debug/debugrenderer.cpp b/core/src/rendering/debug/debugrenderer.cpp index 002056f..3da7764 100644 --- a/core/src/rendering/debug/debugrenderer.cpp +++ b/core/src/rendering/debug/debugrenderer.cpp @@ -7,19 +7,19 @@ #include extern int viewportWidth, viewportHeight; -extern Texture* fontTexture; -extern Shader* fontShader; +extern Texture* debugFontTexture; +extern Shader* debugFontShader; extern Shader* identityShader; void drawChar(char c, int x, int y, float scale=1.f) { - fontShader->use(); - fontTexture->activate(1); - fontShader->set("fontTex", 1); + debugFontShader->use(); + debugFontTexture->activate(1); + debugFontShader->set("fontTex", 1); - fontShader->set("charIndex", (int)c); + debugFontShader->set("charIndex", (int)c); // https://stackoverflow.com/a/10631263 - int tex = fontShader->getAttribute("aTexCoord"); + int tex = debugFontShader->getAttribute("aTexCoord"); y = viewportHeight - y - 16*scale; float x0 = float(x)/viewportWidth, y0 = float(y)/viewportHeight, x1 = ((float)x + 8*scale)/viewportWidth, y1 = ((float)y + 16*scale)/viewportHeight; diff --git a/core/src/rendering/font.cpp b/core/src/rendering/font.cpp new file mode 100644 index 0000000..e70fdc0 --- /dev/null +++ b/core/src/rendering/font.cpp @@ -0,0 +1,162 @@ +#include "font.h" +#include "logger.h" +#include "panic.h" +#include "rendering/shader.h" + +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +// https://learnopengl.com/In-Practice/Text-Rendering + +FT_Library freetype; +Shader* fontShader; + +extern int viewportWidth, viewportHeight; + +unsigned int textVAO, textVBO; + +void fontInit() { + if (FT_Error err = FT_Init_FreeType(&freetype)) { + Logger::fatalErrorf("Failed to initialize Freetype: [%d]", err); + panic(); + } + + fontShader = new Shader("assets/shaders/font.vs", "assets/shaders/font.fs"); + + // Set up buffer + glGenVertexArrays(1, &textVAO); + glBindVertexArray(textVAO); + + glGenBuffers(1, &textVBO); + glBindBuffer(GL_ARRAY_BUFFER, textVBO); + + // Dynamic, because we update the vertices often V~~~~~~~~~~~~~~ + 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 fontFinish() { + if (FT_Error err = FT_Done_FreeType(freetype)) { + Logger::fatalErrorf("Failed to free Freetype: [%d]", err); + panic(); + } +} + +std::shared_ptr loadFont(std::string fontName) { + std::string fontPath = "assets/font/" + fontName; + + FT_Face face; + if (FT_Error err = FT_New_Face(freetype, fontPath.c_str(), 0, &face)) { + Logger::fatalErrorf("Failed to create font '%s': [%d]", fontName.c_str(), err); + panic(); + } + + std::shared_ptr font = std::make_shared(); + + FT_Set_Pixel_Sizes(face, 0, 48); + + // Load each glyph + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for (unsigned char c = 0; c < 128; c++) { + // load character glyph + if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_RENDER)) { + Logger::errorf("Failed to load glyph %d in font '%s': [%d]", c, fontName.c_str(), err); + 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; + character.ID = texture; + character.size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows); + character.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top); + character.advance = (unsigned int)face->glyph->advance.x; + font->characters[c] = character; + } + + FT_Done_Face(face); + + return font; +} + +void drawText(std::shared_ptr font, std::string text, float x, float y, float scale, glm::vec3 color) { + // activate corresponding render state + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + fontShader->use(); + fontShader->set("textColor", color); + fontShader->set("text", 0); + glActiveTexture(GL_TEXTURE0); + + glm::mat4 projection = glm::ortho(0.0f, (float)viewportWidth, 0.0f, (float)viewportHeight); + fontShader->set("projection", projection); + + // This is not in the learnopengl guide but it is VERY important + // I'm surprised I missed it but honestly... not so much. I'm an idiot + glBindVertexArray(textVAO); + + // iterate through all characters + for (size_t i = 0; i < text.size(); i++) { + unsigned char c = text[i]; + Character ch = font->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; + // render glyph texture over quad + glBindTexture(GL_TEXTURE_2D, ch.ID); + + 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.ID); + // update content of VBO memory + glBindBuffer(GL_ARRAY_BUFFER, textVBO); + 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) + } + glBindTexture(GL_TEXTURE_2D, 0); +} \ No newline at end of file diff --git a/core/src/rendering/font.h b/core/src/rendering/font.h new file mode 100644 index 0000000..4cd3709 --- /dev/null +++ b/core/src/rendering/font.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +// https://learnopengl.com/In-Practice/Text-Rendering + +struct Character { + unsigned int ID; // ID handle of the glyph texture + glm::ivec2 size; // Size of glyph + glm::ivec2 bearing; // Offset from baseline to left/top of glyph + unsigned int advance; // Offset to advance to next glyph +}; + +struct Font { + Character characters[128]; +}; + +void fontInit(); +void fontFinish(); +std::shared_ptr loadFont(std::string fontName); +void drawText(std::shared_ptr font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1)); \ No newline at end of file diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index 29ad631..88156ba 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -23,6 +23,7 @@ #include "math_helper.h" #include "objects/service/selection.h" #include "partassembly.h" +#include "rendering/font.h" #include "rendering/mesh2d.h" #include "rendering/texture.h" #include "rendering/torus.h" @@ -46,14 +47,16 @@ Shader* identityShader = NULL; Shader* ghostShader = NULL; Shader* wireframeShader = NULL; Shader* outlineShader = NULL; -Shader* fontShader = NULL; +Shader* debugFontShader = NULL; Shader* generic2dShader = NULL; extern Camera camera; Skybox* skyboxTexture = NULL; Texture3D* studsTexture = NULL; -Texture* fontTexture = NULL; +Texture* debugFontTexture = NULL; Mesh2D* rect2DMesh = NULL; +std::shared_ptr sansSerif; + bool debugRendererEnabled = false; bool wireframeRendering = false; @@ -74,7 +77,7 @@ void renderInit(GLFWwindow* window, int width, int height) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - fontTexture = new Texture("assets/textures/debugfnt.bmp", GL_RGB); + debugFontTexture = new Texture("assets/textures/debugfnt.bmp", GL_RGB); skyboxTexture = new Skybox({ "assets/textures/skybox/null_plainsky512_lf.jpg", @@ -95,7 +98,7 @@ void renderInit(GLFWwindow* window, int width, int height) { ghostShader = new Shader("assets/shaders/ghost.vs", "assets/shaders/ghost.fs"); wireframeShader = new Shader("assets/shaders/wireframe.vs", "assets/shaders/wireframe.fs"); outlineShader = new Shader("assets/shaders/outline.vs", "assets/shaders/outline.fs"); - fontShader = new Shader("assets/shaders/font.vs", "assets/shaders/font.fs"); + debugFontShader = new Shader("assets/shaders/debug/debugfont.vs", "assets/shaders/debug/debugfont.fs"); generic2dShader = new Shader("assets/shaders/generic2d.vs", "assets/shaders/generic2d.fs"); // Create mesh for 2d rectangle @@ -110,6 +113,10 @@ void renderInit(GLFWwindow* window, int width, int height) { }; rect2DMesh = new Mesh2D(6, rectVerts); + + // Initialize fonts + fontInit(); + sansSerif = loadFont("LiberationSans-Regular.ttf"); } void renderParts() { @@ -356,7 +363,6 @@ void renderHandles() { glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f); screenPos /= screenPos.w; screenPos += 1; screenPos /= 2; screenPos.y = 1 - screenPos.y; screenPos *= glm::vec4(glm::vec2(viewportWidth, viewportHeight), 1, 1); - printVec((glm::vec3)screenPos); drawRect(screenPos.x - 3, screenPos.y - 3, 6, 6, glm::vec3(0, 1, 1)); } @@ -664,6 +670,7 @@ void render(GLFWwindow* window) { renderDebugInfo(); // TODO: Make this a debug flag // renderAABB(); + renderTime = tu_clock_micros() - startTime; } diff --git a/deps.txt b/deps.txt index b23c3e0..ad0a1a1 100644 --- a/deps.txt +++ b/deps.txt @@ -8,4 +8,5 @@ qt6 reactphysics3d pugixml luajit -qscintilla \ No newline at end of file +qscintilla +freetype2 \ No newline at end of file