Created
April 12, 2019 21:13
-
-
Save paulocanedo/719398ca336211a17e1d799b61a82b55 to your computer and use it in GitHub Desktop.
freetype opengl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "app.hpp" | |
#include "include/mapbox/earcut.hpp" | |
#define BEZIER_STEP 0.01f | |
void framebuffer_size_callback(GLFWwindow* window, int width, int height); | |
void processInput(GLFWwindow *window); | |
// settings | |
const unsigned int SCR_WIDTH = 800; | |
const unsigned int SCR_HEIGHT = 600; | |
static int MoveToFunction(FT_Vector *to, void *user); | |
static int LineToFunction(FT_Vector *to, void *user); | |
static int ConicToFunction(FT_Vector *control, FT_Vector *to, void *user); | |
static int CubicToFunction(const FT_Vector *controlOne, | |
const FT_Vector *controlTwo, | |
const FT_Vector *to, | |
void *user); | |
using Coord = float; | |
using N = uint32_t; | |
using Point = std::array<Coord, 2>; | |
struct char_drawing_t | |
{ | |
int control = 0; | |
int seg_count = 0; | |
std::vector<int> first; | |
std::vector<int> count; | |
std::vector<Point> data; | |
} char_drawing; | |
int MoveToFunction(FT_Vector *to, void *user) { | |
char_drawing_t *info = static_cast<char_drawing_t *>(user); | |
info->seg_count++; | |
if(info->control > 0) { | |
info->count.push_back(info->control - info->first.back()); | |
} | |
info->first.push_back(info->control); | |
float x = to->x, y = to->y; | |
info->data.push_back({x, y}); | |
// info->data.push_back(to->x); | |
// info->data.push_back(to->y); | |
info->control++; | |
return 0; | |
} | |
int LineToFunction(FT_Vector *to, void *user) { | |
char_drawing_t *info = static_cast<char_drawing_t *>(user); | |
float x = to->x, y = to->y; | |
info->data.push_back({x, y}); | |
// info->data.push_back(to->x); | |
// info->data.push_back(to->y); | |
info->control++; | |
return 0; | |
} | |
int ConicToFunction(FT_Vector *control, FT_Vector *to, void *user) { | |
char_drawing_t *info = static_cast<char_drawing_t *>(user); | |
Point p0 = info->data.end()[-1]; | |
float px0 = p0[0]; | |
float py0 = p0[1]; | |
// float px0 = info->data.end()[-2]; | |
// float py0 = info->data.end()[-1]; | |
float px1 = control->x; | |
float py1 = control->y; | |
float px2 = to->x; | |
float py2 = to->y; | |
for(float delta=0.0f; delta <= 1.0f; delta += BEZIER_STEP) { | |
float x = pow(1 - delta, 2) * px0 + (1 - delta) * 2 * delta * px1 + delta * delta * px2; | |
float y = pow(1 - delta, 2) * py0 + (1 - delta) * 2 * delta * py1 + delta * delta * py2; | |
info->data.push_back({x, y}); | |
// info->data.push_back(x); | |
// info->data.push_back(y); | |
info->control++; | |
} | |
return 0; | |
} | |
static int CubicToFunction(const FT_Vector *controlOne, | |
const FT_Vector *controlTwo, | |
const FT_Vector *to, | |
void *user) { | |
char_drawing_t *info = static_cast<char_drawing_t *>(user); | |
Point p = info->data.end()[-1]; | |
float px0 = p[0]; | |
float py0 = p[1]; | |
// float px0 = info->data.end()[-2]; | |
// float py0 = info->data.end()[-1]; | |
float px1 = controlOne->x; | |
float py1 = controlOne->y; | |
float px2 = controlTwo->x; | |
float py2 = controlTwo->y; | |
float px3 = to->x; | |
float py3 = to->y; | |
for(float delta=0.0f; delta <= 1.0f; delta += BEZIER_STEP) { | |
float x = pow(1 - delta, 3) * px0 + pow(1 - delta, 2) * 3 * delta * px1 + (1 - delta) * 3 * delta * delta * px2 + delta * delta * delta * px3; | |
float y = pow(1 - delta, 3) * py0 + pow(1 - delta, 2) * 3 * delta * py1 + (1 - delta) * 3 * delta * delta * py2 + delta * delta * delta * py3; | |
info->data.push_back({x, y}); | |
// info->data.push_back(x); | |
// info->data.push_back(y); | |
info->control++; | |
} | |
return 0; | |
} | |
int main() | |
{ | |
// std::vector<std::vector<Point>> teste; | |
// teste.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}}); | |
// teste.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}}); | |
// std::cout << "------------" << std::endl; | |
// std::vector<N> idx = mapbox::earcut<N>(teste); | |
// for(size_t i=0; i<idx.size(); i++) { | |
// std::cout << idx.at(i); | |
// std::cout << ","; | |
// // std::cout << ((i%2 == 0) ? "," : "\n"); | |
// } | |
// std::cout << std::endl; | |
// std::cout << "------------" << std::endl; | |
FT_Library ft_library; | |
FT_Error error = FT_Init_FreeType(&ft_library); | |
if (error) { | |
std::cout << "Failed loading freetype lib" << std::endl; | |
return -1; | |
} | |
FT_ULong code = 'I'; | |
// For simplicity, use the charmap FreeType provides by default; | |
// in most cases this means Unicode. | |
FT_Face face; | |
FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/Ubuntu-R.ttf", 0, &face); | |
// FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/Txt Regular.ttf", 0, &face); | |
// FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/Air Millhouse Outline.ttf", 0, &face); | |
// FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/Camouflage Snow Snow.ttf", 0, &face); | |
// FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/Starcraft Normal.ttf", 0, &face); | |
// FT_New_Face(ft_library, "/home/paulocanedo/Downloads/fonts/wetp.ttf", 0, &face); | |
FT_UInt index = FT_Get_Char_Index(face, code); | |
error = FT_Load_Glyph(face, | |
index, | |
FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); | |
if (error) { | |
std::cout << "Couldn't load the glyph: FT_Load_Glyph() failed" << std::endl; | |
return -1; | |
} | |
FT_Outline_Funcs callbacks; | |
callbacks.move_to = (FT_Outline_MoveTo_Func)&MoveToFunction; | |
callbacks.line_to = (FT_Outline_LineTo_Func)&LineToFunction; | |
callbacks.conic_to = (FT_Outline_ConicTo_Func)&ConicToFunction; | |
callbacks.cubic_to = (FT_Outline_CubicTo_Func)&CubicToFunction; | |
callbacks.shift = 0; | |
callbacks.delta = 0; | |
FT_GlyphSlot slot = face->glyph; | |
FT_Outline &outline = slot->outline; | |
error = FT_Outline_Decompose(&outline, &callbacks, &char_drawing); | |
char_drawing.count.push_back(char_drawing.control - char_drawing.first.back()); | |
if (error) { | |
std::cout << "Couldn't extract the outline: FT_Outline_Decompose() failed"; | |
return -1; | |
} | |
FT_BBox boundingBox; | |
FT_Outline_Get_BBox(&outline, &boundingBox); | |
float xMin = boundingBox.xMin; | |
float yMin = boundingBox.yMin; | |
float xMax = boundingBox.xMax; | |
float yMax = boundingBox.yMax; | |
std::vector<int> indices_count; | |
size_t segcount = char_drawing.seg_count; | |
std::vector<uint32_t*> indices; | |
std::vector<std::vector<Point>> polygon; | |
for(int i=0; i<char_drawing.seg_count; i++) { | |
polygon.clear(); | |
int start = char_drawing.first[i]; | |
int count = char_drawing.count[i]; | |
auto first = char_drawing.data.cbegin() + start; | |
auto last = char_drawing.data.cbegin() + (start + count); | |
std::vector<Point> temp(first, last); | |
polygon.push_back(temp); | |
std::vector<uint32_t> triangles_idx = mapbox::earcut<uint32_t>(polygon); | |
uint32_t *data = triangles_idx.data(); | |
indices_count.push_back(triangles_idx.size()); | |
indices.push_back(data); | |
for(size_t a=0; a<triangles_idx.size(); a++) { | |
std::cout << data[a]; | |
std::cout << ","; | |
} | |
} | |
std::cout << std::endl; | |
uint32_t **temp = indices.data(); | |
std::cout << "(" << indices.size() << ")\n"; | |
// // const uint32_t * const *temp = reinterpret_cast<const uint32_t * const *>(indices.data()); | |
for(size_t i=0; i<indices.size(); i++) { | |
uint32_t count = indices_count[i]; | |
std::cout << "(" << count << ")\n"; | |
for(uint32_t j=0; j<count; j++) { | |
std::cout << temp[i][j]; | |
std::cout << ","; | |
} | |
} | |
std::cout << std::endl; | |
// glfw: initialize and configure | |
// ------------------------------ | |
glfwInit(); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
#ifdef __APPLE__ | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X | |
#endif | |
// glfw window creation | |
// -------------------- | |
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); | |
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; | |
} | |
// glEnable(GL_BLEND); | |
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
// glfwWindowHint(GLFW_SAMPLES, 4); | |
// glEnable(GL_MULTISAMPLE); | |
// glEnable(GL_LINE_SMOOTH); | |
// glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); | |
Shader shader("./shaders/simple.vert", "./shaders/simple.frag"); | |
glm::mat4 model(1.0f); | |
glm::mat4 projection = glm::ortho(xMin, xMax, yMin, yMax, -1.0f, 1.0f); | |
// glm::mat4 projection = glm::ortho(-500.0f, 2000.0f, -500.0f, 2000.0f, -1.0f, 1.0f); | |
std::cout << "char: " << (char)code << std::endl; | |
unsigned int EBO, VBO, VAO; | |
glGenVertexArrays(1, &VAO); | |
glGenBuffers(1, &VBO); | |
glGenBuffers(1, &EBO); | |
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). | |
glBindVertexArray(VAO); | |
float *values = reinterpret_cast<float*>(char_drawing.data.data()); | |
glBindBuffer(GL_ARRAY_BUFFER, VBO); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * char_drawing.data.size() * 2, values, GL_STATIC_DRAW); | |
// glBufferData(GL_ARRAY_BUFFER, sizeof(float) * char_drawing.data.size() * 2, char_drawing.data.data(), GL_STATIC_DRAW); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * indices.size(), indices.data(), GL_STATIC_DRAW); | |
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); | |
glEnableVertexAttribArray(0); | |
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other | |
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary. | |
glBindVertexArray(0); | |
// uncomment this call to draw in wireframe polygons. | |
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); | |
// render loop | |
// ----------- | |
while (!glfwWindowShouldClose(window)) | |
{ | |
// input | |
// ----- | |
processInput(window); | |
// render | |
// ------ | |
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
// draw our first triangle | |
// glUseProgram(shaderProgram); | |
shader.use(); | |
shader.setMat4("model", model); | |
shader.setMat4("projection", projection); | |
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized | |
shader.setVec3("uColor", 1.0f, 0.0f, 0.0f); | |
const void * const *elem_values = reinterpret_cast<const void * const *>(indices.data()); | |
glMultiDrawElements(GL_TRIANGLES, indices_count.data(), GL_UNSIGNED_INT, elem_values, char_drawing.seg_count); | |
// glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); | |
// shader.setVec3("uColor", 0.0f, 0.0f, 1.0f); | |
// glDrawElements(GL_LINES, indices.size(), GL_UNSIGNED_INT, 0); | |
shader.setVec3("uColor", 0.0f, 0.0f, 0.0f); | |
glMultiDrawArrays(GL_LINE_LOOP, char_drawing.first.data(), char_drawing.count.data(), char_drawing.seg_count); | |
// glBindVertexArray(0); // no need to unbind it every time | |
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) | |
// ------------------------------------------------------------------------------- | |
glfwSwapBuffers(window); | |
glfwPollEvents(); | |
} | |
// optional: de-allocate all resources once they've outlived their purpose: | |
// ------------------------------------------------------------------------ | |
glDeleteVertexArrays(1, &VAO); | |
glDeleteBuffers(1, &VBO); | |
// glfw: terminate, clearing all previously allocated GLFW resources. | |
// ------------------------------------------------------------------ | |
glfwTerminate(); | |
FT_Done_FreeType(ft_library); | |
return 0; | |
} | |
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly | |
// --------------------------------------------------------------------------------------------------------- | |
void processInput(GLFWwindow *window) | |
{ | |
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) | |
glfwSetWindowShouldClose(window, true); | |
} | |
// 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment