From: Andy Belle-Isle Date: Wed, 18 Sep 2019 18:40:23 +0000 (-0400) Subject: Added UI rendering support to render loop, although fonts don't render yet X-Git-Tag: v0.2-alpha~5^2~3 X-Git-Url: https://code.bitgloo.com/?a=commitdiff_plain;h=0869328b12ff9b77000915f37443dcf2468d881e;p=clyne%2Fgamedev2.git Added UI rendering support to render loop, although fonts don't render yet --- 0869328b12ff9b77000915f37443dcf2468d881e diff --cc Scripts/init.lua index d2abb00,d76a402..9e6848e --- a/Scripts/init.lua +++ b/Scripts/init.lua @@@ -8,11 -6,10 +8,11 @@@ player = self.Render.flipx = true; end, MoveLeftReleased = function(self) + game.puts("default", self.Position.x, self.Position.y, "Hey.") - self.Velocity.x = self.Velocity.x + 3 + self.Velocity.x = self.Velocity.x + 3.0 end, MoveRightPressed = function(self) - self.Velocity.x = self.Velocity.x + 3 + self.Velocity.x = self.Velocity.x + 3.0 self.Render.flipx = false; end, MoveRightReleased = function(self) diff --cc Shaders/ui.frag index 0000000,737344e..118108c mode 000000,100644..100644 --- a/Shaders/ui.frag +++ b/Shaders/ui.frag @@@ -1,0 -1,12 +1,16 @@@ ++#version 130 ++ + uniform sampler2D sampler; + -varying vec2 texCoord; -varying vec4 color; ++in vec2 texCoord; ++in vec4 color; ++ ++out vec4 FragColor; + + void main(){ + vec4 pixelColor = texture2D(sampler, vec2(texCoord.x, texCoord.y)); + //TODO allow antialiasing + //if (pixelColor.w != 1.0f) + // discard; - gl_FragColor = pixelColor * color; ++ FragColor = pixelColor * color; + } diff --cc Shaders/ui.vert index 0000000,b2fcba4..ee4f92c mode 000000,100644..100644 --- a/Shaders/ui.vert +++ b/Shaders/ui.vert @@@ -1,0 -1,14 +1,19 @@@ -attribute vec3 coord2d; -attribute vec2 tex_coord; ++#version 130 + -uniform mat4 ortho; -uniform vec4 tex_color; ++in vec3 coord2d; ++in vec2 tex_coord; + -varying vec2 texCoord; -varying vec4 color; ++uniform mat4 projection; ++uniform mat4 view; ++uniform mat4 model; ++ ++out vec2 texCoord; ++out vec4 color; + + void main(){ + texCoord = tex_coord; - color = tex_color; - gl_Position = ortho * vec4(coord2d.xyz, 1.0); ++ color = vec4(1.0, 1.0, 1.0, 1.0); ++ //color = tex_color; ++ //gl_Position = ortho * vec4(coord2d.xyz, 1.0); ++ gl_Position = projection * view * model * vec4(coord2d, 1.0f); + } diff --cc src/render.cpp index 3eea57b,0c92475..1b5ba62 --- a/src/render.cpp +++ b/src/render.cpp @@@ -223,32 -221,32 +223,74 @@@ void RenderSystem::update([[maybe_unuse glDrawArrays(GL_TRIANGLES, 0, worldVertex); } ++ glDisableVertexAttribArray(a); ++ glDisableVertexAttribArray(t); ++ + /****************** + * UI RENDERING * + ******************/ + - view = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), // Pos ++ 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(width) / scale; + scaleHeight = static_cast(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); - glUniform1i(q, 0); ++ glUniform1i(uiS_q, 0); + - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, render.normal); - glUniform1i(n, 1); ++ //glActiveTexture(GL_TEXTURE1); ++ //glBindTexture(GL_TEXTURE_2D, render.normal); ++ //glUniform1i(n, 1); + + glBindBuffer(GL_ARRAY_BUFFER, r.first); + - glVertexAttribPointer(a, 3, GL_FLOAT, GL_FALSE, ++ glVertexAttribPointer(uiS_a, 3, GL_FLOAT, GL_FALSE, + 6*sizeof(float), 0); - glVertexAttribPointer(t, 2, GL_FLOAT, GL_FALSE, ++ 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); @@@ -317,8 -315,8 +359,19 @@@ int RenderSystem::init(void 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); diff --cc src/render.hpp index eabf4be,f3064d1..88668cc --- a/src/render.hpp +++ b/src/render.hpp @@@ -64,11 -52,8 +64,12 @@@ private SDL_GLContext context; Shader worldShader; ++ Shader uiShader; glm::vec3 camPos; + // Map of VBOs and their render data + std::map uiRenders; + GLuint worldVBO = 0; unsigned int worldVertex = 0; GLuint worldTexture = 0; diff --cc src/text.cpp index 1381eb2,0000000..fdb3245 mode 100644,000000..100644 --- a/src/text.cpp +++ b/src/text.cpp @@@ -1,152 -1,0 +1,199 @@@ +#include "text.hpp" + +#include "events/render.hpp" + +#include + +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(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(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 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, text.y, text.z, - d.data[c].offset.first, d.data[c].offset.second, ++ 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); + } +} + diff --cc src/world.cpp index 93cf511,c20ce17..fb7870d --- a/src/world.cpp +++ b/src/world.cpp @@@ -230,7 -229,7 +230,6 @@@ void WorldSystem::update([[maybe_unused if (currentWorld == nullptr) { currentWorld = &(worlds.front()); events.emit(currentWorld); -- std::cout << "Emitted" << std::endl; } if (currentWorld->meshUpdated) {