diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b3e7acd7..c9345e4c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,7 @@ set(6.pbr set(7.in_practice 1.debugging - # 2.text_rendering + 2.text_rendering ) @@ -174,7 +174,9 @@ add_library(STB_IMAGE "src/stb_image.cpp") set(LIBS ${LIBS} STB_IMAGE) add_library(GLAD "src/glad.c") -set(LIBS ${LIBS} GLAD) +set(LIBS ${LIBS} GLAD freetype) +#https://www.freetype.org/download.html +link_directories(/home/max/opensource/freetype-2.10.0/objs/.libs/) macro(makeLink src dest target) add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink ${src} ${dest} DEPENDS ${dest} COMMENT "mklink ${src} -> ${dest}") @@ -182,6 +184,7 @@ endmacro() # then create a project file per tutorial foreach(CHAPTER ${CHAPTERS}) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin/${CHAPTER}) foreach(DEMO ${${CHAPTER}}) file(GLOB SOURCE "src/${CHAPTER}/${DEMO}/*.h" @@ -189,6 +192,7 @@ foreach(CHAPTER ${CHAPTERS}) "src/${CHAPTER}/${DEMO}/*.vs" "src/${CHAPTER}/${DEMO}/*.fs" "src/${CHAPTER}/${DEMO}/*.gs" + ) set(NAME "${CHAPTER}__${DEMO}") add_executable(${NAME} ${SOURCE}) @@ -208,13 +212,14 @@ foreach(CHAPTER ${CHAPTERS}) # "src/${CHAPTER}/${DEMO}/*.frag" "src/${CHAPTER}/${DEMO}/*.fs" "src/${CHAPTER}/${DEMO}/*.gs" + "src/${CHAPTER}/${DEMO}/*.ttf" ) foreach(SHADER ${SHADERS}) if(WIN32) # configure_file(${SHADER} "test") add_custom_command(TARGET ${NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${SHADER} $) elseif(UNIX AND NOT APPLE) - file(COPY ${SHADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/bin/${CHAPTER}) + configure_file(${SHADER} ${CMAKE_CURRENT_BINARY_DIR}/bin/${CHAPTER} COPYONLY) elseif(APPLE) # create symbolic link for *.vs *.fs *.gs get_filename_component(SHADERNAME ${SHADER} NAME) @@ -229,3 +234,4 @@ foreach(CHAPTER ${CHAPTERS}) endforeach(CHAPTER) include_directories(${CMAKE_SOURCE_DIR}/includes) +include_directories(/home/max/opensource/freetype-2.10.0/include) diff --git a/src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp b/src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp index 82a82e9cf..f8322a080 100644 --- a/src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp +++ b/src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp @@ -137,7 +137,7 @@ int main() // uncomment this call to draw in wireframe polygons. - //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render loop // ----------- @@ -192,4 +192,4 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height) // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); -} \ No newline at end of file +} diff --git a/src/7.in_practice/2.text_rendering/arial.ttf b/src/7.in_practice/2.text_rendering/arial.ttf new file mode 100644 index 000000000..ff0815cd8 Binary files /dev/null and b/src/7.in_practice/2.text_rendering/arial.ttf differ diff --git a/src/7.in_practice/2.text_rendering/text-rendering.cpp b/src/7.in_practice/2.text_rendering/text-rendering.cpp new file mode 100644 index 000000000..90682f9cb --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text-rendering.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +//// GLEW +//#define GLEW_STATIC +//#include +//#include +#include +// GLFW +#include +// GLM +#include +#include +#include +// FreeType +#include +#include FT_FREETYPE_H +// GL includes +#include "learnopengl/shader.h" + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); + +// Properties +const GLuint WIDTH = 800, HEIGHT = 600; + +/// Holds all state information relevant to a character as loaded using FreeType +struct Character { + GLuint TextureID; // ID handle of the glyph texture + glm::ivec2 Size; // Size of glyph + glm::ivec2 Bearing; // Offset from baseline to left/top of glyph + GLuint Advance; // Horizontal offset to advance to next glyph +}; + +std::map Characters; +GLuint VAO, VBO; + +void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color); + +// The MAIN function, from here we start our application and run the Game loop +int main() +{ + // Init GLFW + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +// glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + + GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + +// // Initialize GLEW to setup the OpenGL Function pointers +// glewExperimental = GL_TRUE; +// glewInit(); + + // Define the viewport dimensions +// glViewport(0, 0, WIDTH, HEIGHT); + + // Set OpenGL options + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Compile and setup the shader + Shader shader("text.vs", "text.fs"); + glm::mat4 projection = glm::ortho(0.0f, static_cast(WIDTH), 0.0f, static_cast(HEIGHT)); + shader.use(); +// glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + shader.setMat4("projection", projection); + + // FreeType + FT_Library ft; + // All functions return a value different than 0 whenever an error occurred + if (FT_Init_FreeType(&ft)) + std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; + + // Load font as face + FT_Face face; + if (FT_New_Face(ft, "arial.ttf", 0, &face)) + std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; + + // Set size to load glyphs as + FT_Set_Pixel_Sizes(face, 0, 48); + + // Disable byte-alignment restriction + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // Load first 128 characters of ASCII set + for (GLubyte c = 0; c < 128; c++) + { + // Load character glyph + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + { + std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl; + 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, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + (GLuint)face->glyph->advance.x + }; + Characters.insert(std::pair(c, character)); + } + glBindTexture(GL_TEXTURE_2D, 0); + // Destroy FreeType once we're finished + FT_Done_Face(face); + FT_Done_FreeType(ft); + + + // Configure VAO/VBO for texture quads + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + 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); + + // Game loop + while (!glfwWindowShouldClose(window)) + { + // Check and call events + glfwPollEvents(); + + // Clear the colorbuffer + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f)); + RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f)); + + // Swap the buffers + glfwSwapBuffers(window); + } + + glfwTerminate(); + return 0; +} + +void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) +{ + // Activate corresponding render state + shader.use(); +// glUniform3f(glGetUniformLocation(shader.Program, "textColor"), color.x, color.y, color.z); + shader.setVec3("textColor", 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]; + + GLfloat xpos = x + ch.Bearing.x * scale; + GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale; + + GLfloat w = ch.Size.x * scale; + GLfloat h = ch.Size.y * scale; + // Update VBO for each character + GLfloat vertices[6][4] = { + { xpos, ypos + h, 0.0, 0.0 }, + { xpos, ypos, 0.0, 1.0 }, + { xpos + w, ypos, 1.0, 1.0 }, + + { xpos, ypos + h, 0.0, 0.0 }, + { xpos + w, ypos, 1.0, 1.0 }, + { xpos + w, ypos + h, 1.0, 0.0 } + }; + // Render glyph texture over quad + glBindTexture(GL_TEXTURE_2D, ch.TextureID); + // Update content of VBO memory + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData + + 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 (divide amount of 1/64th pixels by 64 to get amount of pixels)) + } + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); +} +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} diff --git a/src/7.in_practice/2.text_rendering/text.fs b/src/7.in_practice/2.text_rendering/text.fs new file mode 100644 index 000000000..73129f596 --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text.fs @@ -0,0 +1,13 @@ + +#version 330 core +in vec2 TexCoords; +out vec4 color; + +uniform sampler2D text; +uniform vec3 textColor; + +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/src/7.in_practice/2.text_rendering/text.vs b/src/7.in_practice/2.text_rendering/text.vs new file mode 100644 index 000000000..2f835dafa --- /dev/null +++ b/src/7.in_practice/2.text_rendering/text.vs @@ -0,0 +1,11 @@ +#version 330 core +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; +}