glDrawArrays(GL_TRIANGLES, 0, worldVertex);
}
- view = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), // Pos
++ glDisableVertexAttribArray(a);
++ glDisableVertexAttribArray(t);
++
+ /******************
+ * UI RENDERING *
+ ******************/
+
- glUniform1i(q, 0);
++ static GLuint uiS = uiShader.getProgram();
++ static GLuint uiS_v = uiShader.getUniform("view");
++ static GLuint uiS_p = uiShader.getUniform("projection");
++ static GLuint uiS_m = uiShader.getUniform("model");
++ static GLuint uiS_a = uiShader.getAttribute("coord2d");
++ static GLuint uiS_t = uiShader.getAttribute("tex_coord");
++ static GLuint uiS_q = uiShader.getUniform("sampler");
++
++ glUseProgram(uiS);
++
++ view = glm::lookAt(glm::vec3(0.0f, 0.0f, 10.0f), // Pos
+ glm::vec3(0.0f, 0.0f, 0.0f), // Facing
+ glm::vec3(0.0f, 1.0f, 0.0f)); // Up
+
+ scale = 1.0f;
+ scaleWidth = static_cast<float>(width) / scale;
+ scaleHeight = static_cast<float>(height) / scale;
+
+ projection = glm::ortho(-(scaleWidth/2), // Left
+ (scaleWidth/2), // Right
+ -(scaleHeight/2), // Bottom
+ (scaleHeight/2), // Top
+ 10.0f, // zFar
+ -10.0f); // zNear
+
+ model = glm::mat4(1.0f);
+
++ glUniformMatrix4fv(uiS_v, 1, GL_FALSE, glm::value_ptr(view));
++ glUniformMatrix4fv(uiS_p, 1, GL_FALSE, glm::value_ptr(projection));
++ glUniformMatrix4fv(uiS_m, 1, GL_FALSE, glm::value_ptr(model));
++
++ glEnableVertexAttribArray(uiS_a);
++ glEnableVertexAttribArray(uiS_t);
++
+ // Update all UI VBOs
+ for (auto& r : uiRenders) {
+ auto& render = r.second;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, render.tex);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, render.normal);
- glUniform1i(n, 1);
++ glUniform1i(uiS_q, 0);
+
- glVertexAttribPointer(a, 3, GL_FLOAT, GL_FALSE,
++ //glActiveTexture(GL_TEXTURE1);
++ //glBindTexture(GL_TEXTURE_2D, render.normal);
++ //glUniform1i(n, 1);
+
+ glBindBuffer(GL_ARRAY_BUFFER, r.first);
+
- glVertexAttribPointer(t, 2, GL_FLOAT, GL_FALSE,
++ glVertexAttribPointer(uiS_a, 3, GL_FLOAT, GL_FALSE,
+ 6*sizeof(float), 0);
++ glVertexAttribPointer(uiS_t, 2, GL_FLOAT, GL_FALSE,
+ 6*sizeof(float), (void*)(3*sizeof(float)));
+ glDrawArrays(GL_TRIANGLES, 0, render.vertex);
+ }
+
++ glDisableVertexAttribArray(uiS_a);
++ glDisableVertexAttribArray(uiS_t);
++
/*************
* CLEANUP *
*************/
-- glDisableVertexAttribArray(a);
-- glDisableVertexAttribArray(t);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_CULL_FACE);
worldShader.addUniform("AmbientLight");
worldShader.addUniform("Flipped");
++ uiShader.createProgram("Shaders/ui.vert", "Shaders/ui.frag");
++
++ uiShader.addUniform("projection");
++ uiShader.addUniform("view");
++ uiShader.addUniform("model");
++
++ uiShader.addAttribute("coord2d");
++ uiShader.addAttribute("tex_coord");
++
++ uiShader.addUniform("sampler");
++
glEnableVertexAttribArray(worldShader.getAttribute("vertex"));
-- glUseProgram(worldShader.getProgram());
++ glEnableVertexAttribArray(uiShader.getAttribute("coord2d"));
// TODO
//glPolygonOffset(1.0, 1.0);
--- /dev/null
- text.x, text.y, text.z,
- d.data[c].offset.first, d.data[c].offset.second,
+#include "text.hpp"
+
+#include "events/render.hpp"
+
+#include <iostream>
+
+void TextSystem::configure([[maybe_unused]] entityx::EntityManager& entities,
+ [[maybe_unused]] entityx::EventManager& events)
+{
+ if (FT_Init_FreeType(&freetype) != 0) {
+ // TODO handle error
+ }
+
+ shouldUpdateVBOs = false;
+}
+
+/**
+ * Draws the text for all entities.
+ */
+void TextSystem::update([[maybe_unused]] entityx::EntityManager& entites,
+ entityx::EventManager& events,
+ [[maybe_unused]] entityx::TimeDelta dt)
+{
+ if (shouldUpdateVBOs) {
+ shouldUpdateVBOs = false;
+ updateVBOs();
+
+ for (auto& data : fontData) {
+ auto& d = data.second;
+ if (d.text.size() == 0)
+ continue;
+
+ // TODO make normal
+ events.emit<NewRenderEvent>(d.vbo, d.tex, 0, d.buffer.size());
+ }
+ }
+}
+
+void TextSystem::loadFont(const std::string& name,
+ const std::string& file,
+ int size)
+{
+ // Find or load font at given size
+ //
+
+ if (fonts.find(file) == fonts.end()) {
+ FT_Face face;
+ if (FT_New_Face(freetype, file.c_str(), 0, &face)) {
+ // TODO handle this error
+ }
+ fonts.emplace(file, face);
+ }
+
+ auto& face = fonts[file];
+ FT_Set_Pixel_Sizes(face, 0, size);
+ fontData.try_emplace(name);
+
+ // Calculate dimensions of final texture
+ //
+
+ float width = 0, height = 0;
+ for (int c = 32; c < 128; c++) {
+ FT_Load_Char(face, c, FT_LOAD_RENDER);
+ width += face->glyph->bitmap.width + 1;
+ height = std::max(height, static_cast<float>(face->glyph->bitmap.rows));
+ }
+
+ // Generate texture to hold entire font
+ //
+
+ auto& font = fontData[name];
+ glGenTextures(1, &font.tex);
+ glGenBuffers(1, &font.vbo);
+ glBindTexture(GL_TEXTURE_2D, font.tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height,
+ 0, GL_RED, GL_UNSIGNED_BYTE, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // // convert red-on-black to RGBA
+ // auto& g = face->glyph;
+ // std::vector<uint32_t> buf (g->bitmap.width * g->bitmap.rows, 0xFFFFFF);
+ // for (auto j = buf.size(); j--;)
+ // buf[j] |= g->bitmap.buffer[j] << 24;
+
+ // Load each character and add it to the texture
+ //
+
+ float offsetX = 0, offsetY = 0;
+ for (int c = 32; c < 128; c++) {
+ FT_Load_Char(face, c, FT_LOAD_RENDER);
+
+ auto* g = face->glyph;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY,
+ g->bitmap.width, g->bitmap.rows,
+ GL_RED, GL_UNSIGNED_BYTE,
+ g->bitmap.buffer);
+
+ auto& d = font.data[c - 32];
+ d.dim = { g->bitmap.width, g->bitmap.rows };
+ d.bitmap = { g->bitmap_left, g->bitmap_top };
+ d.advance = { g->advance.x >> 6, g->advance.y >> 6 };
+
+ d.offset = { offsetX / width, offsetY / height };
+ offsetX += g->bitmap.width;
+ // Keep offsetY at zero?
+ }
+
+ std::cout << "Loaded font: " << file << " (size: " << size << ", tex: "
+ << font.tex << ")" << std::endl;
+}
+
+void TextSystem::put(const std::string& font,
+ float x,
+ float y,
+ const std::string& text)
+{
+ if (fontData.find(font) == fontData.end())
+ return;
+
+ fontData[font].text.emplace_back(text, x, y, -9.0f);
+ shouldUpdateVBOs = true;
+}
+
+void TextSystem::updateVBOs(void)
+{
+ for (auto& data : fontData) {
+ auto& d = data.second;
+ d.buffer.clear();
+ for (auto& text : d.text) {
++ float cOff = 0.0f;
+ for (char c : text.text) {
+ if (c < 32)
+ continue;
+
+ d.buffer += {
++ text.x+cOff,
++ text.y,
++ text.z,
++ d.data[c].offset.first,
++ d.data[c].offset.second+d.data[c].dim.second,
+ 1.0f
+ };
++ d.buffer += {
++ text.x+cOff+d.data[c].dim.first,
++ text.y,
++ text.z,
++ d.data[c].offset.first+d.data[c].dim.first,
++ d.data[c].offset.second+d.data[c].dim.second,
++ 1.0f
++ };
++ d.buffer += {
++ text.x+cOff,
++ text.y+d.data[c].dim.second,
++ text.z,
++ d.data[c].offset.first,
++ d.data[c].offset.second,
++ 1.0f
++ };
++
++ d.buffer += {
++ text.x+cOff+d.data[c].dim.first,
++ text.y,
++ text.z,
++ d.data[c].offset.first+d.data[c].dim.first,
++ d.data[c].offset.second+d.data[c].dim.second,
++ 1.0f
++ };
++ d.buffer += {
++ text.x+cOff+d.data[c].dim.first,
++ text.y+d.data[c].dim.second,
++ text.z,
++ d.data[c].offset.first+d.data[c].dim.first,
++ d.data[c].offset.second,
++ 1.0f
++ };
++ d.buffer += {
++ text.x+cOff,
++ text.y+d.data[c].dim.second,
++ text.z,
++ d.data[c].offset.first+d.data[c].dim.first,
++ d.data[c].offset.second,
++ 1.0f
++ };
++
++ cOff += d.data[c].dim.first + d.data[c].advance.first;
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, d.vbo);
+ glBufferData(GL_ARRAY_BUFFER,
+ d.text.size() * sizeof(TextMeshData), d.text.data(),
+ GL_STREAM_DRAW);
+ }
+}
+