From a5c21ea30d4823247566976e1c64de1e510c617f Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 22 Feb 2026 22:48:26 -0600 Subject: [PATCH] work on text --- src/include/utils/Objects/Body.h | 4 +- src/include/utils/Renderer/RenderTarget.h | 158 +++++++++-- src/include/utils/Renderer/Renderer.h | 13 +- .../RendererOpenGL/RendererOpenGL.h | 178 ++++++------ .../Archimedes-Modules/Sandbox/JObject.h | 256 +++++++++++++++--- .../Archimedes-Modules/Sandbox/Sandbox.cpp | 29 +- .../Archimedes-Modules/Sandbox/Sandbox.h | 4 + 7 files changed, 480 insertions(+), 162 deletions(-) diff --git a/src/include/utils/Objects/Body.h b/src/include/utils/Objects/Body.h index 8500eed..8c71895 100644 --- a/src/include/utils/Objects/Body.h +++ b/src/include/utils/Objects/Body.h @@ -14,8 +14,8 @@ namespace Archimedes { public: Body(RenderTarget rt, glm::mat4 t = glm::mat4(1.0f)) : Object(t), mesh(rt) {}; - Body(VertexBuffer vb, IndexArray ia, VertexLayout vl, Shader s, RenderMode rm = RenderMode::Triangles, glm::mat4 t = glm::mat4(1.0f)) - : Object(t), mesh(vb, ia, vl, s, rm) {} + Body(VertexBuffer vb, IndexArray ia, VertexLayout vl, Shader s, std::optional tex = std::nullopt, RenderMode rm = RenderMode::Triangles, glm::mat4 t = glm::mat4(1.0f)) + : Object(t), mesh(vb, ia, vl, s, tex, rm) {} Body() : Object(glm::mat4(1.0f)) {} diff --git a/src/include/utils/Renderer/RenderTarget.h b/src/include/utils/Renderer/RenderTarget.h index 7ee758e..d6d1edd 100644 --- a/src/include/utils/Renderer/RenderTarget.h +++ b/src/include/utils/Renderer/RenderTarget.h @@ -17,32 +17,72 @@ namespace Archimedes { Polygon }; + class Texture { + + public: + Texture() {} + Texture(std::vector bin, size_t width, size_t height) + : bin(bin), width(width), height(height) {} + + ~Texture() {} + + const void* getBin() { return bin.data(); } + + void setActive(bool b) { active = b; } + const bool getActive() const { return active; } + + size_t getWidth() { return width; } + size_t getHeight() { return height; } + + unsigned int id; + + private: + std::vector bin; + + bool active = false; + size_t width, height; + }; + class LayoutElement { public: - LayoutElement(unsigned int type, size_t count) : type(type), count(count) {} + + enum class Type { + Float, + UInt, + Int, + Double + }; + + LayoutElement(Type type, size_t count, size_t stride, size_t offset) + : type(type), count(count), stride(stride), offset(offset) {} ~LayoutElement() {} - unsigned int getType() const { return type; } - - size_t getSize() const { return size; } + Type getType() const { return type; } size_t getCount() const { return count; } + + size_t getStride() const { return stride; } + + size_t getOffset() const { return offset; } + private: - unsigned int type; - size_t size; + Type type; size_t count; + size_t stride; + size_t offset; }; class VertexLayout { public: VertexLayout() {} + VertexLayout(std::vector elements) : elements(elements) {} ~VertexLayout() {} - const std::vector& getElements() const { return elements; } + std::vector& getElements() { return elements; } private: std::vector elements; @@ -58,11 +98,15 @@ namespace Archimedes { ~VertexBuffer() {} + void setActive(bool b) { active = b; } + const bool getActive() const { return active; } + const void* getData() const { return data.data(); } size_t getSize() const { return data.size() * sizeof(float); } unsigned int id; private: + bool active = false; std::vector data; }; @@ -74,9 +118,12 @@ namespace Archimedes { ~VertexArray() {} + void setActive(bool b) { active = b; } + const bool getActive() const { return active; } unsigned int id; private: + bool active = false; }; class IndexArray { @@ -89,11 +136,15 @@ namespace Archimedes { ~IndexArray() {} + void setActive(bool b) { active = b; } + const bool getActive() const { return active; } + const void* getIndicies() const { return indices.data(); } size_t getCount() const { return indices.size(); } unsigned int id; private: + bool active = false; std::vector indices; }; @@ -118,14 +169,31 @@ namespace Archimedes { void init(const std::string& vs, const std::string& fs, LoadType loadType) { switch(loadType) { case LoadType::FromSource: - this->vs = vs; - this->fs = fs; + this->shader = source { vs, fs }; break; case LoadType::FromBin: break; case LoadType::FromFileSource: - this->vs = readSourceFile(vs); - this->fs = readSourceFile(fs); + this->shader = source { + readSourceFile(vs), + readSourceFile(fs) + }; + break; + case LoadType::FromFileBin: + break; + default: + break; + } + } + + void init(const std::vector& vs, const std::vector& fs, LoadType loadType) { + switch(loadType) { + case LoadType::FromSource: + break; + case LoadType::FromBin: + this->shader = bin { vs, fs }; + break; + case LoadType::FromFileSource: break; case LoadType::FromFileBin: break; @@ -134,13 +202,31 @@ namespace Archimedes { } } - std::string getVSource() const { return vs; } - std::string getFSource() const { return fs; } + void setActive(bool b) { active = b; } + const bool getActive() const { return active; } + + std::string getVSource() const { return std::get(shader).vertex; } + std::string getFSource() const { return std::get(shader).fragment; } + + std::vector& getVbin() { return std::get(shader).vertex; } + std::vector& getFbin() { return std::get(shader).fragment; } unsigned int id; private: - std::string vs; - std::string fs; + + struct source { + std::string vertex; + std::string fragment; + }; + + struct bin { + std::vector vertex; + std::vector fragment; + }; + + std::variant shader; + + bool active = false; std::string readSourceFile(const std::string& path) { @@ -159,25 +245,38 @@ namespace Archimedes { return s; } + + std::vector readBinFile(const std::string& path) { + + std::ifstream file(path, std::ios::binary); + + if(!file.is_open()) { + return std::vector(); + } + + std::vector s; + + s.reserve(10000); + + while(!file.eof()) { + s.push_back(file.get()); + } + + return s; + + } }; class RenderTarget { public: - RenderTarget(const std::vector data, const std::vector indices, VertexLayout layout, const std::string& vs, const std::string& fs, RenderMode rm = RenderMode::Triangles) - : vertexBuffer(data), - indexArray(indices), - layout(layout), - shader(vs, fs, Shader::LoadType::FromFileSource), - renderMode(rm) { - } - - RenderTarget(VertexBuffer vb, IndexArray ia, VertexLayout vl, Shader s, RenderMode rm = RenderMode::Triangles) + RenderTarget(VertexBuffer vb, IndexArray ia, VertexLayout vl, Shader s, std::optional t = std::nullopt, RenderMode rm = RenderMode::Triangles) : vertexBuffer(vb), indexArray(ia), layout(vl), shader(s), + texture(t), renderMode(rm) { } @@ -186,6 +285,15 @@ namespace Archimedes { ~RenderTarget() {} + void setup(VertexBuffer vb, IndexArray ia, VertexLayout vl, Shader s, std::optional t = std::nullopt, RenderMode rm = RenderMode::Triangles) { + vertexBuffer = vb; + indexArray = ia; + layout = vl; + shader = s; + texture = t; + renderMode = rm; + } + void setActive(bool b) { active = b; } const bool getActive() const { return active; } @@ -201,6 +309,8 @@ namespace Archimedes { Shader shader; + std::optional texture; + RenderMode renderMode; bool active = false; diff --git a/src/include/utils/Renderer/Renderer.h b/src/include/utils/Renderer/Renderer.h index 6b87136..848a5df 100644 --- a/src/include/utils/Renderer/Renderer.h +++ b/src/include/utils/Renderer/Renderer.h @@ -26,16 +26,11 @@ namespace Archimedes { virtual void render() = 0; - virtual Shader createShader(const std::string& vs, const std::string& fs, const Shader::LoadType& lt) = 0; - virtual void setupShader(Shader& shader) = 0; - - virtual RenderTarget createRenderTarget( - VertexBuffer vb, - IndexArray ia, - VertexLayout layout, - Shader& s - ) = 0; + + virtual void setupTexture(Texture& texture) = 0; + + virtual void updateTexture(Texture& texture) = 0; virtual void setupRenderTarget(RenderTarget& rt) = 0; diff --git a/src/include/utils/Renderer/RendererImpl/RendererOpenGL/RendererOpenGL.h b/src/include/utils/Renderer/RendererImpl/RendererOpenGL/RendererOpenGL.h index c5fa805..9d0ce48 100644 --- a/src/include/utils/Renderer/RendererImpl/RendererOpenGL/RendererOpenGL.h +++ b/src/include/utils/Renderer/RendererImpl/RendererOpenGL/RendererOpenGL.h @@ -39,59 +39,6 @@ namespace Archimedes { } - Shader createShader(const std::string& vs, const std::string& fs, const Shader::LoadType& lt) override { - - - Shader shader(vs, fs, lt); - - std::string vss = shader.getVSource(); - std::string fss = shader.getFSource(); - - const char* vsc = vss.c_str(); - const char* fsc = fss.c_str(); - - unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vsc, NULL); - glCompileShader(vertexShader); - // check for shader compile errors - int success; - char infoLog[512]; - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - if (!success) - { - glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); - std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; - } - // fragment shader - unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fsc, NULL); - glCompileShader(fragmentShader); - // check for shader compile errors - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); - if (!success) - { - glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); - std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; - } - // link shaders - unsigned int shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glLinkProgram(shaderProgram); - // check for linking errors - glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); - std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; - } - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - shader.id = shaderProgram; - - return shader; - } - void setupShader(Shader& shader) override { std::string vss = shader.getVSource(); @@ -141,14 +88,16 @@ namespace Archimedes { } - RenderTarget createRenderTarget(VertexBuffer vb, IndexArray ia, VertexLayout layout, Shader& s) override { - - auto rt = RenderTarget(vb, ia, layout, s); + void setupTexture(Texture& texture) override { + glGenTextures(1, &texture.id); + updateTexture(texture); + } - setupRenderTarget(rt); - - return rt; - }; + void updateTexture(Texture& texture) override { + glBindTexture(GL_TEXTURE_2D, texture.id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture.getWidth(), texture.getHeight(), 0, GL_ALPHA, GL_UNSIGNED_BYTE, texture.getBin()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } void setupRenderTarget(RenderTarget& rt) override { @@ -157,28 +106,87 @@ namespace Archimedes { glGenBuffers(1, &rt.vertexBuffer.id); glGenBuffers(1, &rt.indexArray.id); + + + rt.vertexArray.setActive(true); + rt.vertexBuffer.setActive(true); + rt.indexArray.setActive(true); + + if(rt.texture.has_value()) { + glGenTextures(1, &rt.texture.value().id); + rt.texture.value().setActive(true); + } + updateRenderTarget(rt); + + rt.setActive(true); }; void updateRenderTarget(RenderTarget& rt) override { + + if(rt.vertexArray.getActive()) { + + glBindVertexArray(rt.vertexArray.id); + + if(rt.vertexBuffer.getActive()) { + glBindBuffer(GL_ARRAY_BUFFER, rt.vertexBuffer.id); + glBufferData(GL_ARRAY_BUFFER, rt.vertexBuffer.getSize(), rt.vertexBuffer.getData(), GL_STATIC_DRAW); + } + + + if(rt.indexArray.getActive()) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rt.indexArray.id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, rt.indexArray.getCount() * sizeof(unsigned int), rt.indexArray.getIndicies(), GL_STATIC_DRAW); + } + + if(rt.texture.has_value()) { + if(rt.texture.value().getActive()) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt.texture.value().id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, rt.texture.value().getWidth(), rt.texture.value().getHeight(), 0, GL_ALPHA, GL_UNSIGNED_BYTE, rt.texture.value().getBin()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + } + + if(rt.shader.getActive()) { + glUseProgram(rt.shader.id); + } + + + unsigned int i = 0; + for(LayoutElement e : rt.layout.getElements()) { + + switch(e.getType()) { + case LayoutElement::Type::Float: + glVertexAttribPointer(i, e.getCount(), GL_FLOAT, GL_FALSE, e.getStride(), (void*)e.getOffset()); + glEnableVertexAttribArray(i); + break; + case LayoutElement::Type::Double: + glVertexAttribPointer(i, e.getCount(), GL_DOUBLE, GL_FALSE, e.getStride(), (void*)e.getOffset()); + glEnableVertexAttribArray(i); + break; + case LayoutElement::Type::Int: + glVertexAttribPointer(i, e.getCount(), GL_INT, GL_FALSE, e.getStride(), (void*)e.getOffset()); + glEnableVertexAttribArray(i); + break; + case LayoutElement::Type::UInt: + glVertexAttribPointer(i, e.getCount(), GL_UNSIGNED_INT, GL_FALSE, e.getStride(), (void*)e.getOffset()); + glEnableVertexAttribArray(i); + break; + default: + break; + } + + i++; + } + + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + } - glBindVertexArray(rt.vertexArray.id); - - glBindBuffer(GL_ARRAY_BUFFER, rt.vertexBuffer.id); - glBufferData(GL_ARRAY_BUFFER, rt.vertexBuffer.getSize(), rt.vertexBuffer.getData(), GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rt.indexArray.id); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, rt.indexArray.getCount() * sizeof(unsigned int), rt.indexArray.getIndicies(), GL_STATIC_DRAW); - - glUseProgram(rt.shader.id); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - }; void freeRenderTarget(RenderTarget& rt) override { @@ -186,6 +194,12 @@ namespace Archimedes { glDeleteVertexArrays(1, &rt.vertexArray.id); glDeleteBuffers(1, &rt.vertexBuffer.id); glDeleteBuffers(1, &rt.indexArray.id); + + rt.vertexArray.setActive(false); + rt.vertexBuffer.setActive(false); + rt.indexArray.setActive(false); + + rt.setActive(false); }; void draw( @@ -210,6 +224,18 @@ namespace Archimedes { unsigned int colorLoc = glGetUniformLocation(rt.shader.id, "color"); glUniform4f(colorLoc, color.r, color.g, color.b, color.a); + + if(rt.texture.has_value()) { + if(rt.texture.value().getActive()) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt.texture.value().id); + + glUniform1i(glGetUniformLocation(rt.shader.id, "tex"), 0); + } + } + glBindVertexArray(rt.vertexArray.id); switch(rt.renderMode) { diff --git a/src/modules/Archimedes-Modules/Sandbox/JObject.h b/src/modules/Archimedes-Modules/Sandbox/JObject.h index 7d90337..e158d29 100644 --- a/src/modules/Archimedes-Modules/Sandbox/JObject.h +++ b/src/modules/Archimedes-Modules/Sandbox/JObject.h @@ -3,7 +3,6 @@ #include "utils/Objects/Body.h" -#define STB_TRUETYPE_IMPLIMENTATION #include #include @@ -12,25 +11,162 @@ namespace Archimedes { class Text { - GLuint tex; + unsigned int tex; std::vector ttf; stbtt_bakedchar cdata[96]; - std::string path; + std::string fontPath; + + std::string vs = "#version 330 core\n" + "layout (location = 0) in vec3 aPos;\n" + "layout (location = 1) in vec2 aTex;\n" + "uniform mat4 model;\n" + "uniform mat4 view;\n" + "uniform mat4 proj;\n" + "out vec2 TexCoord;\n" + "void main()\n" + "{\n" + " gl_Position = proj * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" + " TexCoord = aTex;\n" + "}\0"; + + std::string fs = "#version 330 core\n" + "out vec4 FragColor;\n" + "in vec2 TexCoord;\n" + "uniform sampler2D tex;\n" + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " FragColor = vec4(0.6f, 0.1f, 0.1f, texture(tex, TexCoord).a);\n" + //" FragColor = vec4(1.0f);\n" + "}\n\0"; + public: + Shader shader; + Texture texture; - Text(std::string p) : path(p) {} + Text() {} ~Text() {} - void textInit() { + ////////////////////////////////////// + + // Load a TTF file into a texture and return the character data. +stbtt_packedchar* LoadFont(const char* filename) { + // Load TTF file into memory. + std::ifstream file(filename, std::ios::ate | std::ios::binary); + size_t fileSize = (size_t)file.tellg(); + char* ttfData = (char*)calloc(fileSize + 1, sizeof(char)); + file.seekg(0); + file.read(ttfData, fileSize); + file.close(); + + // Pack TTF into pixel data using stb_truetype. + stbtt_pack_context packContext; + stbtt_packedchar *charData = (stbtt_packedchar*)calloc(126, sizeof(stbtt_packedchar)); + unsigned char* pixels = (unsigned char*)calloc(1024 * 1024, sizeof(char)); + stbtt_PackBegin(&packContext, pixels, 1024, 1024, 1024, 1, NULL); + + // Pack unicode codepoints 0 to 125 into the texture and character data. If a different starting + // point than 0 is picked then lookups in charData array must be offset by that number. + // With 0-125 the uppercase A will be at charData[65]. + // With 32-125 the uppercase A will be at charData[65-32]. + stbtt_PackFontRange(&packContext, (unsigned char*)ttfData, 0, 200.0f, 0, 125, charData); + stbtt_PackEnd(&packContext); + + // Create OpenGL texture with the font pack pixel data. + // Only uses one color channel since font data is a monochrome alpha mask. + GLuint fontTexture; + glGenTextures(1, &fontTexture); + glBindTexture(GL_TEXTURE_2D, fontTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1024, 1024, 0, GL_RED, GL_UNSIGNED_BYTE, pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + return charData; +} + +// Calculate pixel width of a string. +float TextWidth(const char* text, float height, stbtt_packedchar *charData) { + float w = 0.0f; + + for (int i = 0; i < strlen(text); i++) { + stbtt_packedchar c = charData[text[i]]; + w += c.xadvance; + } + // Scale width by rendered height to texture generated height ratio for final size. + return w * (height / 200.0f); +} + + +// Render string using glDrawArrays. +// X indicates the leftmost, center or rightmost point of the based on align (0:left, 1:center, 2:rigght). +// Y is the baseline of the text. +void RenderString(const char* text, float x, float y, float height, int align, stbtt_packedchar *charData, GLuint program) { + // Scale of actual text compared to size on stb_truetype generated texture. + float scale = height / 200.0f; + + // Resolution of window/display for shader position calculations. + float resolution[2] = { 1024, 1024 }; + + float position[2] = {x, y}; + if (align == 1) { + // Center align + position[0] -= TextWidth(text, height, charData) / 2.0f; + } else if (align == 2) { + // Right align + position[0] -= TextWidth(text, height, charData); + } + + glUniform2fv(glGetUniformLocation(program, "resolution"), 1, resolution); + for (int i = 0; i < strlen(text); i++) { + // Lookup current character data from stb_truetype. + stbtt_packedchar c = charData[text[i]]; + + // Position of character in texture. + float charPosition[4] = {static_cast(c.x0), static_cast(c.y0), static_cast(c.x1), static_cast(c.y1)}; + + // Find the actual size of character since fonts can be variable width. + // 'M' usually is wider than '!', 'L' taller than 'o', etc. + // Calculated by substracting start-offset from end-offset. + // xoff is start offset from the left. + // xoff2 is end offset from the left. + // yoff is start offset from the baseline (will be negative if above baseline). + // yoff2 is end offset from the baseline. + float size[2] = {(c.xoff2 - c.xoff) * scale, (c.yoff2 - c.yoff) * scale}; + + // The actual position of character based on its offset form left/baseline. + float renderPos[2] = {position[0] + c.xoff * scale, position[1] - c.yoff2 * scale}; + + glUniform4fv(glGetUniformLocation(program, "charPosition"), 1, charPosition); + glUniform2fv(glGetUniformLocation(program, "position"), 1, renderPos); + glUniform2fv(glGetUniformLocation(program, "size"), 1, size); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Advance x position to start of next character. + position[0] += c.xadvance * scale; + } +} + + +/////////////////////////////////////////// + + void textInit(std::string p) { + + fontPath = p; - ttf.reserve(1<<20); unsigned char temp_bitmap[1024*1024]; - std::ifstream file(path, std::ios::binary); + std::ifstream file(fontPath, std::ios::binary); + + file.seekg(0, std::ios::end); + auto&& size = file.tellg(); + file.seekg(0, std::ios::beg); + + ttf.reserve(size); if(!file.is_open()) { std::cout << "ttf won't open!\n"; @@ -43,28 +179,40 @@ namespace Archimedes { file.close(); - stbtt_BakeFontBitmap(ttf.data(),0, 64.0, temp_bitmap,1024,1024, 32,96, cdata); - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1024,1024,0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + stbtt_BakeFontBitmap(ttf.data(),0, 200.0f, temp_bitmap,1024,1024, 32,96, cdata); + + std::vector bin; + bin.reserve(1024 * 1024); + + for(unsigned char c : temp_bitmap) { + bin.push_back(c); + } + + shader = Shader(vs, fs, Shader::LoadType::FromSource); + texture = Texture(bin, 1024, 1024); } - Body* genText(float x, float y, std::string text) { + Body* genText(std::vector texts) { // assume orthographic projection with units = screen pixels, origin at top left glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, tex); std::vector v; std::vector i; + float x = 0, y = 0, oX = 0, dY = 0; + + std::string text = ""; + + for(std::string s : texts) { + text += s + " "; + } + for(unsigned int j = 0; j < text.length(); j++) { if (text[j] >= 32) { stbtt_aligned_quad q; stbtt_GetBakedQuad(cdata, 512,512, text[j]-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 - + v.push_back(q.x0); v.push_back(q.y0); v.push_back(0.0f); @@ -72,8 +220,7 @@ namespace Archimedes { v.push_back(q.s0); v.push_back(q.t0); - i.push_back(j * 4); - + v.push_back(q.x1); v.push_back(q.y0); @@ -81,10 +228,9 @@ namespace Archimedes { v.push_back(q.s1); v.push_back(q.t0); - - i.push_back(j * 4 + 1); - + + v.push_back(q.x1); v.push_back(q.y1); @@ -92,10 +238,9 @@ namespace Archimedes { v.push_back(q.s1); v.push_back(q.t1); - - i.push_back(j * 4 + 2); - + + v.push_back(q.x0); v.push_back(q.y1); @@ -104,15 +249,27 @@ namespace Archimedes { v.push_back(q.s0); v.push_back(q.t1); + i.push_back(j * 4); + i.push_back(j * 4 + 1); + i.push_back(j * 4 + 2); + i.push_back(j * 4 + 2); i.push_back(j * 4 + 3); + i.push_back(j * 4); + + dY = q.y1 - q.y0; } } + return new Body( - VertexBuffer(v), + VertexBuffer(v), IndexArray(i), - VertexLayout(), - Shader() + VertexLayout({ + LayoutElement(LayoutElement::Type::Float, 3, 5 * sizeof(float), 0), + LayoutElement(LayoutElement::Type::Float, 2, 5 * sizeof(float), 3 * sizeof(float)) + }), + shader, + texture ); } }; @@ -122,10 +279,10 @@ namespace Archimedes { class JObject { public: - JObject() = default; + JObject() {} - void load(std::string path) { - std::ifstream file(path); + void load(std::string pathJSON, std::string pathFont) { + std::ifstream file(pathJSON); if (!file.is_open()) goodJSON = false; document = json::parse(file); file.close(); @@ -136,6 +293,8 @@ namespace Archimedes { cameras = document["Cameras"]; lights = document["Lights"]; actions = document["Actions"]; + + text.textInit(pathFont); } bool isValid() const { return goodJSON; } @@ -157,8 +316,9 @@ namespace Archimedes { return new Body( VertexBuffer(v), IndexArray(i), - VertexLayout(), + layout, shader, + std::nullopt, RenderMode::ConnectedLinesLooped ); @@ -197,8 +357,9 @@ namespace Archimedes { return new Body( VertexBuffer(v), IndexArray(i), - VertexLayout(), + layout, shader, + std::nullopt, RenderMode::ConnectedLinesLooped ); } @@ -237,8 +398,9 @@ namespace Archimedes { return new Body( VertexBuffer(v), IndexArray(i), - VertexLayout(), + layout, shader, + std::nullopt, RenderMode::ConnectedLinesLooped ); } @@ -262,24 +424,25 @@ namespace Archimedes { return new Body( VertexBuffer(v), IndexArray(i), - VertexLayout(), + layout, shader, + std::nullopt, RenderMode::ConnectedLinesLooped ); } Body* createText(json& element, std::string name) { - std::vector v; - std::vector i; - //do something + json lines = element["text"]; - return new Body( - VertexBuffer(v), - IndexArray(i), - VertexLayout(), - shader - ); + std::vector theText; + for (json::const_iterator it = lines.cbegin(); it != lines.cend(); it++) + { + std::string line = *it; + theText.push_back(line); + } + + return text.genText(theText); } void buildObjects() { @@ -303,7 +466,7 @@ namespace Archimedes { objectMap[name] = createPoly(element, name); } if (type == "TEXT") { - objectMap[name] = nullptr; + objectMap[name] = createText(element, name); } } @@ -355,8 +518,13 @@ namespace Archimedes { std::unordered_map objectMap; Shader shader; + + Text text; private: + Archimedes::VertexLayout layout = Archimedes::VertexLayout({ + Archimedes::LayoutElement(Archimedes::LayoutElement::Type::Float, 3, 0, 0) + }); std::unordered_map templateMap; diff --git a/src/modules/Archimedes-Modules/Sandbox/Sandbox.cpp b/src/modules/Archimedes-Modules/Sandbox/Sandbox.cpp index 419fb67..a22fa6b 100644 --- a/src/modules/Archimedes-Modules/Sandbox/Sandbox.cpp +++ b/src/modules/Archimedes-Modules/Sandbox/Sandbox.cpp @@ -3,6 +3,8 @@ #include "Sandbox.h" +#define STB_TRUETYPE_IMPLEMENTATION +#include Sandbox::Sandbox(Archimedes::App* a, void* h) : Archimedes::Module(a, h) { @@ -58,7 +60,7 @@ void Sandbox::onLoad() { cube = Archimedes::Body( Archimedes::VertexBuffer(vertices), Archimedes::IndexArray(indices), - Archimedes::VertexLayout(), + layout, cubeShader ); @@ -67,7 +69,7 @@ void Sandbox::onLoad() { grid = Archimedes::Body( Archimedes::VertexBuffer(gridVertices), Archimedes::IndexArray(gridIndices), - Archimedes::VertexLayout(), + layout, cubeShader ); @@ -115,7 +117,7 @@ void Sandbox::run() { camera.setTransform(glm::lookAt( camPos, - glm::normalize(glm::vec3(glm::cos(camRot.x) * glm::sin(camRot.y), glm::sin(camRot.x), glm::cos(camRot.x) * glm::cos(camRot.y))), + camPos + glm::normalize(glm::vec3(glm::cos(camRot.x) * glm::sin(camRot.y), glm::sin(camRot.x), glm::cos(camRot.x) * glm::cos(camRot.y))), glm::vec3(0.0f, 1.0f, 0.0f) )); @@ -209,13 +211,18 @@ void Sandbox::run() { } else { - static std::string path = "/home/nathan/Projects/tests/buildzone/test.json"; - ImGui::InputText("JSON Path", &path); + static std::string pathJSON = "/home/nathan/Projects/tests/buildzone/test.json"; + static std::string pathFont = "/nix/store/05zf1s3wlw0k90l34iaby4ysrafv6w95-nerd-fonts-fira-code-3.4.0+6.2/share/fonts/truetype/NerdFonts/FiraCode/FiraCodeNerdFontMono-Regular.ttf"; + ImGui::InputText("JSON Path", &pathJSON); + ImGui::InputText("Font Path", &pathFont); if(ImGui::Button("load")) { jObject.shader = cubeShader; - jObject.load(path); + jObject.load(pathJSON, pathFont); + + window->getRenderer()->setupShader(jObject.text.shader); + //window->getRenderer()->setupTexture(jObject.text.texture); jObject.buildObjects(); @@ -257,8 +264,16 @@ bool Sandbox::onEvent(const Archimedes::Event& e) { unsigned int type = app->getEventType(e); ImGuiIO& io = ImGui::GetIO(); (void)io; + + if(type == app->getEventType(Archimedes::ResizeWindowEvent())) { + Archimedes::ResizeWindowEvent event = (Archimedes::ResizeWindowEvent&) e; - if(type == app->getEventType(Archimedes::KeyPressedWindowEvent()) && !io.WantCaptureKeyboard) { + for(auto o : jObject.objectMap) { + if(o.second != nullptr) { + o.second->scaleRel(1.0f / (float)event.height); + } + } + } else if(type == app->getEventType(Archimedes::KeyPressedWindowEvent()) && !io.WantCaptureKeyboard) { return false; } else if(type == app->getEventType(Archimedes::KeyReleasedWindowEvent()) && !io.WantCaptureKeyboard) { diff --git a/src/modules/Archimedes-Modules/Sandbox/Sandbox.h b/src/modules/Archimedes-Modules/Sandbox/Sandbox.h index 1d312e3..489d23c 100644 --- a/src/modules/Archimedes-Modules/Sandbox/Sandbox.h +++ b/src/modules/Archimedes-Modules/Sandbox/Sandbox.h @@ -59,6 +59,10 @@ class Sandbox : public Archimedes::Module { Archimedes::Shader cubeShader, gridShader; Archimedes::Body cube, grid, hexagon; + Archimedes::VertexLayout layout = Archimedes::VertexLayout({ + Archimedes::LayoutElement(Archimedes::LayoutElement::Type::Float, 3, 0, 0) + }); + Archimedes::Camera camera; std::vector gridVertices = {