77#define STB_IMAGE_IMPLEMENTATION
88#include " stb_image.h"
99
10+ #include < ft2build.h>
11+ #include FT_FREETYPE_H
12+
1013Drawer::Drawer (Game& g)
1114{
15+
1216 // standard rect EBO
1317 {
1418 unsigned int indices[] = {
@@ -24,6 +28,167 @@ Drawer::Drawer(Game& g)
2428 glBufferData (GL_ARRAY_BUFFER, sizeof (indices), indices, GL_STATIC_DRAW);
2529 }
2630
31+ // font stuff with freetype
32+
33+ {
34+ FT_Library ft;
35+ if (FT_Init_FreeType (&ft)) {
36+ std::cout << " ERROR::FREETYPE: Failed to load FreeType\n " ;
37+ std::cin.get ();
38+ }
39+
40+ FT_Face face;
41+ if (FT_New_Face (ft, " f/fonts/arial.ttf" , 0 , &face))
42+ {
43+ std::cout << " ERROR::FREETYPE: Font failed to load arial\n " ;
44+ std::cin.get ();
45+ }
46+ // set width to 0 to dynamically calculate it
47+ FT_Set_Pixel_Sizes (face, 0 , 48 );
48+
49+ /*
50+ * How to render text efficiently
51+ * The easy method is having a seperate texture for each character,
52+ * and then issuing a draw call for each one of them,
53+ * where you specify the texture in question and it's rectangular information
54+ * precisely. Which is very costly.
55+ * Couldn't you just store all the characters in one big image?
56+ * Then in an array indexed by the character, you should store where all the
57+ * characters are exactly in 2D space, and how big they are.
58+ * Let's say you want to render the character A. You pass the shader a uniform
59+ * containing the starting position. In the vertex parameters you give it the character.
60+ * It indexes into the array buffer stored into the GPU containing the correct offsets
61+ * for characters. There it gets the x, y, w and h of the image, which it uses to
62+ * successfully query the texture in question and render a part of it. Let's now say we
63+ * want to do this for a larger amount of characters at a time; We shall then use instanced
64+ * rendering and give each instance it's own letter, but we also have to give it it's
65+ * offset compared to the previous characters. They can't all be drawn onto the same
66+ * place. So the CPU should first examine the text in question, query all the advancements
67+ * of each character and compute where they all should be offset to. Load all that into
68+ * an array containing all offsets. Then instancing will take care of moving through that
69+ * array one time per instance. Thus, all the vertices in a single instance can use it's
70+ * GL_VertexID to compute it's offset as well from the starting position of the character
71+ * because they all share the offset, width and height. The instanced array should also
72+ * contain the character in question, because there will be static memory storing the
73+ * texture offsets for all the characters in an SSBO, which the fragment shader will index
74+ * into, recieving the correct 4 coordintes in some way.
75+ */
76+
77+ unsigned int width = 0 , height = 0 ;
78+
79+ static constexpr int PIX_OFFSET = 10 ;
80+
81+ for (unsigned char c = CHARACTERS_START_AT; c < CHARACTERS; c++)
82+ {
83+ // load character glyph
84+ if (FT_Load_Char (face, c, FT_LOAD_RENDER))
85+ {
86+ std::cout << " ERROR::FREETYTPE: Failed to load Glyph:" << c << std::endl;
87+ continue ;
88+ }
89+ // generate texture
90+ // glTexImage2D(GL_TEXTURE_2D,0, GL_RED, face->glyph->bitmap.width,
91+ // face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer
92+ // );
93+ width += face->glyph ->bitmap .width + PIX_OFFSET;
94+ height = std::max (height, face->glyph ->bitmap .rows );
95+ // now store character for later use
96+ Character character = {
97+ face->glyph ->bitmap .width , face->glyph ->bitmap .rows ,
98+ face->glyph ->bitmap_left , face->glyph ->bitmap_top ,
99+ face->glyph ->advance .x
100+ };
101+ m_characters[c] = character;
102+ }
103+
104+ // generate all standard ascii characters
105+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1 ); // disable byte-alignment restriction
106+
107+ // create texture for use
108+ glGenTextures (1 , &m_main_font_tex);
109+ glBindTexture (GL_TEXTURE_2D, m_main_font_tex); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object
110+
111+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
112+
113+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
114+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
115+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
116+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
117+
118+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_RED, width, height, 0 , GL_RED, GL_UNSIGNED_BYTE, NULL );
119+ int x = PIX_OFFSET;
120+
121+ // exclude loading space by starting at 33.
122+ for (unsigned char c = CHARACTERS_START_AT + 1 ; c < CHARACTERS; c++)
123+ {
124+ // load character glyph
125+ if (FT_Load_Char (face, c, FT_LOAD_RENDER))
126+ {
127+ std::cout << " ERROR::FREETYTPE: Failed to load Glyph:" << c << std::endl;
128+ continue ;
129+ }
130+
131+ // generate texture
132+ glTexSubImage2D (GL_TEXTURE_2D, 0 , x, 0 ,
133+ face->glyph ->bitmap .width , face->glyph ->bitmap .rows ,
134+ GL_RED, GL_UNSIGNED_BYTE, face->glyph ->bitmap .buffer );
135+ x += face->glyph ->bitmap .width + PIX_OFFSET;
136+
137+ }
138+
139+
140+
141+ FT_Done_Face (face);
142+ FT_Done_FreeType (ft);
143+
144+ glPixelStorei (GL_UNPACK_ALIGNMENT, 4 ); // turn back on?
145+
146+
147+ // /////////////// OPENGL buffer stuff
148+
149+ m_text_program = g.l .compile_shader_program (" f/shaders/text.vert" , " f/shaders/text.frag" , " text shader" );
150+ glUseProgram (m_text_program);
151+
152+ glGenVertexArrays (1 , &m_text_VAO);
153+ glBindVertexArray (m_text_VAO);
154+
155+ glGenBuffers (1 , &m_text_VBO);
156+ glBindBuffer (GL_ARRAY_BUFFER, m_text_VBO);
157+ glBufferData (GL_ARRAY_BUFFER, TEXT_ATTRIBUTE_BYTES, NULL , GL_DYNAMIC_DRAW);
158+
159+ glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, standard_rect_EBO);
160+
161+ glEnableVertexAttribArray (0 );
162+ glVertexAttribPointer (0 , TEXT_ATTRIBUTES, GL_FLOAT, GL_FALSE, TEXT_BYTES_PER, (void *)0 );
163+ glVertexAttribDivisor (0 , 1 ); // one per instance
164+
165+ m_text_u_offset_scale = glGetUniformLocation (m_text_program, " u_offset_scale" );
166+
167+ {
168+ // make it the correct amount of data
169+ float data[CHARACTERS][U_TEXT_ATTRIBUTES_PER]; // store the width,height,bearingx,bearingy
170+ for (int i = CHARACTERS_START_AT; i < CHARACTERS; ++i) {
171+ data[i][0 ] = m_characters[i].width ; // width
172+ data[i][1 ] = m_characters[i].height ; // height
173+ data[i][2 ] = m_characters[i].bearing_x ; // bearing X
174+ data[i][3 ] = m_characters[i].bearing_y ; // bearing Y
175+ }
176+
177+ glUniform4fv (glGetUniformLocation (m_text_program, " u_text" ), 128 , (float *)data);
178+ }
179+
180+ {
181+ float char_x_offset_data[CHARACTERS];
182+ float x = 0 .f ;
183+ for (int i = CHARACTERS_START_AT; i < CHARACTERS; ++i) {
184+ char_x_offset_data[i] = x;
185+ x += m_characters[i].width + PIX_OFFSET;
186+ }
187+
188+ glUniform1fv (glGetUniformLocation (m_text_program, " u_char_x_offsets" ), 128 , (float *)char_x_offset_data);
189+ }
190+ }
191+
27192 // rectangle shader
28193 {
29194 rectangle_program = g.l .compile_shader_program (" f/shaders/rectangle.vert" , " f/shaders/rectangle.frag" , " rectangle shader" );
@@ -135,7 +300,7 @@ Drawer::Drawer(Game& g)
135300 constexpr float left = -g.l .WIDTH ;
136301 constexpr float right = -g.G_WIDTH ;
137302
138- constexpr float vertices[] = {
303+ static constexpr float vertices[] = {
139304 // position texture coordinates
140305
141306 // left side of screen
@@ -234,33 +399,85 @@ Drawer::Drawer(Game& g)
234399 }
235400
236401 // load textures
237- constexpr std::array<std::tuple<const char *, int , int >, TEX::TOTAL> data =
238- { {
239- {" f/images/bird.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
240- {" f/images/bird_closed.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
241- {" f/images/mushroom_cap.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
242- {" f/images/mushroom_stem.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
243- {" f/images/side_background.png" , GL_CLAMP_TO_BORDER, GL_REPEAT },
244- {" f/images/vines.png" , GL_REPEAT, GL_REPEAT },
245- {" f/images/sky.png" , GL_REPEAT, GL_REPEAT},
246- {" f/images/sky_blurred.png" , GL_REPEAT, GL_REPEAT},
247- {" f/images/sky2.png" , GL_REPEAT, GL_REPEAT},
248- {" f/images/sky3.png" , GL_REPEAT, GL_REPEAT},
249- {" f/images/storm.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
250- {" f/images/cloud_1.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
251- {" f/images/cloud_2.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
252- {" f/images/cloud_3.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
253- {" f/images/cloud_4.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
254- {" f/images/coin.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
255- {" f/images/coin_blurred.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER}
256- } };
257-
258- for (int i = 0 ; i < TEX::TOTAL; ++i) {
259- auto & d = data[i];
260- tex_sizes[i] = load_texture (std::get<0 >(d), &texs[i], std::get<1 >(d), std::get<2 >(d));
402+ {
403+
404+ static constexpr std::array<std::tuple<const char *, int , int >, TEX::TOTAL> data =
405+ { {
406+ {" f/images/bird.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
407+ {" f/images/bird_closed.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
408+ {" f/images/mushroom_cap.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
409+ {" f/images/mushroom_stem.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
410+ {" f/images/side_background.png" , GL_CLAMP_TO_BORDER, GL_REPEAT },
411+ {" f/images/vines.png" , GL_REPEAT, GL_REPEAT },
412+ {" f/images/sky.png" , GL_REPEAT, GL_REPEAT},
413+ {" f/images/sky_blurred.png" , GL_REPEAT, GL_REPEAT},
414+ {" f/images/sky2.png" , GL_REPEAT, GL_REPEAT},
415+ {" f/images/sky3.png" , GL_REPEAT, GL_REPEAT},
416+ {" f/images/storm.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
417+ {" f/images/cloud_1.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
418+ {" f/images/cloud_2.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
419+ {" f/images/cloud_3.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
420+ {" f/images/cloud_4.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
421+ {" f/images/coin.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER},
422+ {" f/images/coin_blurred.png" , GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER}
423+ } };
424+
425+ for (int i = 0 ; i < TEX::TOTAL; ++i) {
426+ auto & d = data[i];
427+ tex_sizes[i] = load_texture (std::get<0 >(d), &texs[i], std::get<1 >(d), std::get<2 >(d));
428+ }
429+ }
430+
431+ // mess with a texture2D
432+ {
433+ unsigned char data[100 ][100 ][4 ];
434+ for (int i = 0 ; i < 100 ; ++i) {
435+ for (int j = 0 ; j < 100 ; ++j) {
436+ data[i][j][0 ] = 255 *sin (i/20.0 );
437+ data[i][j][1 ] = 0 ;
438+ data[i][j][2 ] = 0 ;
439+ data[i][j][3 ] = 255 ;
440+ }
441+ }
442+
443+ for (int i = 0 ; i < 3 ; ++i) {
444+ glBindTexture (GL_TEXTURE_2D, texs[TEX::side_background]);
445+ glTexSubImage2D (GL_TEXTURE_2D, i, 100 , 100 , 100 , 100 , GL_RGBA, GL_UNSIGNED_BYTE, data);
446+ }
261447 }
262448}
263449
450+ void Drawer::draw_text (const char * text, Color color, float x, float y, float scale)
451+ {
452+ glUseProgram (m_text_program);
453+ glBindVertexArray (m_text_VAO);
454+
455+ glBindTexture (GL_TEXTURE_2D, m_main_font_tex);
456+
457+ float data[TEXT_MAX_CHARS_RENDER][TEXT_ATTRIBUTES];
458+ float char_x_offset = 0 ;
459+ // i will be the total amount of elements later
460+ int i = 0 ;
461+ for (; text[i] != ' \0 ' ; ++i) {
462+ // char
463+ assert (i < TEXT_MAX_CHARS_RENDER);
464+ char c = text[i];
465+ data[i][0 ] = (float )c;
466+ data[i][1 ] = (float )char_x_offset;
467+
468+ // advance is number of 1/64 pixels for some reason
469+ char_x_offset += (m_characters[c].advance >> 6 ) * scale; // bitshift by 6 == * 2^6
470+ }
471+ const int nr_of_chars = i;
472+
473+ glBindBuffer (GL_ARRAY_BUFFER, m_text_VBO);
474+ glBufferSubData (GL_ARRAY_BUFFER, 0 , nr_of_chars * TEXT_BYTES_PER, data);
475+
476+ glUniform3f (m_text_u_offset_scale, x, y, scale);
477+
478+ glDrawElementsInstanced (GL_TRIANGLES, 6 , GL_UNSIGNED_INT, NULL , nr_of_chars);
479+ }
480+
264481void Drawer::draw_rectangle (float x, float y, float w, float h, const Color& color)
265482{
266483 glUseProgram (rectangle_program);
0 commit comments