From 147123b2023a0245f3678b61eb7e59b67db66ba5 Mon Sep 17 00:00:00 2001 From: Redacted Date: Tue, 15 Apr 2025 12:18:14 -0400 Subject: [PATCH 01/18] Working on the default shader ( Need a system for accessing texture units & sampling etc ) --- assets/shader_programs/test_fragment.glsl | 47 +---------------------- assets/shader_programs/test_vertex.glsl | 4 ++ main.cpp | 20 ++-------- src/renderer/OpenGL/internals/internals.h | 2 + src/types/Shader.cpp | 15 -------- 5 files changed, 12 insertions(+), 76 deletions(-) diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index 1c78d74..b813979 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -1,50 +1,7 @@ #version 120 -#ifdef GL_ES -precision mediump float; -#endif - -attribute vec4 gl_Color; - -uniform vec2 u_resolution; -uniform float u_time; - -vec3 rgb2hsb( in vec3 c ){ - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), - vec4(c.gb, K.xy), - step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), - vec4(c.r, p.yzx), - step(p.x, c.r)); - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), - d / (q.x + e), - q.x); -} - -// Function from Iñigo Quiles -// https://www.shadertoy.com/view/MsS3Wc -vec3 hsb2rgb( in vec3 c ){ - vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), - 6.0)-3.0)-1.0, - 0.0, - 1.0 ); - rgb = rgb*rgb*(3.0-2.0*rgb); - return c.z * mix(vec3(1.0), rgb, c.y); -} +varying vec4 v_color; void main(){ - vec2 st = gl_FragCoord.xy/u_resolution; - vec3 color = vec3(0.0); - - // We map x (0.0 - 1.0) to the hue (0.0 - 1.0) - // And the y (0.0 - 1.0) to the brightness - color = hsb2rgb(vec3(st.x,1.0,st.y)); - - gl_FragColor = vec4(color,1.0); - - - gl_FragColor = gl_Color; + gl_FragColor = v_color; } \ No newline at end of file diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index a243a19..1fb19a9 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -1,5 +1,9 @@ #version 120 +// The color manually set with glColor4f, glColor4ubv etc etc. +varying vec4 v_color; + void main() { + v_color = gl_Color; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } diff --git a/main.cpp b/main.cpp index 622e4cc..32769f9 100644 --- a/main.cpp +++ b/main.cpp @@ -113,7 +113,6 @@ Texture* image; Texture* image_mask; RenderTarget* j2d_render_target; Shader shader; -float u_time = 0; class JGLDemoWindow : public ReWindow::OpenGLWindow { @@ -194,7 +193,8 @@ public: J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1); J3D::End(); - J2D::Begin(j2d_render_target, true); + shader.Use(); + J2D::Begin(nullptr, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); @@ -235,11 +235,9 @@ public: J2D::End(); - shader.Use(); - J2D::Begin(); - J2D::DrawRenderTarget(j2d_render_target, {0, 0}); - J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); + //J2D::DrawRenderTarget(j2d_render_target, {0, 0}); + //J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); @@ -248,16 +246,6 @@ public: } void OnRefresh(float elapsed) override { - - u_time += elapsed; - - shader.SetFloat("u_time", u_time); - - auto dimensions = GetSize(); - - shader.SetVector2("u_resolution", Vector2(dimensions.x, dimensions.y)); - - fps = GetRefreshRate(); if (IsKeyDown(Keys::RightArrow)) diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 578b798..7535d24 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace JGL { @@ -23,6 +24,7 @@ public: GLint blend_func[2]; GLuint current_fbo = 0; RenderTarget* current_render_target = nullptr; + //Shader* current_shader = nullptr; bool texture_2D = false; bool texture_coordinate_array = false; diff --git a/src/types/Shader.cpp b/src/types/Shader.cpp index dcc1733..8f3fc68 100644 --- a/src/types/Shader.cpp +++ b/src/types/Shader.cpp @@ -150,32 +150,17 @@ namespace JGL { glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); - // if geometry shader is given, compile geometry shader. - unsigned int geometry; - if (false) { - // const char* gShaderCode = geometry_code.c_str(); - // geometry = glCreateShader(GL_GEOMETRY_SHADER); - // glShaderSource(geometry, 1 &gShaderCode, NULL); - // glCompileShader(geometry); - // checkCompileErrors(geometry, "GEOMETRY"); - } - // shader Program id = glCreateProgram(); glAttachShader(id, vertex); glAttachShader(id, fragment); - if (false) - glAttachShader(id, geometry); - glLinkProgram(id); checkCompileErrors(id, "PROGRAM"); // delete the shaders as they're linked into our program now and are no longer necessary glDeleteShader(vertex); glDeleteShader(fragment); - if (false) - glDeleteShader(geometry); } void Shader::Use() { From af31b417826ad75bf2ae7ab1edc4734e806082cb Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 16 Apr 2025 01:36:39 -0400 Subject: [PATCH 02/18] Implement testing GLSL preprocessing and #include support. --- assets/shader_programs/shared.glsl | 30 +++ assets/shader_programs/test_fragment.glsl | 26 +-- include/JGL/types/Shader.h | 11 +- src/types/Shader.cpp | 224 ++++++++++++++-------- 4 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 assets/shader_programs/shared.glsl diff --git a/assets/shader_programs/shared.glsl b/assets/shader_programs/shared.glsl new file mode 100644 index 0000000..379bf6d --- /dev/null +++ b/assets/shader_programs/shared.glsl @@ -0,0 +1,30 @@ +#ifndef include_shared +#define include_shared +#version 120 + +vec3 rgb2hsb( in vec3 c ){ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), + vec4(c.gb, K.xy), + step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), + vec4(c.r, p.yzx), + step(p.x, c.r)); + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), + d / (q.x + e), + q.x); +} + +// Function from Iñigo Quiles +// https://www.shadertoy.com/view/MsS3Wc +vec3 hsb2rgb( in vec3 c ){ + vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), + 6.0)-3.0)-1.0, + 0.0, + 1.0 ); + rgb = rgb*rgb*(3.0-2.0*rgb); + return c.z * mix(vec3(1.0), rgb, c.y); +} +#endif \ No newline at end of file diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index 1c78d74..27fec00 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -4,36 +4,14 @@ precision mediump float; #endif +#include "shared.glsl" + attribute vec4 gl_Color; uniform vec2 u_resolution; uniform float u_time; -vec3 rgb2hsb( in vec3 c ){ - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), - vec4(c.gb, K.xy), - step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), - vec4(c.r, p.yzx), - step(p.x, c.r)); - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), - d / (q.x + e), - q.x); -} -// Function from Iñigo Quiles -// https://www.shadertoy.com/view/MsS3Wc -vec3 hsb2rgb( in vec3 c ){ - vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), - 6.0)-3.0)-1.0, - 0.0, - 1.0 ); - rgb = rgb*rgb*(3.0-2.0*rgb); - return c.z * mix(vec3(1.0), rgb, c.y); -} void main(){ vec2 st = gl_FragCoord.xy/u_resolution; diff --git a/include/JGL/types/Shader.h b/include/JGL/types/Shader.h index e9c1e23..a4fe40a 100644 --- a/include/JGL/types/Shader.h +++ b/include/JGL/types/Shader.h @@ -24,11 +24,18 @@ class JGL::Shader { public: static inline Event OnCompilationErrorMessage; + static bool HasFile(const std::filesystem::path& path) + { + return std::filesystem::exists(path); + } + + static std::string ReadFile(const std::filesystem::path& path); + /// The default constructor does not initialize any member values. Shader() = default; /// Creates a shader by compiling a vertex and fragment program from the sources in the respective filesystem paths. - Shader(std::filesystem::path vertex_source_path, std::filesystem::path fragment_source_path); + Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path); /// Creates a shader by compiling a vertex and fragment program from the respective GLSL source-strings. Shader(const std::string& vertex_code, const std::string& fragment_code); @@ -110,6 +117,8 @@ protected: private: unsigned int id = 0; std::string vertexPath; + std::string vertexSource; + std::string fragmentSource; std::string fragmentPath; static void checkCompileErrors(GLuint shader, const std::string& type); }; \ No newline at end of file diff --git a/src/types/Shader.cpp b/src/types/Shader.cpp index ac17ff2..6710086 100644 --- a/src/types/Shader.cpp +++ b/src/types/Shader.cpp @@ -3,102 +3,122 @@ #include namespace JGL { - Shader::Shader(std::filesystem::path vertex_source_path, std::filesystem::path fragment_source_path) { - std::string vertex_code; - std::string fragment_code; - std::string geometry_code; // Currently unused, might be supported later. - std::string compute_code; // Currently unused, might be supported later. - std::ifstream vShaderFile; - std::ifstream fShaderFile; - std::ifstream gShaderFile; - std::ifstream cShaderFile; + class ShaderPreprocessor { + public: + std::string include_keyword = "#include"; + ShaderPreprocessor() {} + std::string LoadShaderFile(const std::filesystem::path& filepath) + { + // if size is 0, it indicates, that we are at the top of the recursive load stack + bool stack_top = (already_included.size() == 0); - - vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - cShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - - try { - // Open Files - vShaderFile.open(vertex_source_path); - fShaderFile.open(fragment_source_path); - - std::stringstream vShaderStream, fShaderStream; - - // Read file's buffer contents into streams - vShaderStream << vShaderFile.rdbuf(); - fShaderStream << fShaderFile.rdbuf(); - - // close file handlers - vShaderFile.close(); - fShaderFile.close(); - - - // convert stream into string - - vertex_code = vShaderStream.str(); - fragment_code = fShaderStream.str(); - - if (false) { - gShaderFile.open(""); - std::stringstream gShaderStream; - gShaderStream << gShaderFile.rdbuf(); - gShaderFile.close(); - geometry_code = gShaderStream.str(); + if (stack_top) { + already_included.emplace_back(filepath); } - } catch (std::ifstream::failure& e) { - std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl; + + std::string ret_data = ""; + std::ifstream file(filepath); + + if (!file.good()) + { + std::cout << "ERROR [ ShaderLoader::load_shader ]: Failed to start fstream, check if '" << filepath << "' exists\n"; + return ret_data; + } + + if (file.is_open()) { + std::string line; + while (getline(file, line)) { + if (line.find(include_keyword) == 0) + { + // Get path between double quotes + std::string rel_include_path = extract_first_between(line.substr(include_keyword.length()), '"', '"'); + + // Modify path according to os + // TODO: Use std::filesystem dummy... + // later, let me get this stolen code to work first. + #ifdef _WIN32 + std::replace(rel_include_path.begin(), rel_include_path.end(), '/', '\\'); + #elif __linux__ + std::replace(rel_include_path.begin(), rel_include_path.end(), '\\', '/'); + #endif + std::string full_include_path = extract_path(filepath) + rel_include_path; + + // Avoid including self + if (filepath == full_include_path) { + std::cout << "WARNING [ ShaderLoader::load_shader ]: '"<< filepath <<"' tried to include itself\n"; + continue; + } else { + bool include_flag = true; + // check if the current file is already included + for (const auto& file_to_check : already_included) { + // Avoid duplicate includes + if (file_to_check == full_include_path) { + include_flag = false; + break; + } + } + + // If not yet included, push path to include vector and replace line with file contents + if (include_flag) { + already_included.push_back(full_include_path); + // Repeat recursively. + ret_data += LoadShaderFile(full_include_path) + '\n'; + } + } + } else { + ret_data += line + "\n"; + } + } + file.close(); + } else { + std::cout << "ERROR [ ShaderLoader::load_shader ]: Unable to open file '" << filepath << "'\n"; + } + + // We are back to the first call + if (stack_top) { + already_included.clear(); + } + return ret_data; } - const char* vShaderCode = vertex_code.c_str(); - const char* fShaderCode = fragment_code.c_str(); - // 2. Compile shaders. - unsigned int vertex, fragment; - // vertex shader. - vertex = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex, 1, &vShaderCode, NULL); - glCompileShader(vertex); - checkCompileErrors(vertex, "VERTEX"); - // fragment shader - fragment = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment, 1, &fShaderCode, NULL); - glCompileShader(fragment); - checkCompileErrors(fragment, "FRAGMENT"); + std::string PreprocessShaderSource(const std::string& source); + private: + std::vector already_included; + /// Helper function that strips filename from a path, basically getting the path to the directory containing the file. + std::string extract_path(const std::string& path) + { + // find the position of the last directory separator. + std::size_t pos = path.find_last_of("\\/"); - // if geometry shader is given, compile geometry shader. - unsigned int geometry; - if (false) { - // const char* gShaderCode = geometry_code.c_str(); - // geometry = glCreateShader(GL_GEOMETRY_SHADER); - // glShaderSource(geometry, 1 &gShaderCode, NULL); - // glCompileShader(geometry); - // checkCompileErrors(geometry, "GEOMETRY"); + // strip fname from path + if (pos != std::string::npos) { + return path.substr(0, pos+1); + } + return ""; } + std::string extract_first_between(const std::string& input, char start_symbol, char end_symbol) + { + size_t start_index = input.find(start_symbol); + size_t end_index = input.find(end_symbol, start_index+1); - // shader Program - id = glCreateProgram(); - glAttachShader(id, vertex); - glAttachShader(id, fragment); - - if (false) - glAttachShader(id, geometry); - - glLinkProgram(id); - checkCompileErrors(id, "PROGRAM"); - - // delete the shaders as they're linked into our program now and are no longer necessary - glDeleteShader(vertex); - glDeleteShader(fragment); - if (false) - glDeleteShader(geometry); - - + std::string extracted = ""; + if (start_index != std::string::npos && end_index != std::string::npos) { + extracted = input.substr(start_index + 1, end_index - start_index - 1); + } else { + std::cout << "ERROR [ ShaderLoader::extract_first_between ]: Start '" << start_symbol << "' or end symbol '" << end_symbol << "' not found" << std::endl; + } + return extracted; + } + }; + Shader::Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path) : + Shader(ReadFile(vertex_source_path), ReadFile(fragment_source_path)) { + vertexPath = vertex_source_path; + fragmentPath = fragment_source_path; } void Shader::checkCompileErrors(GLuint shader, const std::string& type) { @@ -132,6 +152,11 @@ namespace JGL { } Shader::Shader(const std::string &vertex_code, const std::string &fragment_code) { + vertexSource = vertex_code; + fragmentSource = fragment_code; + + + const char* vShaderCode = vertex_code.c_str(); const char* fShaderCode = fragment_code.c_str(); @@ -229,4 +254,35 @@ namespace JGL { bool transpose = false; glUniformMatrix4fv(Uniform(name), 16, transpose, value.ptr()); } + + std::string Shader::ReadFile(const std::filesystem::path &path) { + /*std::string contents; + + std::ifstream file; + + file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try { + // Open Files + file.open(path); + std::stringstream stream; + + // Read file's buffer contents into streams + stream << file.rdbuf(); + + // close file handlers + file.close(); + + // convert stream into string + contents = stream.str(); + + return contents; + } catch (std::ifstream::failure& e) { + std::cout << "ERROR::FILE_READ_FAILURE: " << e.what() << std::endl; + return ""; + }*/ + + ShaderPreprocessor processor; + return processor.LoadShaderFile(path); + } } \ No newline at end of file From 4c798ea76af16159ef25bcb6ecd25368be1ad597 Mon Sep 17 00:00:00 2001 From: Redacted Date: Wed, 16 Apr 2025 01:43:00 -0400 Subject: [PATCH 03/18] shader with state stack. --- include/JGL/JGL.h | 3 ++- include/JGL/types/Shader.h | 4 ---- main.cpp | 10 ++++------ src/renderer/OpenGL/J2D.cpp | 10 +++++++--- src/renderer/OpenGL/internals/internals.cpp | 11 ++++++++--- src/renderer/OpenGL/internals/internals.h | 7 ++++--- src/types/Shader.cpp | 12 ------------ 7 files changed, 25 insertions(+), 32 deletions(-) diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 2c5918c..3a843c0 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -30,6 +30,7 @@ #include #include #include +#include // Fonts that are included by default. namespace JGL::Fonts { @@ -73,7 +74,7 @@ namespace JGL::J2D { /// This keeps our code from, say, clobbering the OpenGL rendering context driving 3D content in between our calls. /// @param render_target /// @param clear_buffers - void Begin(RenderTarget* render_target = nullptr, bool clear_buffers = false); + void Begin(RenderTarget* render_target = nullptr, Shader* shader = nullptr, bool clear_buffers = false); /// Closes a 2-D rendering context with the underlying graphics system (In this case& by default OpenGL). /// @see Begin(). diff --git a/include/JGL/types/Shader.h b/include/JGL/types/Shader.h index e4a68d5..9ac8398 100644 --- a/include/JGL/types/Shader.h +++ b/include/JGL/types/Shader.h @@ -36,10 +36,6 @@ public: unsigned int Handle() const; /// Enable this shader. All rendering performed thereafter will be affected by this shader code. /// @see UseDefault. - void Use(); - - static void Use(GLuint shader_program_id); - static void UseDefault(); // TODO: Implement for hot-reloading. void Reload(); diff --git a/main.cpp b/main.cpp index 32769f9..7b4ec93 100644 --- a/main.cpp +++ b/main.cpp @@ -133,7 +133,7 @@ public: image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST, JGL::SampleRate::X16); image_mask = new Texture("assets/sprites/alpha_mask_2.png"); j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false, - SampleRate::NONE, FilteringMode::MIPMAP_TRILINEAR); + SampleRate::X0, FilteringMode::MIPMAP_TRILINEAR); //Texture::MultiplyByAlphaMask(*image, *image_mask); @@ -149,8 +149,6 @@ public: void display() { - Shader::UseDefault(); - pulse += 20 * delta_time; float dt = GetDeltaTime(); @@ -193,9 +191,7 @@ public: J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1); J3D::End(); - shader.Use(); - J2D::Begin(nullptr, true); - + J2D::Begin(nullptr, &shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); @@ -235,10 +231,12 @@ public: J2D::End(); + /* J2D::Begin(); //J2D::DrawRenderTarget(j2d_render_target, {0, 0}); //J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); + */ diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index b44d532..b6e8447 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -4,9 +4,9 @@ #include "internals/internals.h" -void J2D::Begin(RenderTarget* render_target, bool clear_buffers) { +void J2D::Begin(RenderTarget* render_target, Shader* shader, bool clear_buffers) { State new_state = default_state; - state_stack.Push(State::SaveState()); + state_stack.Push(State::SaveState(current_state)); glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -31,13 +31,17 @@ void J2D::Begin(RenderTarget* render_target, bool clear_buffers) { new_state.viewport[3] = window_size.y; } + if (shader) { + new_state.current_shader = shader; + new_state.current_shader_handle = shader->Handle(); + } + State::RestoreState(new_state); current_state = new_state; if (current_state.current_render_target) RenderTarget::SetActiveGLRenderTarget(*current_state.current_render_target); - if (render_target != nullptr && clear_buffers) { glClearColor(render_target->GetClearColor().RedChannelNormalized(), render_target->GetClearColor().GreenChannelNormalized(), render_target->GetClearColor().BlueChannelNormalized(), render_target->GetClearColor().AlphaChannelNormalized()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); diff --git a/src/renderer/OpenGL/internals/internals.cpp b/src/renderer/OpenGL/internals/internals.cpp index 2b32ef7..d1bd013 100644 --- a/src/renderer/OpenGL/internals/internals.cpp +++ b/src/renderer/OpenGL/internals/internals.cpp @@ -133,7 +133,7 @@ JGL::State* JGL::StateStack::PreviousState() { return &states.back(); } -JGL::State JGL::State::SaveState() { +JGL::State JGL::State::SaveState(const State& state) { State result; result.depth_test = glIsEnabled(GL_DEPTH_TEST); @@ -152,6 +152,8 @@ JGL::State JGL::State::SaveState() { glGetFloatv(GL_CURRENT_COLOR, result.draw_color); glGetIntegerv(GL_VIEWPORT, result.viewport); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&result.current_fbo); + glGetIntegerv(GL_CURRENT_PROGRAM, &result.current_shader_handle); + result.current_shader = state.current_shader; glGetIntegerv(GL_BLEND_SRC, &result.blend_func[0]); glGetIntegerv(GL_BLEND_DST, &result.blend_func[1]); @@ -184,7 +186,6 @@ void JGL::State::RestoreState(const State& state) { else glDisable(GL_BLEND); - glActiveTexture(GL_TEXTURE0 + state.selected_texture_unit); glClientActiveTexture(GL_TEXTURE0 + state.selected_texture_unit); @@ -203,10 +204,14 @@ void JGL::State::RestoreState(const State& state) { else glDisableClientState(GL_COLOR_ARRAY); + if (state.current_shader) + glUseProgram(state.current_shader->Handle()); + else + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, state.current_fbo); glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]); glClearColor(state.clear_color[0], state.clear_color[1], state.clear_color[2], state.clear_color[3]); glColor4f(state.draw_color[0], state.draw_color[1], state.draw_color[2], state.draw_color[3]); glBlendFunc(state.blend_func[0], state.blend_func[1]); - } diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 7535d24..139ddf7 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -24,7 +24,8 @@ public: GLint blend_func[2]; GLuint current_fbo = 0; RenderTarget* current_render_target = nullptr; - //Shader* current_shader = nullptr; + GLint current_shader_handle = 0; + Shader* current_shader = nullptr; bool texture_2D = false; bool texture_coordinate_array = false; @@ -40,7 +41,7 @@ public: // List of all lights in the scene. std::vector optional_lights{}; public: - static State SaveState(); + static State SaveState(const State& state); static void RestoreState(const State& state); }; @@ -72,7 +73,7 @@ namespace JGL::J2D { {1, 1, 1, 1}, {0, 0, 0, 0}, {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, - 0, nullptr, false, false, + 0, nullptr, 0, nullptr, false, false, false, false, true, true, true, false, 0, {}, {} diff --git a/src/types/Shader.cpp b/src/types/Shader.cpp index 8f3fc68..a13a723 100644 --- a/src/types/Shader.cpp +++ b/src/types/Shader.cpp @@ -163,22 +163,10 @@ namespace JGL { glDeleteShader(fragment); } - void Shader::Use() { - glUseProgram(id); - } - bool Shader::Loaded() const { return id != 0; } unsigned int Shader::Handle() const { return id;} - void Shader::UseDefault() { - glUseProgram(0); - } - - void Shader::Use(GLuint shader_program_id) { - glUseProgram(shader_program_id); - } - void Shader::SetBool(const std::string &name, bool value) const { glUniform1i(Uniform(name), (int)value); } void Shader::SetInt(const std::string &name, int value) const { glUniform1i(Uniform(name), value); } From 02370c6bfad9ae79ab67168fac9479604ec372dc Mon Sep 17 00:00:00 2001 From: Redacted Date: Thu, 17 Apr 2025 13:33:08 -0400 Subject: [PATCH 04/18] work on the default shader. --- CMakeLists.txt | 2 +- assets/shader_programs/test_fragment.glsl | 50 ++++++++++++++++++++++- assets/shader_programs/test_vertex.glsl | 20 +++++++++ main.cpp | 17 ++++---- src/renderer/OpenGL/J2D.cpp | 27 ++++++++++++ src/renderer/OpenGL/TextRendering.cpp | 16 ++++++++ 6 files changed, 121 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ab8bbd..07a11c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ if (WIN32) ) endif() -#set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra") +set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra") file(COPY "assets" DESTINATION "${PROJECT_BINARY_DIR}") file(GLOB_RECURSE ASSETS "assets/*") diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index b813979..3607003 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -1,7 +1,55 @@ #version 120 +// The color manually set with glColor4f, glColor4ubv etc. varying vec4 v_color; -void main(){ +// The number of texture units that have been set. +uniform int TEXTURE_UNIT_SET_COUNT; + +// True if we should perform the alpha mask operation. +uniform bool ALPHA_MASK; + +// True if we're rendering text. +uniform bool TEXT; + +// Texture unit 0 - 7 (8 - 31 will come later). +uniform sampler2D GL_TEXTURE0; +uniform sampler2D GL_TEXTURE1; +uniform sampler2D GL_TEXTURE2; +uniform sampler2D GL_TEXTURE3; +uniform sampler2D GL_TEXTURE4; +uniform sampler2D GL_TEXTURE5; +uniform sampler2D GL_TEXTURE6; +uniform sampler2D GL_TEXTURE7; + +// Texture coordinates. +varying vec2 GL_TEXTURE0_COORD; +varying vec2 GL_TEXTURE1_COORD; +varying vec2 GL_TEXTURE2_COORD; +varying vec2 GL_TEXTURE3_COORD; +varying vec2 GL_TEXTURE4_COORD; +varying vec2 GL_TEXTURE5_COORD; +varying vec2 GL_TEXTURE6_COORD; +varying vec2 GL_TEXTURE7_COORD; + +void DrawColorOnly() { gl_FragColor = v_color; +} + +// TODO fix positive alpha mask. +void SampleTextureUnits() { + if (TEXT) + gl_FragColor = vec4(v_color.rgb, v_color.a * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD).a); + + // Draw sprite, partial sprite, mirror sprite, render target, partial render target. + else if (TEXTURE_UNIT_SET_COUNT == 1) + gl_FragColor = v_color * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD); +} + +void main() { + if (TEXTURE_UNIT_SET_COUNT == 0) { + DrawColorOnly(); return; + } + + SampleTextureUnits(); } \ No newline at end of file diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index 1fb19a9..e1fcb80 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -3,7 +3,27 @@ // The color manually set with glColor4f, glColor4ubv etc etc. varying vec4 v_color; +// The texture coordinates in each texture unit. +varying vec2 GL_TEXTURE0_COORD; +varying vec2 GL_TEXTURE1_COORD; +varying vec2 GL_TEXTURE2_COORD; +varying vec2 GL_TEXTURE3_COORD; +varying vec2 GL_TEXTURE4_COORD; +varying vec2 GL_TEXTURE5_COORD; +varying vec2 GL_TEXTURE6_COORD; +varying vec2 GL_TEXTURE7_COORD; + + void main() { v_color = gl_Color; + GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy; + GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy; + GL_TEXTURE2_COORD = gl_MultiTexCoord2.xy; + GL_TEXTURE3_COORD = gl_MultiTexCoord3.xy; + GL_TEXTURE4_COORD = gl_MultiTexCoord4.xy; + GL_TEXTURE5_COORD = gl_MultiTexCoord5.xy; + GL_TEXTURE6_COORD = gl_MultiTexCoord6.xy; + GL_TEXTURE7_COORD = gl_MultiTexCoord7.xy; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } diff --git a/main.cpp b/main.cpp index 3cb8d35..695ce52 100644 --- a/main.cpp +++ b/main.cpp @@ -133,7 +133,7 @@ public: image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST, JGL::SampleRate::X16); image_mask = new Texture("assets/sprites/alpha_mask_2.png"); j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false, - SampleRate::X0, FilteringMode::MIPMAP_TRILINEAR); + SampleRate::NONE, FilteringMode::MIPMAP_NEAREST); //Texture::MultiplyByAlphaMask(*image, *image_mask); @@ -145,7 +145,6 @@ public: float pulse = 0; float sprite_radians = 0; bool fov_increasing = true; - int blit_pos = 0; void display() { @@ -176,7 +175,7 @@ public: camera->render(); // All 3D elements of the scene and JGL elements *must* be rendered before the 2D stuff /* if rendering to screen space directly. */ - auto test_light = PointLight({2,1,2}, {pulse,pulse,pulse, 255}, {pulse, pulse, pulse, 255}, {0,0,0}, 1, 0.1, 0.01); + auto test_light = PointLight({2,1,2}, {(u8) pulse,(u8) pulse,(u8) pulse, 255}, {(u8) pulse, (u8) pulse, (u8) pulse, 255}, {0,0,0}, 1, 0.1, 0.01); // If a 3D object has transparency. The things you'd like to see through it must be drawn before. J3D::Begin(); @@ -191,7 +190,7 @@ public: J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1); J3D::End(); - J2D::Begin(nullptr, &shader, true); + J2D::Begin(j2d_render_target, &shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); @@ -231,12 +230,12 @@ public: J2D::End(); - /* - J2D::Begin(); - //J2D::DrawRenderTarget(j2d_render_target, {0, 0}); - //J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); + + J2D::Begin(nullptr, &shader, true); + J2D::DrawRenderTarget(j2d_render_target, {0, 0}); + J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); - */ + diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index b6e8447..76cde18 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -427,9 +427,14 @@ void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, const Ve glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 2); // Draw. glDrawArrays(GL_QUADS, 0, 4); + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); + // Reset Texture 1. glBindTexture(GL_TEXTURE_2D, 0); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -551,11 +556,18 @@ void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotat glBindTexture(GL_TEXTURE_2D, texture.GetHandle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); + glDrawArrays(GL_QUADS, 0, 4); glBindTexture(GL_TEXTURE_2D, 0); glColor4fv(default_state.draw_color); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); } @@ -640,7 +652,15 @@ void J2D::DrawPartialSprite(const Texture& texture, const Vector2& position, con glBindTexture(GL_TEXTURE_2D, texture.GetHandle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); + glDrawArrays(GL_QUADS, 0, 4); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); + glBindTexture(GL_TEXTURE_2D, 0); glColor4fv(default_state.draw_color); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -722,8 +742,15 @@ void J2D::DrawMirrorSprite(const Texture& texture, const Vector2& position, Dire glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); + glDrawArrays(GL_QUADS, 0, 4); + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); + //Reset the wrapping mode. if (texture.GetWrappingMode() == WrappingMode::CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE), diff --git a/src/renderer/OpenGL/TextRendering.cpp b/src/renderer/OpenGL/TextRendering.cpp index 4db8b27..51f1f5f 100644 --- a/src/renderer/OpenGL/TextRendering.cpp +++ b/src/renderer/OpenGL/TextRendering.cpp @@ -156,7 +156,17 @@ namespace JGL { } glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1), + current_state.current_shader->SetBool("TEXT", true); + glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); + + if (current_state.current_shader) + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0), + current_state.current_shader->SetBool("TEXT", false); + glBindTexture(GL_TEXTURE_2D, 0); glColor4fv(default_state.draw_color); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -238,8 +248,14 @@ namespace JGL { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); + if (current_state.current_shader) + current_state.current_shader->SetBool("TEXT", true); + glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); + if (current_state.current_shader) + current_state.current_shader->SetBool("TEXT", false); + if (!draw_back_face) glDisable(GL_CULL_FACE); From a1ca7ace7792fb8efc36757a3916bdc33b5488e3 Mon Sep 17 00:00:00 2001 From: josh Date: Thu, 17 Apr 2025 13:57:03 -0400 Subject: [PATCH 05/18] Flip order of J2D::DrawString argument, so that we can default scale to 1, and add vector2 overload. --- assets/shader_programs/test_vertex.glsl | 2 ++ include/JGL/JGL.h | 3 ++- src/renderer/OpenGL/J2D.cpp | 5 +++++ src/renderer/OpenGL/TextRendering.cpp | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index a243a19..640d743 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -1,5 +1,7 @@ #version 120 +#include "shared.glsl" + void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 2c5918c..ac60d9e 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -367,8 +367,9 @@ namespace JGL::J2D { /// @param scale The value (in both axes) to scale the text by. Defaults to {1,1}. /// @param size The point-size at which to render the font out. Re-using the same point-size allows efficient glyph caching. /// @param font The font to use for rendering. @see Font. - void DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font = Fonts::Jupiteroid); + void DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale = 1.f, const Font& font = Fonts::Jupiteroid); + void DrawString(const Color4& color, const std::string& text, const Vector2& pos, u32 size, float scale = 1.f, const Font& font = Fonts::Jupiteroid); /// Draws an Arc (section of a circle) to the screen. /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index b44d532..a963041 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -1083,3 +1083,8 @@ void J2D::DrawSprite(const TextureAtlas* texture_atlas, const AtlasRegion& atlas void J2D::DrawSprite(const TextureAtlas& texture_atlas, const AtlasRegion& atlas_region, const Vector2& position, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color) { J2D::DrawSprite(&texture_atlas, atlas_region, position, rad_rotation, origin, scale, color); } + +void J2D::DrawString(const Color4 &color, const std::string &text, const Vector2 &pos, u32 size, float scale, + const Font &font) { + DrawString(color, text, pos.x, pos.y, size, scale, font); +} diff --git a/src/renderer/OpenGL/TextRendering.cpp b/src/renderer/OpenGL/TextRendering.cpp index 4db8b27..4e9585c 100644 --- a/src/renderer/OpenGL/TextRendering.cpp +++ b/src/renderer/OpenGL/TextRendering.cpp @@ -95,7 +95,7 @@ namespace JGL { return cachedFont; } - void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) { + void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, u32 size, float scale, const Font& font) { // Offset by height to render at "correct" location. y += size; From f7eff123bea64404b4a804a49a9c936300802f6a Mon Sep 17 00:00:00 2001 From: Redacted Date: Fri, 18 Apr 2025 11:59:17 -0400 Subject: [PATCH 06/18] Fix alpha masking when using shader. --- assets/shader_programs/test_fragment.glsl | 12 ++++++++---- main.cpp | 12 ++++-------- src/renderer/OpenGL/J2D.cpp | 7 +++++-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index 3607003..a1ceb32 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -6,9 +6,6 @@ varying vec4 v_color; // The number of texture units that have been set. uniform int TEXTURE_UNIT_SET_COUNT; -// True if we should perform the alpha mask operation. -uniform bool ALPHA_MASK; - // True if we're rendering text. uniform bool TEXT; @@ -36,7 +33,6 @@ void DrawColorOnly() { gl_FragColor = v_color; } -// TODO fix positive alpha mask. void SampleTextureUnits() { if (TEXT) gl_FragColor = vec4(v_color.rgb, v_color.a * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD).a); @@ -44,6 +40,14 @@ void SampleTextureUnits() { // Draw sprite, partial sprite, mirror sprite, render target, partial render target. else if (TEXTURE_UNIT_SET_COUNT == 1) gl_FragColor = v_color * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD); + + // Draw alpha masked sprite. + else if (TEXTURE_UNIT_SET_COUNT == 2) { + vec4 color_texture = texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD); + float alpha_mask = texture2D(GL_TEXTURE1, GL_TEXTURE1_COORD).a; + gl_FragColor = vec4(v_color.rgb * color_texture.rgb, v_color.a * alpha_mask); + } + } void main() { diff --git a/main.cpp b/main.cpp index 695ce52..ad6b39e 100644 --- a/main.cpp +++ b/main.cpp @@ -112,7 +112,7 @@ JGL::Font FreeSans; Texture* image; Texture* image_mask; RenderTarget* j2d_render_target; -Shader shader; +Shader* shader; class JGLDemoWindow : public ReWindow::OpenGLWindow { @@ -137,7 +137,7 @@ public: //Texture::MultiplyByAlphaMask(*image, *image_mask); - shader = Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl")); + shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl")); } EulerAngleXYZ textAngle = {0,0,0}; @@ -190,7 +190,7 @@ public: J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1); J3D::End(); - J2D::Begin(j2d_render_target, &shader, true); + J2D::Begin(j2d_render_target, shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); @@ -231,15 +231,11 @@ public: J2D::End(); - J2D::Begin(nullptr, &shader, true); + J2D::Begin(nullptr, shader, true); J2D::DrawRenderTarget(j2d_render_target, {0, 0}); J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); - - - - } void OnRefresh(float elapsed) override { diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 76cde18..c8baeeb 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -428,12 +428,15 @@ void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, const Ve glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); if (current_state.current_shader) - current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 2); + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 2), + current_state.current_shader->SetInt("GL_TEXTURE1", 1); + // Draw. glDrawArrays(GL_QUADS, 0, 4); if (current_state.current_shader) - current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0), + current_state.current_shader->SetInt("GL_TEXTURE1", 0); // Reset Texture 1. glBindTexture(GL_TEXTURE_2D, 0); From d2497d64a20501708fbf0403d743a2edcbc5e560 Mon Sep 17 00:00:00 2001 From: Redacted Date: Sun, 20 Apr 2025 04:17:05 -0400 Subject: [PATCH 07/18] Performance Optimization Draw points can accept multiple colors. Draw points now uses vbos. Allowed the user to set the usage hint for the VBO. When changing data in a vbo, the data will be orphaned if the mode is not Fixed. --- include/JGL/JGL.h | 11 ++++++- include/JGL/types/VRamList.h | 26 +++++++++++----- src/ShapeCache.cpp | 10 ++++++ src/renderer/OpenGL/J2D.cpp | 26 ++++++++++++++-- src/types/VRamList.cpp | 59 +++++++++++++++++++++--------------- 5 files changed, 97 insertions(+), 35 deletions(-) diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 3a843c0..0314b16 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -47,6 +47,8 @@ namespace JGL::ShapeCache { // Facing straight out. inline VRamList* j2d_default_normal_data = nullptr; inline VRamList* square_origin_topleft_vertex_data = nullptr; + inline VRamList* draw_points_positions = nullptr; + inline VRamList* draw_points_colors = nullptr; void Init(); } @@ -96,7 +98,14 @@ namespace JGL::J2D { /// Plots a series of pixel-points on the screen, in a batch. /// @note This is more performant for multiple points than plotting them individually. - /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 + /// @param colors A set of 4 channel color values. @see class Color4. + /// @param points A set of x,y points to render. + /// @param radius The size of the point to plot. By default, a single pixel. + void DrawPoints(const Color4* color, const Vector2* points, int point_count, float radius = 1.f); + + /// Plots a series of pixel-points on the screen, in a batch. + /// @note This is more performant for multiple points than plotting them individually. + /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 /// @param points A set of x,y points to render. /// @param radius The size of the point to plot. By default, a single pixel. void DrawPoints(const Color4& color, const Vector2* points, int point_count, float radius = 1.f); diff --git a/include/JGL/types/VRamList.h b/include/JGL/types/VRamList.h index e26fd79..2a06916 100644 --- a/include/JGL/types/VRamList.h +++ b/include/JGL/types/VRamList.h @@ -5,18 +5,28 @@ #include #include #include +#include namespace JGL { class VRamList; + /// A hint for OpenGL on how the VBO is to be used. + enum VRamUsageHint : GLenum { + // Never updated after creation. + Fixed = GL_STATIC_DRAW, + // Modified occasionally. + Dynamic = GL_DYNAMIC_DRAW, + // Constantly modified and used one or a few times. + Stream = GL_STREAM_DRAW + }; } /// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in VRam. class JGL::VRamList { private: + VRamUsageHint usage_hint = Fixed; GLuint list_handle = 0; long num_elements = 0; bool element_array_buffer = false; - /// "Spin Locking" fix for multi-threading. bool spin_lock = false; void load(const GLfloat* data, const long& size); void load(const GLuint* data, const long& size); @@ -24,11 +34,12 @@ private: void UpdateData(void* data, const long& offset, const long& count); void Erase(); public: - VRamList(const GLuint* data, const long& count); - VRamList(const GLfloat* data, const long& count); - VRamList(const Vector2* data, const long& count); - VRamList(const Vector3* data, const long& count); - VRamList(const Vector4* data, const long& count); + VRamList(const GLuint* data, const long& count, VRamUsageHint hint = Fixed); + VRamList(const GLfloat* data, const long& count, VRamUsageHint hint = Fixed); + VRamList(const Vector2* data, const long& count, VRamUsageHint hint = Fixed); + VRamList(const Vector3* data, const long& count, VRamUsageHint hint = Fixed); + VRamList(const Vector4* data, const long& count, VRamUsageHint hint = Fixed); + VRamList(const Color4* data, const long& count, VRamUsageHint hint = Fixed); ~VRamList(); /** Copying around the VBO data to a new VBO like this is slow. @@ -51,6 +62,7 @@ public: void SetData(const Vector2* data, const long& count); void SetData(const Vector3* data, const long& count); void SetData(const Vector4* data, const long& count); + void SetData(const Color4* data, const long& count); void SetData(const GLuint* data, const long& count); void SetData(const Vector2i* data, const long& count); @@ -62,7 +74,7 @@ public: void UpdateData(const Vector2* data, const long& offset, const long& count); void UpdateData(const Vector3* data, const long& offset, const long& count); void UpdateData(const Vector4* data, const long& offset, const long& count); - + void UpdateData(const Color4* data, const long& offset, const long& count); void UpdateData(const GLuint* data, const long& offset, const long& count); void UpdateData(const Vector2i* data, const long& offset, const long& count); }; \ No newline at end of file diff --git a/src/ShapeCache.cpp b/src/ShapeCache.cpp index 50a9688..888478e 100644 --- a/src/ShapeCache.cpp +++ b/src/ShapeCache.cpp @@ -54,4 +54,14 @@ void JGL::ShapeCache::Init() { std::array normal {0, 0, 1}; j2d_default_normal_data = new VRamList(normal.data(), normal.size()); } + + if (!draw_points_colors) { + std::array color = { Colors::Transparent }; + draw_points_colors = new VRamList(color.data(), color.size(), Stream); + } + + if (!draw_points_positions) { + std::array position = { Vector2::Zero }; + draw_points_positions = new VRamList(position.data(), position.size(), Stream); + } } \ No newline at end of file diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index c8baeeb..5559a3f 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -1038,11 +1038,31 @@ void J2D::OutlineChamferRect(const Color4& color, const Vector2& pos, const Vect glColor4fv(default_state.draw_color); } -void J2D::DrawPoints(const Color4& color, const Vector2* points, int num_points, float radius) { +void J2D::DrawPoints(const Color4* colors, const Vector2* points, int point_count, float radius) { + ShapeCache::draw_points_colors->SetData(colors, point_count); + ShapeCache::draw_points_positions->SetData(points, point_count); + + glPointSize(radius); + glEnableClientState(GL_COLOR_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_colors->GetHandle()); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Color4), nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + glDrawArrays(GL_POINTS, 0, point_count); + glDisableClientState(GL_COLOR_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glColor4fv(default_state.draw_color); +} + +void J2D::DrawPoints(const Color4& color, const Vector2* points, int point_count, float radius) { + ShapeCache::draw_points_positions->SetData(points, point_count); glPointSize(radius); glColor4ubv(color.ptr()); - glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points); - glDrawArrays(GL_POINTS, 0, num_points); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + glDrawArrays(GL_POINTS, 0, point_count); + glBindBuffer(GL_ARRAY_BUFFER, 0); glColor4fv(default_state.draw_color); } diff --git a/src/types/VRamList.cpp b/src/types/VRamList.cpp index 6bfeea0..f0fe33d 100644 --- a/src/types/VRamList.cpp +++ b/src/types/VRamList.cpp @@ -2,15 +2,16 @@ #include #include +// TODO combine the two load functions. + void JGL::VRamList::load(const GLfloat* data, const long& size) { - while (spin_lock) {} spin_lock = true; GLint current_array_buffer = 0; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_array_buffer); glGenBuffers(1, &list_handle); glBindBuffer(GL_ARRAY_BUFFER, list_handle); - glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, size, data, usage_hint); glBindBuffer(GL_ARRAY_BUFFER, current_array_buffer); element_array_buffer = false; num_elements = size / sizeof(GLfloat); @@ -18,15 +19,15 @@ void JGL::VRamList::load(const GLfloat* data, const long& size) { spin_lock = false; } + void JGL::VRamList::load(const GLuint* data, const long& size) { - while (spin_lock) {} spin_lock = true; GLint current_element_array_buffer = 0; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_element_array_buffer); glGenBuffers(1, &list_handle); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, usage_hint); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_element_array_buffer); element_array_buffer = true; num_elements = size / sizeof(GLuint); @@ -77,6 +78,7 @@ size_t JGL::VRamList::GetDataSize() const { } void JGL::VRamList::SetData(void* data, const long& length) { + while (spin_lock) {} spin_lock = true; @@ -100,14 +102,11 @@ void JGL::VRamList::SetData(void* data, const long& length) { glGetIntegerv(buffer_binding, ¤t_buffer); glBindBuffer(buffer_type, list_handle); - void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY); - if (!vram) - JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?"); + size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat); - memcpy(vram, data, (element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat)) * length); - - if (!glUnmapBuffer(buffer_type)) - JGL::Logger::Fatal("We couldn't unmap the buffer?"); + if (usage_hint != Fixed) + glBufferData(GL_ARRAY_BUFFER, length * element_size, nullptr, usage_hint); + glBufferSubData(buffer_type, 0, length * element_size, data); glBindBuffer(buffer_type, current_buffer); @@ -119,7 +118,6 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt spin_lock = true; if (offset + length > num_elements) { - JGL::Logger::Warning("Using UpdateData to out-of-bounds write the VRamList? I'll resize it for you, But this is slow."); unsigned long oob_delta = (offset + length) - num_elements; if (element_array_buffer) { @@ -149,15 +147,11 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt glGetIntegerv(buffer_binding, ¤t_buffer); glBindBuffer(buffer_type, list_handle); - void* vram = glMapBuffer(buffer_type, GL_WRITE_ONLY); - if (!vram) - JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?"); - size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat); - memcpy(reinterpret_cast(reinterpret_cast(vram) + (offset * element_size)), data, length * element_size); - if (!glUnmapBuffer(buffer_type)) - JGL::Logger::Fatal("We couldn't unmap the buffer?"); + if (usage_hint != Fixed) + glBufferData(GL_ARRAY_BUFFER, length * element_size, nullptr, usage_hint); + glBufferSubData(buffer_type, offset * element_size, length * element_size, data); glBindBuffer(buffer_type, current_buffer); @@ -217,26 +211,35 @@ std::vector JGL::VRamList::GetDataUI() const { return data; } -JGL::VRamList::VRamList(const GLfloat* data, const long& length) { +JGL::VRamList::VRamList(const GLfloat* data, const long& length, VRamUsageHint hint) { + usage_hint = hint; load(data, (long) sizeof(GLfloat) * length); } -JGL::VRamList::VRamList(const GLuint* data, const long& length) { +JGL::VRamList::VRamList(const GLuint* data, const long& length, VRamUsageHint hint) { + usage_hint = hint; load(data, (long) sizeof(GLuint) * length); } -JGL::VRamList::VRamList(const Vector2* data, const long& length) { +JGL::VRamList::VRamList(const Vector2* data, const long& length, VRamUsageHint hint) { + usage_hint = hint; load(reinterpret_cast(data), (long) sizeof(Vector2) * length); } -JGL::VRamList::VRamList(const Vector3* data, const long& length) { +JGL::VRamList::VRamList(const Vector3* data, const long& length, VRamUsageHint hint) { + usage_hint = hint; load(reinterpret_cast(data), (long) sizeof(Vector3) * length); } -JGL::VRamList::VRamList(const Vector4* data, const long& length) { +JGL::VRamList::VRamList(const Vector4* data, const long& length, VRamUsageHint hint) { + usage_hint = hint; load(reinterpret_cast(data), (long) sizeof(Vector4) * length); } +JGL::VRamList::VRamList(const Color4* data, const long& count, VRamUsageHint hint) { + usage_hint = hint; + load(reinterpret_cast(data), (long) sizeof(GLuint) * count); +} void JGL::VRamList::SetData(const GLfloat* data, const long& length) { SetData((void*) data, length); } @@ -261,6 +264,10 @@ void JGL::VRamList::SetData(const Vector2i* data, const long& length) { SetData((void*) data, length * 2); } +void JGL::VRamList::SetData(const Color4* data, const long& count) { + SetData((void*) data, count); +} + void JGL::VRamList::UpdateData(const GLfloat* data, const long& offset, const long& length) { UpdateData((void*) data, offset, length); } @@ -285,6 +292,10 @@ void JGL::VRamList::UpdateData(const Vector2i* data, const long& offset, const l UpdateData((void*) data, offset, length * 2); } +void JGL::VRamList::UpdateData(const Color4* data, const long &offset, const long &count) { + UpdateData((void*) data, offset, count); +} + JGL::VRamList::~VRamList() { Erase(); } From 1a2f7627d3943e41620fe6fa2dcef8ad540d82c2 Mon Sep 17 00:00:00 2001 From: Redacted Date: Sun, 4 May 2025 23:19:49 -0400 Subject: [PATCH 08/18] custom behavior per draw function Add support for doing custom behavior per draw function for JGL. You must define uniform int J2D_RENDERING_ROUTINE in any shader to be used by JGL even if you're not doing per draw function behavior. --- assets/shader_programs/test_fragment.glsl | 109 +++++++++- assets/shader_programs/test_vertex.glsl | 99 ++++++++- src/renderer/OpenGL/J2D.cpp | 234 +++++++++++++++++++++- src/renderer/OpenGL/TextRendering.cpp | 12 +- src/renderer/OpenGL/internals/internals.h | 33 +++ 5 files changed, 468 insertions(+), 19 deletions(-) diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index a1ceb32..f76fc01 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -1,13 +1,43 @@ #version 120 -// The color manually set with glColor4f, glColor4ubv etc. -varying vec4 v_color; +#define J2D_DrawPoint 1 +#define J2D_DrawPoints 2 +#define J2D_DrawLine 3 +#define J2D_DrawLines 4 +#define J2D_DrawDottedLine 5 +#define J2D_DrawDashedLine 6 +#define J2D_DrawGradientLine 7 +#define J2D_OutlineRect 8 +#define J2D_OutlineRoundedRect 9 +#define J2D_OutlineChamferRect 10 +#define J2D_FillRect 11 +#define J2D_FillGradientRect 12 +#define J2D_FillRoundedRect 13 +#define J2D_FillChamferRect 14 +#define J2D_DrawRenderTarget 15 +#define J2D_DrawPartialRenderTarget 16 +#define J2D_DrawSprite 17 +#define J2D_DrawAlphaMaskSprite 18 +#define J2D_DrawPartialSprite 19 +#define J2D_DrawMirrorSprite 20 +#define J2D_OutlineCircle 21 +#define J2D_FillCircle 22 +#define J2D_OutlineTriangle 23 +#define J2D_FillTriangle 24 +#define J2D_FillGradientTriangle 25 +#define J2D_DrawCubicBezierCurve 26 +#define J2D_OutlinePolygon 27 +#define J2D_DrawString 28 +#define J2D_DrawArc 29 + + +uniform int JGL_RENDERING_ROUTINE; // The number of texture units that have been set. uniform int TEXTURE_UNIT_SET_COUNT; -// True if we're rendering text. -uniform bool TEXT; +// The color manually set with glColor4f, glColor4ubv etc. +varying vec4 v_color; // Texture unit 0 - 7 (8 - 31 will come later). uniform sampler2D GL_TEXTURE0; @@ -34,7 +64,7 @@ void DrawColorOnly() { } void SampleTextureUnits() { - if (TEXT) + if (JGL_RENDERING_ROUTINE == J2D_DrawString) gl_FragColor = vec4(v_color.rgb, v_color.a * texture2D(GL_TEXTURE0, GL_TEXTURE0_COORD).a); // Draw sprite, partial sprite, mirror sprite, render target, partial render target. @@ -50,10 +80,75 @@ void SampleTextureUnits() { } -void main() { +void Default() { if (TEXTURE_UNIT_SET_COUNT == 0) { DrawColorOnly(); return; } - SampleTextureUnits(); +} + +void main() { + +/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others. + if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawString) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawLine) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawLines) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillRect) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillCircle) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon) + Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawArc) + Default(); + else { Default(); } +*/ + Default(); } \ No newline at end of file diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index e1fcb80..ae5ec93 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -1,5 +1,37 @@ #version 120 +#define J2D_DrawPoint 1 +#define J2D_DrawPoints 2 +#define J2D_DrawLine 3 +#define J2D_DrawLines 4 +#define J2D_DrawDottedLine 5 +#define J2D_DrawDashedLine 6 +#define J2D_DrawGradientLine 7 +#define J2D_OutlineRect 8 +#define J2D_OutlineRoundedRect 9 +#define J2D_OutlineChamferRect 10 +#define J2D_FillRect 11 +#define J2D_FillGradientRect 12 +#define J2D_FillRoundedRect 13 +#define J2D_FillChamferRect 14 +#define J2D_DrawRenderTarget 15 +#define J2D_DrawPartialRenderTarget 16 +#define J2D_DrawSprite 17 +#define J2D_DrawAlphaMaskSprite 18 +#define J2D_DrawPartialSprite 19 +#define J2D_DrawMirrorSprite 20 +#define J2D_OutlineCircle 21 +#define J2D_FillCircle 22 +#define J2D_OutlineTriangle 23 +#define J2D_FillTriangle 24 +#define J2D_FillGradientTriangle 25 +#define J2D_DrawCubicBezierCurve 26 +#define J2D_OutlinePolygon 27 +#define J2D_DrawString 28 +#define J2D_DrawArc 29 + +uniform int JGL_RENDERING_ROUTINE; + // The color manually set with glColor4f, glColor4ubv etc etc. varying vec4 v_color; @@ -13,6 +45,9 @@ varying vec2 GL_TEXTURE5_COORD; varying vec2 GL_TEXTURE6_COORD; varying vec2 GL_TEXTURE7_COORD; +vec4 Default() { + return gl_ModelViewProjectionMatrix * gl_Vertex; +} void main() { v_color = gl_Color; @@ -25,5 +60,67 @@ void main() { GL_TEXTURE6_COORD = gl_MultiTexCoord6.xy; GL_TEXTURE7_COORD = gl_MultiTexCoord7.xy; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +/* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others. + if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialRenderTarget) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawAlphaMaskSprite) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawMirrorSprite) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawSprite) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPartialSprite) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawString) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPoint) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawPoints) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawCubicBezierCurve) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawLine) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawLines) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawDottedLine) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawDashedLine) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawGradientLine) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineRoundedRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillChamferRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillRoundedRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillGradientRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillRect) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineCircle) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillCircle) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlineTriangle) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillTriangle) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_FillGradientTriangle) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_OutlinePolygon) + gl_Position = Default(); + else if (JGL_RENDERING_ROUTINE == J2D_DrawArc) + gl_Position = Default(); + else { gl_Position = Default(); } + */ + + gl_Position = Default(); } diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 5559a3f..da6d4c1 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -90,7 +90,15 @@ void J2D::DrawPoint(const Color4& color, const Vector2& coordinates, float radiu glPointSize(radius); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), coordinates.ptr()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoint); + glDrawArrays(GL_POINTS, 0, 1); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -106,7 +114,15 @@ void J2D::DrawLine(const Color4& color, const Vector2& A, const Vector2& B, floa glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawLine); + glDrawArrays(GL_LINES, 0, 2); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -121,7 +137,15 @@ void J2D::DrawLines(const Color4& color, const Vector2* points, const size_t& po glLineWidth(thickness); glColor3ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawLines); + glDrawArrays(GL_LINE_STRIP, 0, point_count); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -138,7 +162,13 @@ void J2D::DrawDottedLine(const Color4& color, const Vector2& A, const Vector2& B for (unsigned int i = 0; i < point_count; ++i) points[i] = A + direction * (i * spacing); - return J2D::DrawPoints(color, points.data(), points.size(), thickness); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawDottedLine); + + J2D::DrawPoints(color, points.data(), points.size(), thickness); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } void J2D::DrawDottedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float thickness) { @@ -155,6 +185,10 @@ void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B Logger::Error("Drawing a dashed line that would have no dashes?"); Vector2 A_current, B_current; + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawDashedLine); + for (unsigned int i = 0; i < dash_count; i++) { A_current = A + direction * (i * length_of_dash_and_gap); B_current = A_current + (direction * dash_length); @@ -168,6 +202,9 @@ void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B B_current = A_current + direction * std::min(dash_length, distance_left); J2D::DrawLine(color, A_current, B_current, thickness); } + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } void J2D::DrawDashedLine(const Color4& color, float x1, float y1, float x2, float y2, float spacing, float dash_length, float thickness) { @@ -186,7 +223,15 @@ void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vec glLineWidth(thickness); glColorPointer(4,GL_FLOAT,sizeof(Color4), colors); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawGradientLine); + glDrawArrays(GL_LINES, 0, 2); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glDisableClientState(GL_COLOR_ARRAY); glColor4fv(default_state.draw_color); } @@ -203,7 +248,15 @@ void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& si glLineWidth(thickness); glColor4ubv(color.ptr()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRect); + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glDrawArrays(GL_LINE_LOOP, 0, 4); glColor4fv(default_state.draw_color); } @@ -222,6 +275,9 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->GetHandle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRect); + for (size_t i = 0; i < rect_count; i++) { glPushMatrix(); glColor4ubv(colors[i].ptr()); @@ -230,10 +286,15 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve glDrawArrays(GL_QUADS, 0, 4); glPopMatrix(); } + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); glColor4fv(default_state.draw_color); } +// TODO just let them set the color of each corner. void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); @@ -262,7 +323,15 @@ void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Dir glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); glColorPointer(4, GL_FLOAT, 0, colors.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillGradientRect); + glDrawArrays(GL_QUADS, 0, 4); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glDisableClientState(GL_COLOR_ARRAY); glColor4fv(default_state.draw_color); } @@ -278,8 +347,14 @@ void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2 {pos.x + radius, pos.y + size.y - radius}, {pos.x + size.x - radius, pos.y + size.y - radius} }; + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect); + J2D::BatchFillRect(colors.data(), rect_positions.data(), rect_sizes.data(), 2); J2D::BatchFillCircle(colors.data(), circle_positions.data(), circle_radii.data(), subdivisions, 4); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } void J2D::DrawSprite(const Texture* texture, float positionX, float positionY, float rad_rotation, @@ -351,8 +426,15 @@ void J2D::DrawSprite(const RenderTarget& rt, const Vector2& position, float rad_ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); auto r_position = Vector2(Math::Floor(position.x), Math::Floor(position.y)); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawRenderTarget); + J2D::DrawPartialSprite(*rt.GetTexture(), r_position, {0, 0}, Vector2(rt.GetDimensions()), rad_rotation, origin, scale, color, d); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + if (rt.OwnsTexture()) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -432,8 +514,14 @@ void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, const Ve current_state.current_shader->SetInt("GL_TEXTURE1", 1); // Draw. + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawAlphaMaskSprite); + glDrawArrays(GL_QUADS, 0, 4); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + if (current_state.current_shader) current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0), current_state.current_shader->SetInt("GL_TEXTURE1", 0); @@ -494,8 +582,14 @@ void J2D::DrawPartialRenderTarget(const RenderTarget& rt, const Vector2& positio auto r_position = Vector2(Math::Floor(position.x), Math::Floor(position.y)); auto r_sub_texture_position = Vector2(Math::Floor(sub_texture_position.x), Math::Floor(sub_texture_position.y)); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPartialRenderTarget); + J2D::DrawPartialSprite(*rt.GetTexture(), r_position, r_sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, d); + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + if (rt.OwnsTexture()) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -519,9 +613,6 @@ void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotat else textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)}; - // TODO: Kind of a mess, refactor to be more sensible later. - // Factors in scaling and origin correctly. - // i.e. to render at 2x size, from the center, at coords XY, use {2, 2} scale, and {0.5, 0.5} offset. const Vector2 offset = origin * size; Vector2 pos2 = pos; Vector2 scaled_size = scale * size; @@ -561,16 +652,19 @@ void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotat glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawSprite), current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); glDrawArrays(GL_QUADS, 0, 4); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0), + current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); + glBindTexture(GL_TEXTURE_2D, 0); glColor4fv(default_state.draw_color); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); - - if (current_state.current_shader) - current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); } @@ -657,11 +751,13 @@ void J2D::DrawPartialSprite(const Texture& texture, const Vector2& position, con glTexCoordPointer(2, GL_FLOAT, 0, textureCoordinates.data()); if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPartialSprite), current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); glDrawArrays(GL_QUADS, 0, 4); if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0), current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); glBindTexture(GL_TEXTURE_2D, 0); @@ -747,11 +843,13 @@ void J2D::DrawMirrorSprite(const Texture& texture, const Vector2& position, Dire glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data()); if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawMirrorSprite), current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1); glDrawArrays(GL_QUADS, 0, 4); if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0), current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0); //Reset the wrapping mode. @@ -794,7 +892,15 @@ void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineCircle); + glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -828,6 +934,10 @@ void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float* } glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillCircle); + for (size_t j = 0; j < circle_count; j++) { glPushMatrix(); glColor4ubv(colors[j].ptr()); @@ -836,6 +946,10 @@ void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float* glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size()); glPopMatrix(); } + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -848,7 +962,15 @@ void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thic glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineTriangle); + glDrawArrays(GL_LINE_LOOP, 0, 3); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -860,7 +982,15 @@ void J2D::FillTriangle(const Color4& color, const Triangle2D& tri) { glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillTriangle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -876,7 +1006,15 @@ void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, con glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_FLOAT, sizeof(Color4), colors); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillGradientTriangle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glDisableClientState(GL_COLOR_ARRAY); glColor4fv(default_state.draw_color); } @@ -898,7 +1036,14 @@ void J2D::DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, con } vertices[2 * subdivisions] = last; vertices[2 * subdivisions + 1] = first; + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawCubicBezierCurve); + DrawLines(color, vertices.data(), vertices.size(), thickness); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } void J2D::OutlinePolygon(const Color4& color, const Vector2* points, int points_size, float thickness) { @@ -911,7 +1056,15 @@ void J2D::OutlinePolygon(const Color4& color, const Vector2* points, int points_ glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), points); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlinePolygon); + glDrawArrays(GL_LINE_LOOP, 0, points_size); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -949,7 +1102,15 @@ void J2D::DrawArc(const Color4& color, const Vector2& center, float radius, floa glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawArc); + glDrawArrays(GL_LINE_STRIP, 0, (int) vertices.size()); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -1003,6 +1164,9 @@ void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vect unsigned int subdivisions = 9; + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRoundedRect); + J2D::DrawArc(color, anchor_tl, radius, Math::Pi, 3.01f*Math::Pi/2.f, subdivisions, thickness); J2D::DrawLine(color, anchor_topleft_top, anchor_topright_top, thickness); J2D::DrawArc(color, anchor_tr, radius, 3.f*Math::Pi/2.f, 2.02*Math::Pi, subdivisions, thickness); @@ -1011,13 +1175,22 @@ void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vect J2D::DrawLine(color, anchor_bottomright_bottom, anchor_bottomleft_bottom, thickness); J2D::DrawArc(color, anchor_bl, radius, Math::Pi/2, Math::Pi*1.01f, subdivisions, thickness); J2D::DrawLine(color, anchor_bottomleft_left, anchor_topleft_left, thickness); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); //J2D::End(); } void J2D::FillChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius) { + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillChamferRect); + FillRoundedRect(color, pos, size, radius, 4); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } void J2D::OutlineChamferRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, float thickness) { @@ -1034,7 +1207,15 @@ void J2D::OutlineChamferRect(const Color4& color, const Vector2& pos, const Vect glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineChamferRect); + glDrawArrays(GL_LINE_LOOP, 0, 8); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glColor4fv(default_state.draw_color); } @@ -1049,7 +1230,15 @@ void J2D::DrawPoints(const Color4* colors, const Vector2* points, int point_coun glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoints); + glDrawArrays(GL_POINTS, 0, point_count); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glDisableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); glColor4fv(default_state.draw_color); @@ -1061,7 +1250,15 @@ void J2D::DrawPoints(const Color4& color, const Vector2* points, int point_count glColor4ubv(color.ptr()); glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawPoints); + glDrawArrays(GL_POINTS, 0, point_count); + + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); glColor4fv(default_state.draw_color); } @@ -1098,7 +1295,19 @@ void J2D::OutlineEllipse(const Color4& color, const Vector2& position, float rad glLineWidth(thickness); glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + + /* + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineElipse); + */ + glDrawArrays(GL_LINE_LOOP, 0, (int) vertices.size()); + + /* + if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + */ + glColor4fv(default_state.draw_color); } @@ -1126,7 +1335,18 @@ void J2D::FillEllipse(const Color4& color, const Vector2& position, float radius glColor4ubv(color.ptr()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + + /* +if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineElipse); + */ + glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size()); + + /* +if (current_state.current_shader) + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + */ glColor4fv(default_state.draw_color); } diff --git a/src/renderer/OpenGL/TextRendering.cpp b/src/renderer/OpenGL/TextRendering.cpp index 51f1f5f..6e4556c 100644 --- a/src/renderer/OpenGL/TextRendering.cpp +++ b/src/renderer/OpenGL/TextRendering.cpp @@ -159,13 +159,13 @@ namespace JGL { if (current_state.current_shader) current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 1), - current_state.current_shader->SetBool("TEXT", true); + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString); glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); if (current_state.current_shader) current_state.current_shader->SetInt("TEXTURE_UNIT_SET_COUNT", 0), - current_state.current_shader->SetBool("TEXT", false); + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); glBindTexture(GL_TEXTURE_2D, 0); glColor4fv(default_state.draw_color); @@ -248,13 +248,17 @@ namespace JGL { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); + /* if (current_state.current_shader) - current_state.current_shader->SetBool("TEXT", true); + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_DrawString); + */ glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); + /* if (current_state.current_shader) - current_state.current_shader->SetBool("TEXT", false); + current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); + */ if (!draw_back_face) glDisable(GL_CULL_FACE); diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 139ddf7..1da06c4 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -12,8 +12,41 @@ namespace JGL { class State; class StateStack; + enum RENDERING_ROUTINE_ID : int32_t; } +enum JGL::RENDERING_ROUTINE_ID : int32_t { + J2D_DrawPoint = 1, + J2D_DrawPoints = 2, + J2D_DrawLine = 3, + J2D_DrawLines = 4, + J2D_DrawDottedLine = 5, + J2D_DrawDashedLine = 6, + J2D_DrawGradientLine = 7, + J2D_OutlineRect = 8, + J2D_OutlineRoundedRect = 9, + J2D_OutlineChamferRect = 10, + J2D_FillRect = 11, + J2D_FillGradientRect = 12, + J2D_FillRoundedRect = 13, + J2D_FillChamferRect = 14, + J2D_DrawRenderTarget = 15, + J2D_DrawPartialRenderTarget = 16, + J2D_DrawSprite = 17, + J2D_DrawAlphaMaskSprite = 18, + J2D_DrawPartialSprite = 19, + J2D_DrawMirrorSprite = 20, + J2D_OutlineCircle = 21, + J2D_FillCircle = 22, + J2D_OutlineTriangle = 23, + J2D_FillTriangle = 24, + J2D_FillGradientTriangle = 25, + J2D_DrawCubicBezierCurve = 26, + J2D_OutlinePolygon = 27, + J2D_DrawString = 28, + J2D_DrawArc = 29, +}; + class JGL::State { public: From 347b9cb278d0a8ed1cb5f83ccd8c83d1c97ba129 Mon Sep 17 00:00:00 2001 From: Redacted Date: Mon, 5 May 2025 11:25:49 -0400 Subject: [PATCH 09/18] Performance optimization --- assets/shader_programs/test_fragment.glsl | 1 - include/JGL/types/Shader.h | 7 ++++--- src/renderer/OpenGL/J3D.cpp | 1 + src/types/Shader.cpp | 10 +++++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index f76fc01..ce872d0 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -88,7 +88,6 @@ void Default() { } void main() { - /* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others. if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget) Default(); diff --git a/include/JGL/types/Shader.h b/include/JGL/types/Shader.h index 4aefed0..bbd47d9 100644 --- a/include/JGL/types/Shader.h +++ b/include/JGL/types/Shader.h @@ -41,9 +41,9 @@ public: Shader(const std::string& vertex_code, const std::string& fragment_code); /// @return True if the shader program successfully loaded and compiled. - bool Loaded() const; + [[nodiscard]] bool Loaded() const; /// @return The integer handle that OpenGL links to this shader program. - unsigned int Handle() const; + [[nodiscard]] unsigned int Handle() const; /// Enable this shader. All rendering performed thereafter will be affected by this shader code. /// @see UseDefault. @@ -58,7 +58,7 @@ public: /// @return The Attribute variable linked to the specified name. /// Attributes differ from Uniforms in that their value is different for each instance of the shader-program as it is running. - GLint Attribute(const std::string& name) const; + [[nodiscard]] GLint Attribute(const std::string& name) const; /// Sets a `uniform bool name = value` in the shader program. void SetBool (const std::string& name, bool value) const; @@ -116,5 +116,6 @@ private: std::string vertexSource; std::string fragmentSource; std::string fragmentPath; + mutable std::unordered_map uniform_location_cache; static void checkCompileErrors(GLuint shader, const std::string& type); }; \ No newline at end of file diff --git a/src/renderer/OpenGL/J3D.cpp b/src/renderer/OpenGL/J3D.cpp index 59bc759..7432ef0 100644 --- a/src/renderer/OpenGL/J3D.cpp +++ b/src/renderer/OpenGL/J3D.cpp @@ -236,6 +236,7 @@ void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spher glLineWidth(thickness); glColor4ubv(color.ptr()); + // TODO allocate once. VRamList vertex_data(vertices.data(), vertices.size()); glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle()); glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr); diff --git a/src/types/Shader.cpp b/src/types/Shader.cpp index dc0272c..2e94123 100644 --- a/src/types/Shader.cpp +++ b/src/types/Shader.cpp @@ -144,7 +144,13 @@ namespace JGL { } GLint Shader::Uniform(const std::string &name) const { - return glGetUniformLocation(id, name.c_str()); + auto it = uniform_location_cache.find(name); + if (it == uniform_location_cache.end()) { + auto location = glGetUniformLocation(id, name.c_str()); + uniform_location_cache[name] = location; + return location; + } + return it->second; } GLint Shader::Attribute(const std::string &name) const { @@ -155,8 +161,6 @@ namespace JGL { vertexSource = vertex_code; fragmentSource = fragment_code; - - const char* vShaderCode = vertex_code.c_str(); const char* fShaderCode = fragment_code.c_str(); From ede11131fe6924a573cea64a7240b3572ec61789 Mon Sep 17 00:00:00 2001 From: Redacted Date: Tue, 6 May 2025 11:55:01 -0400 Subject: [PATCH 10/18] Change gradient rect to accept 4x color4 --- include/JGL/JGL.h | 9 +++++---- main.cpp | 2 +- src/renderer/OpenGL/J2D.cpp | 27 +++++++-------------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 0314b16..854525b 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -186,12 +186,13 @@ namespace JGL::J2D { void FillRect(const Color4& color, const Vector2& pos, const Vector2& size); /// Draws a filled rectangle where the color transitions across it. - /// @param color1 A 3-or-4 channel color value. @see class Color3, class Color4 - /// @param color2 A 3-or-4 channel color value. @see class Color3, class Color4 - /// @param gradient See enum Direction + /// @param top_left_color A 3-or-4 channel color value. @see class Color3, class Color4 + /// @param bottom_left_color A 3-or-4 channel color value. @see class Color3, class Color4 + /// @param bottom_right_color A 3-or-4 channel color value. @see class Color3, class Color4 + /// @param top_right_color A 3-or-4 channel color value. @see class Color3, class Color4 /// @param pos The top-left corner of the rectangle. /// @param size The width and height of the rectangle. - void FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size); + void FillGradientRect(const Color4& top_left_color, const Color4& bottom_left_color, const Color4& bottom_right_color, const Color4& top_right_color, const Vector2& pos, const Vector2& size); /// Draws a filled rectangle with rounded corners on the screen. /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 diff --git a/main.cpp b/main.cpp index ad6b39e..f795798 100644 --- a/main.cpp +++ b/main.cpp @@ -196,7 +196,7 @@ public: J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White); J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32}); - J2D::FillGradientRect(Colors::Red, Colors::Blue, Direction::Diagonal_SWNE, {100,52}, {100,100}); + J2D::FillGradientRect(Colors::Red, Colors::Green, Colors::Blue, Colors::White, {100,52}, {100,100}); J2D::FillRoundedRect(Colors::Red, {200, 52}, {100, 100}, 8, 8); J2D::FillRoundedRect(Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4); J2D::FillCircle(Colors::White, {52, 204}, 50, 24); diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index da6d4c1..7909733 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -294,31 +294,18 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve glColor4fv(default_state.draw_color); } -// TODO just let them set the color of each corner. -void J2D::FillGradientRect(const Color4& color1, const Color4& color2, const Direction& gradient, const Vector2& pos, const Vector2& size) { +void J2D::FillGradientRect(const Color4& top_left_color, const Color4& bottom_left_color, const Color4& bottom_right_color, const Color4& top_right_color, const Vector2& pos, const Vector2& size) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}}; - std::vector colors{}; - if (gradient == Direction::Horizontal) - colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), - color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()}; - - else if (gradient == Direction::Vertical) - colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), - color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized()}; - - else if (gradient == Direction::Diagonal_SWNE) - colors = {(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, - color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(), (color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, - (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized()}; - - else if (gradient == Direction::Diagonal_NWSE) - colors = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),(color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, - (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f, (color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f, color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized(), - (color1.RedChannelNormalized() + color2.RedChannelNormalized()) / 2.f, (color1.GreenChannelNormalized() + color2.GreenChannelNormalized()) / 2.f, (color1.BlueChannelNormalized() + color2.BlueChannelNormalized()) / 2.f,(color1.AlphaChannelNormalized() + color2.AlphaChannelNormalized()) / 2.f}; + std::vector colors { + top_left_color.RN(), top_left_color.GN(), top_left_color.BN(), top_left_color.AN(), + bottom_left_color.RN(), bottom_left_color.GN(), bottom_left_color.BN(), bottom_left_color.AN(), + bottom_right_color.RN(), bottom_right_color.GN(), bottom_right_color.BN(), bottom_right_color.AN(), + top_right_color.RN(), top_right_color.GN(), top_right_color.BN(), top_right_color.AN() + }; glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); From 7875b777e540576287186104127def4c21100c09 Mon Sep 17 00:00:00 2001 From: dawsh Date: Fri, 9 May 2025 13:50:18 -0500 Subject: [PATCH 11/18] Demonstrate GLSL custom preprocessor. --- assets/shader_programs/jgl.glsl | 31 +++++++++++++++++++++ assets/shader_programs/test_fragment.glsl | 33 +---------------------- assets/shader_programs/test_vertex.glsl | 32 +--------------------- 3 files changed, 33 insertions(+), 63 deletions(-) create mode 100644 assets/shader_programs/jgl.glsl diff --git a/assets/shader_programs/jgl.glsl b/assets/shader_programs/jgl.glsl new file mode 100644 index 0000000..2cc8e24 --- /dev/null +++ b/assets/shader_programs/jgl.glsl @@ -0,0 +1,31 @@ +#define J2D_DrawPoint 1 +#define J2D_DrawPoints 2 +#define J2D_DrawLine 3 +#define J2D_DrawLines 4 +#define J2D_DrawDottedLine 5 +#define J2D_DrawDashedLine 6 +#define J2D_DrawGradientLine 7 +#define J2D_OutlineRect 8 +#define J2D_OutlineRoundedRect 9 +#define J2D_OutlineChamferRect 10 +#define J2D_FillRect 11 +#define J2D_FillGradientRect 12 +#define J2D_FillRoundedRect 13 +#define J2D_FillChamferRect 14 +#define J2D_DrawRenderTarget 15 +#define J2D_DrawPartialRenderTarget 16 +#define J2D_DrawSprite 17 +#define J2D_DrawAlphaMaskSprite 18 +#define J2D_DrawPartialSprite 19 +#define J2D_DrawMirrorSprite 20 +#define J2D_OutlineCircle 21 +#define J2D_FillCircle 22 +#define J2D_OutlineTriangle 23 +#define J2D_FillTriangle 24 +#define J2D_FillGradientTriangle 25 +#define J2D_DrawCubicBezierCurve 26 +#define J2D_OutlinePolygon 27 +#define J2D_DrawString 28 +#define J2D_DrawArc 29 + +uniform int JGL_RENDERING_ROUTINE; \ No newline at end of file diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index ce872d0..013d69f 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -1,37 +1,6 @@ #version 120 -#define J2D_DrawPoint 1 -#define J2D_DrawPoints 2 -#define J2D_DrawLine 3 -#define J2D_DrawLines 4 -#define J2D_DrawDottedLine 5 -#define J2D_DrawDashedLine 6 -#define J2D_DrawGradientLine 7 -#define J2D_OutlineRect 8 -#define J2D_OutlineRoundedRect 9 -#define J2D_OutlineChamferRect 10 -#define J2D_FillRect 11 -#define J2D_FillGradientRect 12 -#define J2D_FillRoundedRect 13 -#define J2D_FillChamferRect 14 -#define J2D_DrawRenderTarget 15 -#define J2D_DrawPartialRenderTarget 16 -#define J2D_DrawSprite 17 -#define J2D_DrawAlphaMaskSprite 18 -#define J2D_DrawPartialSprite 19 -#define J2D_DrawMirrorSprite 20 -#define J2D_OutlineCircle 21 -#define J2D_FillCircle 22 -#define J2D_OutlineTriangle 23 -#define J2D_FillTriangle 24 -#define J2D_FillGradientTriangle 25 -#define J2D_DrawCubicBezierCurve 26 -#define J2D_OutlinePolygon 27 -#define J2D_DrawString 28 -#define J2D_DrawArc 29 - - -uniform int JGL_RENDERING_ROUTINE; +#include "jgl.glsl" // The number of texture units that have been set. uniform int TEXTURE_UNIT_SET_COUNT; diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index ae5ec93..1dd1acd 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -1,36 +1,6 @@ #version 120 -#define J2D_DrawPoint 1 -#define J2D_DrawPoints 2 -#define J2D_DrawLine 3 -#define J2D_DrawLines 4 -#define J2D_DrawDottedLine 5 -#define J2D_DrawDashedLine 6 -#define J2D_DrawGradientLine 7 -#define J2D_OutlineRect 8 -#define J2D_OutlineRoundedRect 9 -#define J2D_OutlineChamferRect 10 -#define J2D_FillRect 11 -#define J2D_FillGradientRect 12 -#define J2D_FillRoundedRect 13 -#define J2D_FillChamferRect 14 -#define J2D_DrawRenderTarget 15 -#define J2D_DrawPartialRenderTarget 16 -#define J2D_DrawSprite 17 -#define J2D_DrawAlphaMaskSprite 18 -#define J2D_DrawPartialSprite 19 -#define J2D_DrawMirrorSprite 20 -#define J2D_OutlineCircle 21 -#define J2D_FillCircle 22 -#define J2D_OutlineTriangle 23 -#define J2D_FillTriangle 24 -#define J2D_FillGradientTriangle 25 -#define J2D_DrawCubicBezierCurve 26 -#define J2D_OutlinePolygon 27 -#define J2D_DrawString 28 -#define J2D_DrawArc 29 - -uniform int JGL_RENDERING_ROUTINE; +#include "jgl.glsl" // The color manually set with glColor4f, glColor4ubv etc etc. varying vec4 v_color; From 6d37cd93e3389f059b9997897e304f009230ff5d Mon Sep 17 00:00:00 2001 From: Redacted Date: Fri, 9 May 2025 16:41:29 -0400 Subject: [PATCH 12/18] BatchFillRect instanced rendering. during shader creation you can now also specify your attributes. --- CMakeLists.txt | 2 +- assets/shader_programs/jgl.glsl | 31 ----------- assets/shader_programs/test_fragment.glsl | 34 +++++++++++- assets/shader_programs/test_vertex.glsl | 61 +++++++++++++++++++-- include/JGL/types/Shader.h | 4 +- main.cpp | 16 ++++-- src/JGL.cpp | 4 ++ src/renderer/OpenGL/J2D.cpp | 65 +++++++++++++++++++---- src/renderer/OpenGL/internals/internals.h | 11 ++++ src/types/Shader.cpp | 20 ++++--- 10 files changed, 188 insertions(+), 60 deletions(-) delete mode 100644 assets/shader_programs/jgl.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index 07a11c2..5686f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ CPMAddPackage( CPMAddPackage( NAME GLAD - URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow_anisotropic.zip + URL https://git.redacted.cc/Redacted/glad/archive/v2.1ext_fbo_depthtexture_shadow_anisotropic_instanced.zip ) CPMAddPackage( diff --git a/assets/shader_programs/jgl.glsl b/assets/shader_programs/jgl.glsl deleted file mode 100644 index 2cc8e24..0000000 --- a/assets/shader_programs/jgl.glsl +++ /dev/null @@ -1,31 +0,0 @@ -#define J2D_DrawPoint 1 -#define J2D_DrawPoints 2 -#define J2D_DrawLine 3 -#define J2D_DrawLines 4 -#define J2D_DrawDottedLine 5 -#define J2D_DrawDashedLine 6 -#define J2D_DrawGradientLine 7 -#define J2D_OutlineRect 8 -#define J2D_OutlineRoundedRect 9 -#define J2D_OutlineChamferRect 10 -#define J2D_FillRect 11 -#define J2D_FillGradientRect 12 -#define J2D_FillRoundedRect 13 -#define J2D_FillChamferRect 14 -#define J2D_DrawRenderTarget 15 -#define J2D_DrawPartialRenderTarget 16 -#define J2D_DrawSprite 17 -#define J2D_DrawAlphaMaskSprite 18 -#define J2D_DrawPartialSprite 19 -#define J2D_DrawMirrorSprite 20 -#define J2D_OutlineCircle 21 -#define J2D_FillCircle 22 -#define J2D_OutlineTriangle 23 -#define J2D_FillTriangle 24 -#define J2D_FillGradientTriangle 25 -#define J2D_DrawCubicBezierCurve 26 -#define J2D_OutlinePolygon 27 -#define J2D_DrawString 28 -#define J2D_DrawArc 29 - -uniform int JGL_RENDERING_ROUTINE; \ No newline at end of file diff --git a/assets/shader_programs/test_fragment.glsl b/assets/shader_programs/test_fragment.glsl index 013d69f..91c68f9 100644 --- a/assets/shader_programs/test_fragment.glsl +++ b/assets/shader_programs/test_fragment.glsl @@ -1,6 +1,38 @@ #version 120 -#include "jgl.glsl" +#define J2D_DrawPoint 1 +#define J2D_DrawPoints 2 +#define J2D_DrawLine 3 +#define J2D_DrawLines 4 +#define J2D_DrawDottedLine 5 +#define J2D_DrawDashedLine 6 +#define J2D_DrawGradientLine 7 +#define J2D_OutlineRect 8 +#define J2D_OutlineRoundedRect 9 +#define J2D_OutlineChamferRect 10 +#define J2D_FillRect 11 +#define J2D_FillGradientRect 12 +#define J2D_FillRoundedRect 13 +#define J2D_FillChamferRect 14 +#define J2D_DrawRenderTarget 15 +#define J2D_DrawPartialRenderTarget 16 +#define J2D_DrawSprite 17 +#define J2D_DrawAlphaMaskSprite 18 +#define J2D_DrawPartialSprite 19 +#define J2D_DrawMirrorSprite 20 +#define J2D_OutlineCircle 21 +#define J2D_FillCircle 22 +#define J2D_OutlineTriangle 23 +#define J2D_FillTriangle 24 +#define J2D_FillGradientTriangle 25 +#define J2D_DrawCubicBezierCurve 26 +#define J2D_OutlinePolygon 27 +#define J2D_DrawString 28 +#define J2D_DrawArc 29 + + +uniform int JGL_RENDERING_ROUTINE; +uniform bool JGL_INSTANCED_RENDERING; // The number of texture units that have been set. uniform int TEXTURE_UNIT_SET_COUNT; diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index 1dd1acd..535fcb0 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -1,10 +1,55 @@ #version 120 +// TODO this will fail if we don't have the extension. +#extension GL_ARB_instanced_arrays : enable -#include "jgl.glsl" +#define J2D_DrawPoint 1 +#define J2D_DrawPoints 2 +#define J2D_DrawLine 3 +#define J2D_DrawLines 4 +#define J2D_DrawDottedLine 5 +#define J2D_DrawDashedLine 6 +#define J2D_DrawGradientLine 7 +#define J2D_OutlineRect 8 +#define J2D_OutlineRoundedRect 9 +#define J2D_OutlineChamferRect 10 +#define J2D_FillRect 11 +#define J2D_FillGradientRect 12 +#define J2D_FillRoundedRect 13 +#define J2D_FillChamferRect 14 +#define J2D_DrawRenderTarget 15 +#define J2D_DrawPartialRenderTarget 16 +#define J2D_DrawSprite 17 +#define J2D_DrawAlphaMaskSprite 18 +#define J2D_DrawPartialSprite 19 +#define J2D_DrawMirrorSprite 20 +#define J2D_OutlineCircle 21 +#define J2D_FillCircle 22 +#define J2D_OutlineTriangle 23 +#define J2D_FillTriangle 24 +#define J2D_FillGradientTriangle 25 +#define J2D_DrawCubicBezierCurve 26 +#define J2D_OutlinePolygon 27 +#define J2D_DrawString 28 +#define J2D_DrawArc 29 + +uniform int JGL_RENDERING_ROUTINE; +uniform bool JGL_INSTANCED_RENDERING; // The color manually set with glColor4f, glColor4ubv etc etc. varying vec4 v_color; +// Local space vertices for instanced rendering. +attribute vec2 a_vertex_position; // 0 + +// The position at which to render the instance. +attribute vec2 a_instance_position; // 1 + +// The scale of the instance. +attribute vec2 a_instance_size; // 2 + +// The color of the instance. +attribute vec4 a_instance_color; // 3 + // The texture coordinates in each texture unit. varying vec2 GL_TEXTURE0_COORD; varying vec2 GL_TEXTURE1_COORD; @@ -16,11 +61,11 @@ varying vec2 GL_TEXTURE6_COORD; varying vec2 GL_TEXTURE7_COORD; vec4 Default() { + v_color = gl_Color; return gl_ModelViewProjectionMatrix * gl_Vertex; } void main() { - v_color = gl_Color; GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy; GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy; GL_TEXTURE2_COORD = gl_MultiTexCoord2.xy; @@ -30,6 +75,8 @@ void main() { GL_TEXTURE6_COORD = gl_MultiTexCoord6.xy; GL_TEXTURE7_COORD = gl_MultiTexCoord7.xy; + + /* If you want behavior per JGL draw function, Or for specific ones. The order here matters because some JGL functions call others. if (JGL_RENDERING_ROUTINE == J2D_DrawRenderTarget) gl_Position = Default(); @@ -92,5 +139,13 @@ void main() { else { gl_Position = Default(); } */ - gl_Position = Default(); + if (JGL_RENDERING_ROUTINE == J2D_FillRect) { + if (!JGL_INSTANCED_RENDERING) { gl_Position = Default(); return; } + + vec2 scaled = a_vertex_position * a_instance_size; + vec2 world_pos = scaled + a_instance_position; + gl_Position = gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); + v_color = a_instance_color; + } + else { gl_Position = Default(); } } diff --git a/include/JGL/types/Shader.h b/include/JGL/types/Shader.h index bbd47d9..a41685b 100644 --- a/include/JGL/types/Shader.h +++ b/include/JGL/types/Shader.h @@ -35,10 +35,10 @@ public: Shader() = default; /// Creates a shader by compiling a vertex and fragment program from the sources in the respective filesystem paths. - Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path); + Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector>& attribute_bindings = {}); /// Creates a shader by compiling a vertex and fragment program from the respective GLSL source-strings. - Shader(const std::string& vertex_code, const std::string& fragment_code); + Shader(const std::string& vertex_code, const std::string& fragment_code, const std::vector>& attribute_bindings = {}); /// @return True if the shader program successfully loaded and compiled. [[nodiscard]] bool Loaded() const; diff --git a/main.cpp b/main.cpp index f795798..c335605 100644 --- a/main.cpp +++ b/main.cpp @@ -15,7 +15,9 @@ using namespace JGL; using JGL::Font; float fps = 0.0f; - +std::vector rect_pos; +std::vector rect_size; +std::vector rect_colors; /// A draggable 2D point that highlights when moused over and when clicked. class Gizmo { @@ -137,7 +139,14 @@ public: //Texture::MultiplyByAlphaMask(*image, *image_mask); - shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl")); + shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"), + {{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}}); + + for (unsigned int i = 0; i < 100; i++) { + rect_pos.emplace_back(420, 420); + rect_size.emplace_back(20, 20); + rect_colors.emplace_back(Colors::Red); + } } EulerAngleXYZ textAngle = {0,0,0}; @@ -178,6 +187,7 @@ public: auto test_light = PointLight({2,1,2}, {(u8) pulse,(u8) pulse,(u8) pulse, 255}, {(u8) pulse, (u8) pulse, (u8) pulse, 255}, {0,0,0}, 1, 0.1, 0.01); // If a 3D object has transparency. The things you'd like to see through it must be drawn before. + J3D::Begin(); J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-1,-0.125,1}); J3D::DrawLine(Colors::Red, {-0.33,-0.125,1}, {-0.33,0.25,1}); @@ -192,6 +202,7 @@ public: J2D::Begin(j2d_render_target, shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); + J2D::BatchFillRect(rect_colors.data(), rect_pos.data(), rect_size.data(), rect_pos.size()); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White); @@ -235,7 +246,6 @@ public: J2D::DrawRenderTarget(j2d_render_target, {0, 0}); J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); - } void OnRefresh(float elapsed) override { diff --git a/src/JGL.cpp b/src/JGL.cpp index 6f2939e..bd528fb 100644 --- a/src/JGL.cpp +++ b/src/JGL.cpp @@ -43,6 +43,10 @@ namespace JGL { return false; if (!GLAD_GL_ARB_shadow) return false; + if (!GLAD_GL_ARB_draw_instanced) + supports_instanced = false; + if (!GLAD_GL_ARB_instanced_arrays) + supports_instanced = false; return true; } } diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 7909733..4ec0660 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -272,25 +272,68 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve if (rect_count <= 0) return; - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->GetHandle()); - glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); - if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRect); - for (size_t i = 0; i < rect_count; i++) { - glPushMatrix(); - glColor4ubv(colors[i].ptr()); - glTranslatef(positions[i].x, positions[i].y + sizes[i].y, 0); - glScalef(sizes[i].x, sizes[i].y, 1); - glDrawArrays(GL_QUADS, 0, 4); - glPopMatrix(); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->GetHandle()); + + if (rect_count == 1 || !supports_instanced || !current_state.current_shader) { + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + + for (size_t i = 0; i < rect_count; i++) { + glPushMatrix(); + glColor4ubv(colors[i].ptr()); + glTranslatef(positions[i].x, positions[i].y + sizes[i].y, 0); + glScalef(sizes[i].x, sizes[i].y, 1); + glDrawArrays(GL_QUADS, 0, 4); + glPopMatrix(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + else { + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true); + + std::vector instances; + instances.reserve(rect_count); + + for (size_t i = 0; i < rect_count; ++i) + instances.emplace_back(Vector2(positions[i].x, positions[i].y + sizes[i].y), sizes[i], colors[i]); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr); + glVertexAttribDivisorARB(0, 0); + + // count * 5 because 4x float & 4x uint8_t = sizeof(float). + VRamList instance_buffer((GLfloat*) instances.data(), instances.size() * 5, VRamUsageHint::Stream); + + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.GetHandle()); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance), (GLvoid*) offsetof(Instance, position)); + glVertexAttribDivisorARB(1, 1); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance), (GLvoid*) offsetof(Instance, size)); + glVertexAttribDivisorARB(2, 1); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance), (GLvoid*) offsetof(Instance, color)); + glVertexAttribDivisorARB(3, 1); + + glDrawArraysInstancedARB(GL_QUADS, 0, 4, rect_count); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false); } if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); glColor4fv(default_state.draw_color); } diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 1da06c4..6fb6647 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -96,6 +96,17 @@ namespace JGL { std::vector TriangleMeshVertexNormals(const Vector3* vertices, const size_t& vertex_count, const unsigned int* indices, const size_t& index_count); inline StateStack state_stack; inline Vector2i window_size; + inline bool supports_instanced = true; + + struct Instance { + public: + Vector2 position; + Vector2 size; + Color4 color; + public: + Instance(const Vector2& position, const Vector2& size, const Color4& color) : position(position), size(size), color(color) {} + ~Instance() = default; + }; } namespace JGL::J2D { diff --git a/src/types/Shader.cpp b/src/types/Shader.cpp index 2e94123..b059436 100644 --- a/src/types/Shader.cpp +++ b/src/types/Shader.cpp @@ -115,8 +115,8 @@ namespace JGL { } }; - Shader::Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path) : - Shader(ReadFile(vertex_source_path), ReadFile(fragment_source_path)) { + Shader::Shader(const std::filesystem::path& vertex_source_path, const std::filesystem::path& fragment_source_path, const std::vector>& attribute_bindings) : + Shader(ReadFile(vertex_source_path), ReadFile(fragment_source_path), attribute_bindings) { vertexPath = vertex_source_path; fragmentPath = fragment_source_path; } @@ -128,13 +128,13 @@ namespace JGL { if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { - glGetShaderInfoLog(shader, 1024, NULL, infoLog); + glGetShaderInfoLog(shader, 1024, nullptr, infoLog); OnCompilationErrorMessage.Invoke( std::format("COMPILATION_ERROR: {}", type), infoLog); } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { - glGetProgramInfoLog(shader, 1024, NULL, infoLog); + glGetProgramInfoLog(shader, 1024, nullptr, infoLog); OnCompilationErrorMessage.Invoke(std::format("COMPILATION_ERROR: {}", type), infoLog); } } @@ -157,7 +157,7 @@ namespace JGL { return glGetAttribLocation(id, name.c_str()); } - Shader::Shader(const std::string &vertex_code, const std::string &fragment_code) { + Shader::Shader(const std::string &vertex_code, const std::string &fragment_code, const std::vector>& attribute_bindings) { vertexSource = vertex_code; fragmentSource = fragment_code; @@ -169,21 +169,25 @@ namespace JGL { // vertex shader. vertex = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex, 1, &vShaderCode, NULL); + glShaderSource(vertex, 1, &vShaderCode, nullptr); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); // fragment shader fragment = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment, 1, &fShaderCode, NULL); + glShaderSource(fragment, 1, &fShaderCode, nullptr); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); // shader Program id = glCreateProgram(); + + // bind attribute locations. + for (auto& a: attribute_bindings) + glBindAttribLocation(id, a.second, a.first.c_str()); + glAttachShader(id, vertex); glAttachShader(id, fragment); - glLinkProgram(id); checkCompileErrors(id, "PROGRAM"); From 7ea4b8696e2e040800d5399fc5ba71dc8d9663b5 Mon Sep 17 00:00:00 2001 From: Redacted Date: Sun, 11 May 2025 13:49:07 -0400 Subject: [PATCH 13/18] cleanup & performance optimization. --- assets/shader_programs/test_vertex.glsl | 29 +++++--- include/JGL/types/VRamList.h | 26 +++++-- main.cpp | 2 +- src/renderer/OpenGL/J2D.cpp | 22 +++--- src/renderer/OpenGL/J3D.cpp | 28 +++---- src/renderer/OpenGL/internals/internals.h | 1 + src/types/VRamList.cpp | 89 ++++++++++++----------- 7 files changed, 110 insertions(+), 87 deletions(-) diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index 535fcb0..bbc946f 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -65,6 +65,22 @@ vec4 Default() { return gl_ModelViewProjectionMatrix * gl_Vertex; } +vec4 DefaultInstanced() { + v_color = a_instance_color; + vec2 scaled = a_vertex_position * a_instance_size; + vec2 world_pos = scaled + a_instance_position; + return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); +} + +vec4 FillRectInstanced() { + vec2 instance_pos = a_instance_position + vec2(0.0, a_instance_size.y); + vec2 scaled = a_vertex_position * a_instance_size; + vec2 world_pos = scaled + instance_pos; + + v_color = a_instance_color; + return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); +} + void main() { GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy; GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy; @@ -139,13 +155,8 @@ void main() { else { gl_Position = Default(); } */ - if (JGL_RENDERING_ROUTINE == J2D_FillRect) { - if (!JGL_INSTANCED_RENDERING) { gl_Position = Default(); return; } - - vec2 scaled = a_vertex_position * a_instance_size; - vec2 world_pos = scaled + a_instance_position; - gl_Position = gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); - v_color = a_instance_color; - } - else { gl_Position = Default(); } + if (JGL_RENDERING_ROUTINE == J2D_FillRect && JGL_INSTANCED_RENDERING) + gl_Position = FillRectInstanced(); + else + gl_Position = Default(); } diff --git a/include/JGL/types/VRamList.h b/include/JGL/types/VRamList.h index 2a06916..5318d1f 100644 --- a/include/JGL/types/VRamList.h +++ b/include/JGL/types/VRamList.h @@ -20,14 +20,14 @@ namespace JGL { }; } -/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in VRam. +/// A wrapped for "Vertex Buffer Object" In OpenGL, Store things in V-ram. class JGL::VRamList { private: VRamUsageHint usage_hint = Fixed; GLuint list_handle = 0; - long num_elements = 0; + long byte_count = 0; bool element_array_buffer = false; - bool spin_lock = false; + // TODO mutex lock. void load(const GLfloat* data, const long& size); void load(const GLuint* data, const long& size); void SetData(void* data, const long& count); @@ -40,23 +40,27 @@ public: VRamList(const Vector3* data, const long& count, VRamUsageHint hint = Fixed); VRamList(const Vector4* data, const long& count, VRamUsageHint hint = Fixed); VRamList(const Color4* data, const long& count, VRamUsageHint hint = Fixed); + /// Allocate an empty VBO + /// @param byte_count the size of the buffer in bytes. + /// @param element_array_buffer if applicable, whether the buffer is to be used for GLuint indices. + /// @param hint A hint to the graphics driver for what the buffer is to be used for. + VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint = Fixed); ~VRamList(); /** Copying around the VBO data to a new VBO like this is slow. * Pass to function by const reference or pointer always. */ VRamList(const VRamList& rhs); - VRamList() : list_handle(0), num_elements(0), element_array_buffer(false), spin_lock(false) {} + VRamList() : list_handle(0), byte_count(0), element_array_buffer(false) {} public: - [[nodiscard]] GLuint GetHandle() const; + [[nodiscard]] GLuint Handle() const; /// Returns the number of elements in the list. - [[nodiscard]] long GetLength() const; + [[nodiscard]] long Length() const; /// Returns the size of the data in bytes. - [[nodiscard]] size_t GetDataSize() const; + [[nodiscard]] size_t Size() const; /** Get VBO data back from the GPU. This is *bad* because the CPU is going to wait * for the transfer to finish. Has limited use other than testing. */ [[nodiscard]] std::vector GetDataF() const; [[nodiscard]] std::vector GetDataUI() const; - [[nodiscard]] bool IsFloatArray() const; /** Replace the data of an existing VBO in it's entirety. Must be same type. */ void SetData(const GLfloat* data, const long& count); void SetData(const Vector2* data, const long& count); @@ -70,6 +74,8 @@ public: /** Update only a portion of the data in a VBO. Must be same type. * "offset" refers the number of Typename T into the buffer the data you want to change is. * For ex, offset 0 and length of 1 overwrites the first value. Offset 1 the second etc */ + + // TODO provide a bool to specify whether the current buffer should be orphaned. void UpdateData(const GLfloat* data, const long& offset, const long& count); void UpdateData(const Vector2* data, const long& offset, const long& count); void UpdateData(const Vector3* data, const long& offset, const long& count); @@ -77,4 +83,8 @@ public: void UpdateData(const Color4* data, const long& offset, const long& count); void UpdateData(const GLuint* data, const long& offset, const long& count); void UpdateData(const Vector2i* data, const long& offset, const long& count); + + // Update only a portion of the data in a VBO using bytes. + // TODO This version of the function has no protection for out of bounds writes. + void UpdateData(const uint8_t* data, const long& offset, const long& count); }; \ No newline at end of file diff --git a/main.cpp b/main.cpp index c335605..3d7d513 100644 --- a/main.cpp +++ b/main.cpp @@ -142,7 +142,7 @@ public: shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"), {{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}}); - for (unsigned int i = 0; i < 100; i++) { + for (unsigned int i = 0; i < 1000000; i++) { rect_pos.emplace_back(420, 420); rect_size.emplace_back(20, 20); rect_colors.emplace_back(Colors::Red); diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 4ec0660..3c71617 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -275,7 +275,7 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRect); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->Handle()); if (rect_count == 1 || !supports_instanced || !current_state.current_shader) { glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); @@ -294,20 +294,20 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve else { current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true); - std::vector instances; - instances.reserve(rect_count); - + std::vector instances(rect_count); + Instance* ptr = instances.data(); + // Shader does translation to top left corner in this path - Redacted. for (size_t i = 0; i < rect_count; ++i) - instances.emplace_back(Vector2(positions[i].x, positions[i].y + sizes[i].y), sizes[i], colors[i]); + ptr[i] = { positions[i], sizes[i], colors[i] }; glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr); glVertexAttribDivisorARB(0, 0); - // count * 5 because 4x float & 4x uint8_t = sizeof(float). - VRamList instance_buffer((GLfloat*) instances.data(), instances.size() * 5, VRamUsageHint::Stream); + // * 5 because 4x float & 4x uint8_t = sizeof(float) - Redacted. + VRamList instance_buffer((GLfloat*) instances.data(), instances.size() * 5, Stream); - glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle()); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance), (GLvoid*) offsetof(Instance, position)); glVertexAttribDivisorARB(1, 1); @@ -1255,10 +1255,10 @@ void J2D::DrawPoints(const Color4* colors, const Vector2* points, int point_coun glPointSize(radius); glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_colors->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_colors->Handle()); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Color4), nullptr); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); if (current_state.current_shader) @@ -1278,7 +1278,7 @@ void J2D::DrawPoints(const Color4& color, const Vector2* points, int point_count ShapeCache::draw_points_positions->SetData(points, point_count); glPointSize(radius); glColor4ubv(color.ptr()); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle()); glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); if (current_state.current_shader) diff --git a/src/renderer/OpenGL/J3D.cpp b/src/renderer/OpenGL/J3D.cpp index 7432ef0..9f5e698 100644 --- a/src/renderer/OpenGL/J3D.cpp +++ b/src/renderer/OpenGL/J3D.cpp @@ -238,7 +238,7 @@ void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spher // TODO allocate once. VRamList vertex_data(vertices.data(), vertices.size()); - glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, vertex_data.Handle()); glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr); // Render each sphere in the batch at their given position and radius. @@ -246,10 +246,10 @@ void JGL::J3D::BatchWireframeRevoSphere(const Color4& color, const Sphere* spher glPushMatrix(); glTranslatef(spheres[i].Position.x, spheres[i].Position.y, spheres[i].Position.z); glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius); - glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength()); + glDrawArrays(GL_LINE_LOOP, 0, vertex_data.Length()); if (draw_stacks) glRotatef(90, 0, 1, 0), - glDrawArrays(GL_LINE_LOOP, 0, vertex_data.GetLength()); + glDrawArrays(GL_LINE_LOOP, 0, vertex_data.Length()); glPopMatrix(); } glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -372,9 +372,9 @@ void JGL::J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const glColor4ubv(color.ptr()); glLineWidth(thickness); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->Handle()); glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->GetHandle()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle()); for (size_t i = 0; i < box_count; i++) { Vector3 delta = (boxes[i].maxPoint - boxes[i].minPoint) / 2; @@ -382,7 +382,7 @@ void JGL::J3D::BatchWireframeAABB(const Color4& color, const AABB* boxes, const glPushMatrix(); glTranslatef(center.x, center.y, center.z); glScalef(delta.x, delta.y, delta.z); - glDrawElements(GL_LINE_LOOP, ShapeCache::cube_index_data->GetLength(), GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_LINE_LOOP, ShapeCache::cube_index_data->Length(), GL_UNSIGNED_INT, nullptr); glPopMatrix(); } glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -397,9 +397,9 @@ void JGL::J3D::WireframeAABB(const Color4& color, const Vector3& pos, const Vect void JGL::J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_t& box_count) { glColor4ubv(color.ptr()); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_vertex_data->Handle()); glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->GetHandle()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ShapeCache::cube_index_data->Handle()); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -408,7 +408,7 @@ void JGL::J3D::BatchFillAABB(const Color4& color, const AABB* boxes, const size_ if (UsingLighting()) { using_lights = true; glEnableClientState(GL_NORMAL_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_normal_data->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::cube_normal_data->Handle()); glNormalPointer(GL_FLOAT, sizeof(float), nullptr); } @@ -487,8 +487,8 @@ void JGL::J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const VRamList index_data(indices.data(), indices.size()); glColor4ubv(color.ptr()); - glBindBuffer(GL_ARRAY_BUFFER, vertex_data.GetHandle()); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, vertex_data.Handle()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_data.Handle()); glVertexPointer(3, GL_FLOAT, sizeof(Vector3), nullptr); for (size_t i = 0; i < sphere_count; i++) { @@ -496,7 +496,7 @@ void JGL::J3D::BatchFillSphere(const Color4& color, const Sphere* spheres, const glPushMatrix(); glTranslatef(position.x, position.y, position.z); glScalef(spheres[i].Radius, spheres[i].Radius, spheres[i].Radius); - glDrawElements(GL_TRIANGLES, index_data.GetLength(), GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_TRIANGLES, index_data.Length(), GL_UNSIGNED_INT, nullptr); glPopMatrix(); } glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -567,13 +567,13 @@ void JGL::J3D::DrawVertexArray(const Color4& color, const VertexArray& vertex_ar glColor4ubv(color.ptr()); glEnableClientState(GL_VERTEX_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, vertex_array.GetVertices()->GetHandle()); + glBindBuffer(GL_ARRAY_BUFFER, vertex_array.GetVertices()->Handle()); glVertexPointer(3, GL_FLOAT, 0, nullptr); glPushMatrix(); glTranslatef(position.x, position.y, position.z); //glScalef(1,1,1); - glDrawArrays(GL_TRIANGLES, 0, vertex_array.GetVertices()->GetLength()); + glDrawArrays(GL_TRIANGLES, 0, vertex_array.GetVertices()->Length()); glPopMatrix(); //glDrawElements(GL_LINES, vertex_array.GetIndices()->GetLength(), GL_UNSIGNED_INT, nullptr); diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 6fb6647..2f948a0 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -105,6 +105,7 @@ namespace JGL { Color4 color; public: Instance(const Vector2& position, const Vector2& size, const Color4& color) : position(position), size(size), color(color) {} + Instance() = default; ~Instance() = default; }; } diff --git a/src/types/VRamList.cpp b/src/types/VRamList.cpp index f0fe33d..2dc9769 100644 --- a/src/types/VRamList.cpp +++ b/src/types/VRamList.cpp @@ -5,7 +5,6 @@ // TODO combine the two load functions. void JGL::VRamList::load(const GLfloat* data, const long& size) { - spin_lock = true; GLint current_array_buffer = 0; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_array_buffer); @@ -14,14 +13,12 @@ void JGL::VRamList::load(const GLfloat* data, const long& size) { glBufferData(GL_ARRAY_BUFFER, size, data, usage_hint); glBindBuffer(GL_ARRAY_BUFFER, current_array_buffer); element_array_buffer = false; - num_elements = size / sizeof(GLfloat); + byte_count = size; - spin_lock = false; } void JGL::VRamList::load(const GLuint* data, const long& size) { - spin_lock = true; GLint current_element_array_buffer = 0; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_element_array_buffer); @@ -30,17 +27,14 @@ void JGL::VRamList::load(const GLuint* data, const long& size) { glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, usage_hint); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_element_array_buffer); element_array_buffer = true; - num_elements = size / sizeof(GLuint); + byte_count = size; - spin_lock = false; } void JGL::VRamList::Erase() { if (list_handle == 0) return; - while (spin_lock) {} - spin_lock = true; GLint current_element_array_buffer = 0; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_element_array_buffer); @@ -56,37 +50,28 @@ void JGL::VRamList::Erase() { glDeleteBuffers(1, &list_handle); list_handle = 0; - spin_lock = false; } -GLuint JGL::VRamList::GetHandle() const { +GLuint JGL::VRamList::Handle() const { return list_handle; } -bool JGL::VRamList::IsFloatArray() const { - return !element_array_buffer; +long JGL::VRamList::Length() const { + // sizeof(GLfloat) & sizeof(GLuint) are both 4 - Redacted. + return byte_count / 4; } -long JGL::VRamList::GetLength() const { - return num_elements; -} - -size_t JGL::VRamList::GetDataSize() const { - if (element_array_buffer) - return sizeof(GLuint) * num_elements; - return sizeof(GLfloat) * num_elements; +size_t JGL::VRamList::Size() const { + return byte_count; } void JGL::VRamList::SetData(void* data, const long& length) { - while (spin_lock) {} - spin_lock = true; - bool should_resize = (this->num_elements != length); + bool should_resize = (this->byte_count != length * 4); if (should_resize) { glDeleteBuffers(1, &list_handle); list_handle = 0; - spin_lock = false; element_array_buffer ? load((GLuint*) data, sizeof(GLuint) * length) : load((GLfloat*) data, sizeof(GLfloat) * length); return; } @@ -110,21 +95,18 @@ void JGL::VRamList::SetData(void* data, const long& length) { glBindBuffer(buffer_type, current_buffer); - spin_lock = false; } void JGL::VRamList::UpdateData(void* data, const long& offset, const long& length) { - while (spin_lock) {} - spin_lock = true; - if (offset + length > num_elements) { - unsigned long oob_delta = (offset + length) - num_elements; + if (offset + length > Length()) { + unsigned long oob_delta = (offset + length) - Length(); if (element_array_buffer) { auto list_data = GetDataUI(); list_data.resize(list_data.size() + oob_delta); memcpy(list_data.data() + (offset * sizeof(GLuint)), data, length * sizeof(GLuint)); - spin_lock = false; // This is going unlock and relock really fast, But this code fixes something considered wrong anyway - Redacted. + // This is going unlock and relock really fast, But this code fixes something considered wrong anyway - Redacted. return SetData(list_data.data(), list_data.size()); } @@ -132,7 +114,6 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt auto list_data = GetDataF(); list_data.resize(list_data.size() + oob_delta); memcpy(list_data.data() + (offset * sizeof(GLfloat)), data, length * sizeof(GLfloat)); - spin_lock = false; return SetData(list_data.data(), list_data.size()); } @@ -149,17 +130,11 @@ void JGL::VRamList::UpdateData(void* data, const long& offset, const long& lengt size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat); - if (usage_hint != Fixed) - glBufferData(GL_ARRAY_BUFFER, length * element_size, nullptr, usage_hint); glBufferSubData(buffer_type, offset * element_size, length * element_size, data); - glBindBuffer(buffer_type, current_buffer); - - spin_lock = false; } std::vector JGL::VRamList::GetDataF() const { - while (spin_lock) {} if (element_array_buffer) JGL::Logger::Warning("Getting data as GLfloat but the buffer data is GLuint?"); @@ -172,12 +147,12 @@ std::vector JGL::VRamList::GetDataF() const { glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_buffer); glBindBuffer(GL_ARRAY_BUFFER, list_handle); - std::vector data(num_elements); + std::vector data(byte_count / 4); void* vram = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); if (vram == nullptr) JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?"); - memcpy(data.data(), vram, num_elements * sizeof(GLfloat)); + memcpy(data.data(), vram, (byte_count / 4) * sizeof(GLfloat)); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, current_buffer); @@ -188,7 +163,6 @@ std::vector JGL::VRamList::GetDataF() const { } std::vector JGL::VRamList::GetDataUI() const { - while (spin_lock) {} if (!element_array_buffer) JGL::Logger::Warning("Getting data as GLuint but the buffer data is GLfloat?"); @@ -197,13 +171,13 @@ std::vector JGL::VRamList::GetDataUI() const { glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, list_handle); - std::vector data(num_elements); + std::vector data(byte_count / 4); void* vram = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); if (vram == nullptr) JGL::Logger::Fatal("Mapping in a VBO that doesn't exist?"); - memcpy(data.data(), vram, num_elements * sizeof(GLuint)); + memcpy(data.data(), vram, (byte_count / 4) * sizeof(GLuint)); glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, current_buffer); @@ -301,8 +275,6 @@ JGL::VRamList::~VRamList() { } JGL::VRamList::VRamList(const JGL::VRamList& rhs) { - while (rhs.spin_lock) {} - if (rhs.element_array_buffer) { auto data_array = rhs.GetDataUI(); this->load(data_array.data(), data_array.size()); @@ -311,3 +283,32 @@ JGL::VRamList::VRamList(const JGL::VRamList& rhs) { auto data_array = rhs.GetDataF(); this->load(data_array.data(), data_array.size()); } + +JGL::VRamList::VRamList(const size_t& byte_count, bool element_array_buffer, VRamUsageHint hint) { + this->element_array_buffer = element_array_buffer; + this->byte_count = byte_count; + + GLint current_buffer = 0; + GLenum buffer = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; + GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING; + + glGetIntegerv(buffer_binding, ¤t_buffer); + glGenBuffers(1, &list_handle); + glBindBuffer(buffer, list_handle); + glBufferData(buffer, byte_count, nullptr, hint); + + glBindBuffer(buffer, current_buffer); +} + +void JGL::VRamList::UpdateData(const uint8_t* data, const long& offset, const long& count) { + GLint current_buffer = 0; + GLenum buffer_type = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; + GLenum buffer_binding = element_array_buffer ? GL_ELEMENT_ARRAY_BUFFER_BINDING : GL_ARRAY_BUFFER_BINDING; + + glGetIntegerv(buffer_binding, ¤t_buffer); + glBindBuffer(buffer_type, list_handle); + + glBufferSubData(buffer_type, offset, count, data); + + glBindBuffer(buffer_type, current_buffer); +} From 6f99689addc641dd520e2bee8f4b10fb2a1194f5 Mon Sep 17 00:00:00 2001 From: Redacted Date: Mon, 12 May 2025 11:38:28 -0400 Subject: [PATCH 14/18] Performance optimization. --- CMakeLists.txt | 2 +- assets/shader_programs/test_vertex.glsl | 11 +---- include/JGL/JGL.h | 4 +- include/JGL/types/Instance.h | 49 +++++++++++++++++++++++ main.cpp | 13 ++---- src/ShapeCache.cpp | 3 +- src/renderer/OpenGL/J2D.cpp | 41 ++++++++----------- src/renderer/OpenGL/internals/internals.h | 11 ----- src/types/VRamList.cpp | 5 --- 9 files changed, 76 insertions(+), 63 deletions(-) create mode 100644 include/JGL/types/Instance.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5686f44..aa057f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ if (WIN32) ) endif() -set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra") +#set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra") file(COPY "assets" DESTINATION "${PROJECT_BINARY_DIR}") file(GLOB_RECURSE ASSETS "assets/*") diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index bbc946f..0ff03db 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -72,15 +72,6 @@ vec4 DefaultInstanced() { return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); } -vec4 FillRectInstanced() { - vec2 instance_pos = a_instance_position + vec2(0.0, a_instance_size.y); - vec2 scaled = a_vertex_position * a_instance_size; - vec2 world_pos = scaled + instance_pos; - - v_color = a_instance_color; - return gl_ModelViewProjectionMatrix * vec4(world_pos, 0.0, 1.0); -} - void main() { GL_TEXTURE0_COORD = gl_MultiTexCoord0.xy; GL_TEXTURE1_COORD = gl_MultiTexCoord1.xy; @@ -156,7 +147,7 @@ void main() { */ if (JGL_RENDERING_ROUTINE == J2D_FillRect && JGL_INSTANCED_RENDERING) - gl_Position = FillRectInstanced(); + gl_Position = DefaultInstanced(); else gl_Position = Default(); } diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 854525b..7139c08 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -398,8 +399,7 @@ namespace JGL::J2D { void OutlineEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, float thickness = 1, int subdivisions = 8); void FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions = 8); - void BatchFillRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, const size_t& rect_count); - + void BatchFillRect(const Instance2D* instances, const size_t& instance_count); void BatchFillCircle(const Color4 *colors, const Vector2* positions, float* radii, unsigned int subdivisions, const size_t& circle_count); } diff --git a/include/JGL/types/Instance.h b/include/JGL/types/Instance.h new file mode 100644 index 0000000..fb10b47 --- /dev/null +++ b/include/JGL/types/Instance.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace JGL { + struct Instance2D; + struct Instance3D; +} + +/// Information provided to JGL for each instance of an object you're rendering in a batch. +/// @note This is non-negotiable. This information is interleaved in *the same buffer* in v-ram for performance reasons - Redacted. +struct JGL::Instance2D { +public: + Color4 color; + Vector2 position; + Vector2 size; + Vector2 scale; + float rotation; +public: + /// @param position The position of the instance in world space. + /// @param size The size of the instance. + /// @param scale A multiplier to be applied to the size. + /// @param rotation The rotation in radians. + /// @param color A 3-or-4 channel color value. @see class Color3, class Color4. + /// @note If the thing you're drawing is textured you probably want Colors::White + Instance2D(const Color4& color, const Vector2& position, const Vector2& size, const Vector2& scale = Vector2::One, float rotation = 0.0f) : + color(color), position(position), size(size), scale(scale), rotation(rotation) {}; + Instance2D() = default; + ~Instance2D() = default; +}; + +struct JGL::Instance3D { +public: + Matrix4x4 instance_matrix; + Color4 color; +public: + /// @param instance_matrix A matrix containing rotation matrix, position, and scale. + /// @param color A 3-or-4 channel color value. @see class Color3, class Color4. + /// @note If the thing you're drawing is textured you probably want Colors::White + Instance3D(const Matrix4x4& instance_matrix, const Color4& color) : instance_matrix(instance_matrix), color(color) {}; + Instance3D() = default; + ~Instance3D() = default; +}; + + + + diff --git a/main.cpp b/main.cpp index 3d7d513..94893dd 100644 --- a/main.cpp +++ b/main.cpp @@ -15,9 +15,7 @@ using namespace JGL; using JGL::Font; float fps = 0.0f; -std::vector rect_pos; -std::vector rect_size; -std::vector rect_colors; +std::vector rect_instances; /// A draggable 2D point that highlights when moused over and when clicked. class Gizmo { @@ -142,11 +140,8 @@ public: shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"), {{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}}); - for (unsigned int i = 0; i < 1000000; i++) { - rect_pos.emplace_back(420, 420); - rect_size.emplace_back(20, 20); - rect_colors.emplace_back(Colors::Red); - } + for (unsigned int i = 0; i < 100; i++) + rect_instances.emplace_back(Colors::Red, Vector2(420, 420), Vector2(20, 20)); } EulerAngleXYZ textAngle = {0,0,0}; @@ -202,7 +197,7 @@ public: J2D::Begin(j2d_render_target, shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); - J2D::BatchFillRect(rect_colors.data(), rect_pos.data(), rect_size.data(), rect_pos.size()); + J2D::BatchFillRect(rect_instances.data(), rect_instances.size()); J2D::DrawSprite(image, {300, 400}, sprite_radians * 0.10f, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawMirrorSprite(image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White); J2D::DrawPartialSprite(image, Vector2(225, 300), Vector2(image->GetDimensions()) * 0.25, Vector2(image->GetDimensions()) * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White); diff --git a/src/ShapeCache.cpp b/src/ShapeCache.cpp index 888478e..23b921e 100644 --- a/src/ShapeCache.cpp +++ b/src/ShapeCache.cpp @@ -47,7 +47,8 @@ void JGL::ShapeCache::Init() { cube_normal_data = new VRamList(vertex_normals.data(), vertex_normals.size()); if (!square_origin_topleft_vertex_data) { - std::array square_vertices = { Vector2(0, 0), {1, 0}, {1, -1}, {0, -1} }; + //std::array square_vertices = { Vector2(0, 0), {1, 0}, {1, -1}, {0, -1} }; + std::array square_vertices = { Vector2(0, 0), {0, 1}, {1, 1}, {1, 0} }; square_origin_topleft_vertex_data = new VRamList(square_vertices.data(), square_vertices.size()); } if (!j2d_default_normal_data) { diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 3c71617..4baf030 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "internals/internals.h" @@ -262,14 +263,14 @@ void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& si } void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) { - BatchFillRect(&color, &pos, &size, 1); + Instance2D rect(color, pos, size); + BatchFillRect(&rect, 1); } - -void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, const size_t& rect_count) { +void J2D::BatchFillRect(const Instance2D* instances, const size_t& instance_count) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); - if (rect_count <= 0) + if (instance_count <= 0) return; if (current_state.current_shader) @@ -277,14 +278,14 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->Handle()); - if (rect_count == 1 || !supports_instanced || !current_state.current_shader) { + if (instance_count == 1 || !supports_instanced || !current_state.current_shader) { glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); - for (size_t i = 0; i < rect_count; i++) { + for (size_t i = 0; i < instance_count; i++) { glPushMatrix(); - glColor4ubv(colors[i].ptr()); - glTranslatef(positions[i].x, positions[i].y + sizes[i].y, 0); - glScalef(sizes[i].x, sizes[i].y, 1); + glColor4ubv(instances[i].color.ptr()); + glTranslatef(instances[i].position.x, instances[i].position.y, 0); + glScalef(instances[i].size.x, instances[i].size.y, 1); glDrawArrays(GL_QUADS, 0, 4); glPopMatrix(); } @@ -294,33 +295,26 @@ void J2D::BatchFillRect(const Color4* colors, const Vector2* positions, const Ve else { current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true); - std::vector instances(rect_count); - Instance* ptr = instances.data(); - // Shader does translation to top left corner in this path - Redacted. - for (size_t i = 0; i < rect_count; ++i) - ptr[i] = { positions[i], sizes[i], colors[i] }; - glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr); glVertexAttribDivisorARB(0, 0); - // * 5 because 4x float & 4x uint8_t = sizeof(float) - Redacted. - VRamList instance_buffer((GLfloat*) instances.data(), instances.size() * 5, Stream); + VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream); glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle()); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance), (GLvoid*) offsetof(Instance, position)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position)); glVertexAttribDivisorARB(1, 1); glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance), (GLvoid*) offsetof(Instance, size)); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, size)); glVertexAttribDivisorARB(2, 1); glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance), (GLvoid*) offsetof(Instance, color)); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color)); glVertexAttribDivisorARB(3, 1); - glDrawArraysInstancedARB(GL_QUADS, 0, 4, rect_count); + glDrawArraysInstancedARB(GL_QUADS, 0, 4, instance_count); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); @@ -368,8 +362,6 @@ void J2D::FillGradientRect(const Color4& top_left_color, const Color4& bottom_le void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) { std::array colors = { color, color, color, color }; - std::array rect_positions = { Vector2(pos.x + radius, pos.y), {pos.x, pos.y + radius} }; - std::array rect_sizes = { Vector2(size.x - 2 * radius, size.y), {size.x, size.y - 2 * radius} }; std::array circle_radii = { radius, radius, radius, radius }; std::array circle_positions { @@ -380,7 +372,8 @@ void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2 if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect); - J2D::BatchFillRect(colors.data(), rect_positions.data(), rect_sizes.data(), 2); + std::array rect_instances { Instance2D(color,Vector2(pos.x + radius, pos.y), Vector2(size.x - 2 * radius, size.y)), Instance2D(color, Vector2(pos.x, pos.y + radius), Vector2(size.x, size.y - 2 * radius)) }; + J2D::BatchFillRect(rect_instances.data(), rect_instances.size()); J2D::BatchFillCircle(colors.data(), circle_positions.data(), circle_radii.data(), subdivisions, 4); if (current_state.current_shader) diff --git a/src/renderer/OpenGL/internals/internals.h b/src/renderer/OpenGL/internals/internals.h index 2f948a0..d8e3334 100644 --- a/src/renderer/OpenGL/internals/internals.h +++ b/src/renderer/OpenGL/internals/internals.h @@ -97,17 +97,6 @@ namespace JGL { inline StateStack state_stack; inline Vector2i window_size; inline bool supports_instanced = true; - - struct Instance { - public: - Vector2 position; - Vector2 size; - Color4 color; - public: - Instance(const Vector2& position, const Vector2& size, const Color4& color) : position(position), size(size), color(color) {} - Instance() = default; - ~Instance() = default; - }; } namespace JGL::J2D { diff --git a/src/types/VRamList.cpp b/src/types/VRamList.cpp index 2dc9769..26c1cdc 100644 --- a/src/types/VRamList.cpp +++ b/src/types/VRamList.cpp @@ -67,7 +67,6 @@ size_t JGL::VRamList::Size() const { void JGL::VRamList::SetData(void* data, const long& length) { - bool should_resize = (this->byte_count != length * 4); if (should_resize) { glDeleteBuffers(1, &list_handle); @@ -89,12 +88,8 @@ void JGL::VRamList::SetData(void* data, const long& length) { size_t element_size = element_array_buffer ? sizeof(GLuint) : sizeof(GLfloat); - if (usage_hint != Fixed) - glBufferData(GL_ARRAY_BUFFER, length * element_size, nullptr, usage_hint); glBufferSubData(buffer_type, 0, length * element_size, data); - glBindBuffer(buffer_type, current_buffer); - } void JGL::VRamList::UpdateData(void* data, const long& offset, const long& length) { From a5424eb3700911224128e307474fe7a052077068 Mon Sep 17 00:00:00 2001 From: Redacted Date: Sat, 17 May 2025 17:04:27 -0400 Subject: [PATCH 15/18] BatchFillCircle Instanced Rendering. Added JGL::ClearScreen as-well to clear fbo 0 at any time. --- assets/shader_programs/test_vertex.glsl | 2 +- include/JGL/JGL.h | 4 +- main.cpp | 6 +- src/renderer/OpenGL/J2D.cpp | 123 ++++++++++++++++++++---- 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/assets/shader_programs/test_vertex.glsl b/assets/shader_programs/test_vertex.glsl index 0ff03db..38ee1d1 100644 --- a/assets/shader_programs/test_vertex.glsl +++ b/assets/shader_programs/test_vertex.glsl @@ -146,7 +146,7 @@ void main() { else { gl_Position = Default(); } */ - if (JGL_RENDERING_ROUTINE == J2D_FillRect && JGL_INSTANCED_RENDERING) + if (JGL_INSTANCED_RENDERING) gl_Position = DefaultInstanced(); else gl_Position = Default(); diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 7139c08..f687cb7 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -60,6 +60,8 @@ namespace JGL { [[nodiscard]] bool Init(const Vector2i& window_size, float fovY, float far_plane); void Update(const Vector2i& window_size); + /// Clear the default framebuffer for the OpenGL context (0). + void ClearScreen(const Color4& clear_color); inline void PurgeFontCache() { JGL::fontCache.purgeCache(); } @@ -400,7 +402,7 @@ namespace JGL::J2D { void FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions = 8); void BatchFillRect(const Instance2D* instances, const size_t& instance_count); - void BatchFillCircle(const Color4 *colors, const Vector2* positions, float* radii, unsigned int subdivisions, const size_t& circle_count); + void BatchFillCircle(const Instance2D* instances, float subdivisions, const size_t& instance_count); } /// Drawing functions for 3D objects. diff --git a/main.cpp b/main.cpp index 94893dd..0fffe2d 100644 --- a/main.cpp +++ b/main.cpp @@ -130,7 +130,7 @@ public: glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); - image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST, JGL::SampleRate::X16); + image = new Texture("assets/sprites/Re3D.png", FilteringMode::MIPMAP_NEAREST); image_mask = new Texture("assets/sprites/alpha_mask_2.png"); j2d_render_target = new RenderTarget({540, 500}, {0,0,0,0}, false, SampleRate::NONE, FilteringMode::MIPMAP_NEAREST); @@ -194,7 +194,7 @@ public: J3D::FillAABB(Colors::Whites::AliceBlue, {0,0,0.5f}, {0.05f, 0.05f, 0.05f}); J3D::WireframeAABB(Colors::Yellow, {0.5, 0, 0.5}, {0.125, 0.125, 0.125}, 1); J3D::End(); - + //JGL::ClearScreen(Colors::Red); J2D::Begin(j2d_render_target, shader, true); J2D::FillRect(Colors::Blue, {0,52}, {100,100}); J2D::BatchFillRect(rect_instances.data(), rect_instances.size()); @@ -237,7 +237,7 @@ public: J2D::End(); - J2D::Begin(nullptr, shader, true); + J2D::Begin(nullptr, nullptr, true); J2D::DrawRenderTarget(j2d_render_target, {0, 0}); J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 4baf030..c258ea5 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -5,6 +5,41 @@ #include "internals/internals.h" +void JGL::ClearScreen(const Color4& clear_color) { + GLuint current_framebuffer = 0; + GLint current_viewport[4] = {0, 0, 0, 0}; + GLfloat current_clear_color[4]; + + glGetIntegerv(GL_VIEWPORT, current_viewport); + glGetFloatv(GL_COLOR_CLEAR_VALUE, current_clear_color); + + current_framebuffer = RenderTarget::GetActiveGLFramebufferHandle(); + if (current_framebuffer) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + bool changed_viewport = false; + if (current_viewport[2] != window_size.x || current_viewport[3] != window_size.y) + glViewport(0, 0, window_size.x, window_size.y), + changed_viewport = true; + + GLint has_depth = 0; + glGetIntegerv(GL_DEPTH_BITS, &has_depth); + glClearColor(clear_color.RN(), clear_color.GN(), clear_color.BN(), clear_color.AN()); + + if (has_depth) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear(GL_COLOR_BUFFER_BIT); + + glClearColor(current_clear_color[0], current_clear_color[1], current_clear_color[2], current_clear_color[3]); + + if (current_framebuffer) + glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); + + if (changed_viewport) + glViewport(current_viewport[0], current_viewport[1], current_viewport[2], current_viewport[3]); +} + void J2D::Begin(RenderTarget* render_target, Shader* shader, bool clear_buffers) { State new_state = default_state; state_stack.Push(State::SaveState(current_state)); @@ -266,6 +301,7 @@ void J2D::FillRect(const Color4& color, const Vector2& pos, const Vector2& size) Instance2D rect(color, pos, size); BatchFillRect(&rect, 1); } + void J2D::BatchFillRect(const Instance2D* instances, const size_t& instance_count) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); @@ -361,20 +397,25 @@ void J2D::FillGradientRect(const Color4& top_left_color, const Color4& bottom_le } void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) { - std::array colors = { color, color, color, color }; - std::array circle_radii = { radius, radius, radius, radius }; - std::array circle_positions - { - Vector2(pos.x + radius, pos.y + radius), {pos.x + size.x - radius, pos.y + radius}, - {pos.x + radius, pos.y + size.y - radius}, {pos.x + size.x - radius, pos.y + size.y - radius} - }; - if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect); - std::array rect_instances { Instance2D(color,Vector2(pos.x + radius, pos.y), Vector2(size.x - 2 * radius, size.y)), Instance2D(color, Vector2(pos.x, pos.y + radius), Vector2(size.x, size.y - 2 * radius)) }; + std::array circle_instances + { + Instance2D(color, Vector2(pos.x + radius, pos.y + radius), Vector2::One, Vector2(radius, radius)), + Instance2D(color, Vector2(pos.x + size.x - radius, pos.y + radius), Vector2::One, Vector2(radius, radius)), + Instance2D(color, Vector2(pos.x + radius, pos.y + size.y - radius), Vector2::One, Vector2(radius, radius)), + Instance2D(color, Vector2(pos.x + size.x - radius, pos.y + size.y - radius), Vector2::One, Vector2(radius, radius)) + }; + + std::array rect_instances + { + Instance2D(color,Vector2(pos.x + radius, pos.y), Vector2(size.x - 2 * radius, size.y)), + Instance2D(color, Vector2(pos.x, pos.y + radius), Vector2(size.x, size.y - 2 * radius)) + }; + J2D::BatchFillRect(rect_instances.data(), rect_instances.size()); - J2D::BatchFillCircle(colors.data(), circle_positions.data(), circle_radii.data(), subdivisions, 4); + J2D::BatchFillCircle(circle_instances.data(), subdivisions, circle_instances.size()); if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); @@ -928,14 +969,15 @@ void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius } void J2D::FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions) { - BatchFillCircle(&color, ¢er, &radius, subdivisions, 1); + Instance2D circle(color, center, Vector2(1, 1), Vector2(radius, radius)); + BatchFillCircle(&circle, subdivisions, 1); } -void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float* radii, unsigned int subdivisions, const size_t& circle_count) { +void J2D::BatchFillCircle(const JGL::Instance2D* instances, float subdivisions, const size_t &instance_count) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); - if (circle_count <= 0) + if (instance_count <= 0) return; GLfloat angle, x, y; @@ -956,18 +998,59 @@ void J2D::BatchFillCircle(const Color4* colors, const Vector2* positions, float* i++; } - glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); + auto vertex_buffer = VRamList(vertices.data(), vertices.size(), Stream); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.Handle()); if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillCircle); - for (size_t j = 0; j < circle_count; j++) { - glPushMatrix(); - glColor4ubv(colors[j].ptr()); - glTranslatef(positions[j].x, positions[j].y, 0); - glScalef(radii[j], radii[j], 0); + if (instance_count == 1 || !supports_instanced || !current_state.current_shader) { + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + + for (size_t j = 0; j < instance_count; j++) { + glPushMatrix(); + glColor4ubv(instances[j].color.ptr()); + glTranslatef(instances[j].position.x, instances[j].position.y, 0); + glScalef(instances[j].scale.x, instances[j].scale.y, 0); glDrawArrays(GL_TRIANGLE_FAN, 0, (int) vertices.size()); - glPopMatrix(); + glPopMatrix(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + else { + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr); + glVertexAttribDivisorARB(0, 0); + + VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream); + + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle()); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position)); + glVertexAttribDivisorARB(1, 1); + + glEnableVertexAttribArray(2); + // Swapped scale with size in this path because we always render a unit circle scaled up to the given size. + // TODO implement scaling attribute in vertex shader. + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, scale)); + glVertexAttribDivisorARB(2, 1); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color)); + glVertexAttribDivisorARB(3, 1); + + glDrawArraysInstancedARB(GL_TRIANGLE_FAN, 0, vertices.size(), instance_count); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false); } if (current_state.current_shader) From 74ab9d25db864a762e79bfa39ad82e529635422c Mon Sep 17 00:00:00 2001 From: Redacted Date: Sun, 18 May 2025 00:07:58 -0400 Subject: [PATCH 16/18] BatchFillRoundedRect --- include/JGL/JGL.h | 2 ++ src/renderer/OpenGL/J2D.cpp | 32 +++++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index f687cb7..07b4a65 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -205,6 +205,8 @@ namespace JGL::J2D { /// @param subdivisions The amount of sub-divisions (and calculations) to be performed per-arc rounding corner. void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8); + void BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count); + /// Draws a filled rectangle with chamfered (beveled) corners on the screen. /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 /// @param pos The top-left corner of the rectangle. diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index c258ea5..691670f 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -396,23 +396,25 @@ void J2D::FillGradientRect(const Color4& top_left_color, const Color4& bottom_le glColor4fv(default_state.draw_color); } -void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) { +void J2D::BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count) { + std::vector rect_instances(count * 2); + std::vector circle_instances(count * 4); + if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect); - std::array circle_instances - { - Instance2D(color, Vector2(pos.x + radius, pos.y + radius), Vector2::One, Vector2(radius, radius)), - Instance2D(color, Vector2(pos.x + size.x - radius, pos.y + radius), Vector2::One, Vector2(radius, radius)), - Instance2D(color, Vector2(pos.x + radius, pos.y + size.y - radius), Vector2::One, Vector2(radius, radius)), - Instance2D(color, Vector2(pos.x + size.x - radius, pos.y + size.y - radius), Vector2::One, Vector2(radius, radius)) - }; + for (unsigned int i = 0; i < count; i++) { + unsigned int rect_i = i * 2; + unsigned int circle_i = i * 4; - std::array rect_instances - { - Instance2D(color,Vector2(pos.x + radius, pos.y), Vector2(size.x - 2 * radius, size.y)), - Instance2D(color, Vector2(pos.x, pos.y + radius), Vector2(size.x, size.y - 2 * radius)) - }; + rect_instances[rect_i] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y), Vector2(sizes[i].x - 2 * radius, sizes[i].y)); + rect_instances[rect_i + 1] = Instance2D(colors[i], Vector2(positions[i].x, positions[i].y + radius), Vector2(sizes[i].x, sizes[i].y - 2 * radius)); + + circle_instances[circle_i] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y + radius), Vector2::One, Vector2(radius, radius)); + circle_instances[circle_i + 1] = Instance2D(colors[i], Vector2(positions[i].x + sizes[i].x - radius, positions[i].y + radius), Vector2::One, Vector2(radius, radius)); + circle_instances[circle_i + 2] = Instance2D(colors[i], Vector2(positions[i].x + radius, positions[i].y + sizes[i].y - radius), Vector2::One, Vector2(radius, radius)); + circle_instances[circle_i + 3] = Instance2D(colors[i], Vector2(positions[i].x + sizes[i].x - radius, positions[i].y + sizes[i].y - radius), Vector2::One, Vector2(radius, radius)); + } J2D::BatchFillRect(rect_instances.data(), rect_instances.size()); J2D::BatchFillCircle(circle_instances.data(), subdivisions, circle_instances.size()); @@ -421,6 +423,10 @@ void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2 current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); } +void J2D::FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, unsigned int subdivisions) { + J2D::BatchFillRoundedRect(&color, &pos, &size, radius, subdivisions, 1); +} + void J2D::DrawSprite(const Texture* texture, float positionX, float positionY, float rad_rotation, float originX, float originY,float scaleX, float scaleY, const Color4& color, Direction inversion) { From ad3b451659bad407ee33a30f1ddfd872861b37db Mon Sep 17 00:00:00 2001 From: Redacted Date: Sun, 25 May 2025 16:33:58 -0400 Subject: [PATCH 17/18] BatchFillOutlineRect --- include/JGL/JGL.h | 2 ++ src/renderer/OpenGL/J2D.cpp | 62 +++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/include/JGL/JGL.h b/include/JGL/JGL.h index 07b4a65..e24097c 100644 --- a/include/JGL/JGL.h +++ b/include/JGL/JGL.h @@ -165,6 +165,8 @@ namespace JGL::J2D { /// @param thickness The width at which to render the lines. void OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness = 1); + void BatchOutlineRect(const Instance2D* instances, float thickness, const size_t& instance_count); + /// Draws an outline of a rectangle with rounded corners onto the screen. /// @param color A 3-or-4 channel color value. @see class Color3, class Color4 /// @param pos The top-left corner of the rectangle. diff --git a/src/renderer/OpenGL/J2D.cpp b/src/renderer/OpenGL/J2D.cpp index 691670f..c1a25ec 100644 --- a/src/renderer/OpenGL/J2D.cpp +++ b/src/renderer/OpenGL/J2D.cpp @@ -277,23 +277,73 @@ void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float } void J2D::OutlineRect(const Color4& color, const Vector2& pos, const Vector2& size, float thickness) { + Instance2D rect(color, pos, size); + J2D::BatchOutlineRect(&rect, thickness, 1); +} + +void J2D::BatchOutlineRect(const Instance2D* instances, float thickness, const size_t& instance_count) { if (!state_stack.Size()) Logger::Error("Drawing J2D element before J2D begin."); - Vector2 vertices[] = {{pos.x, pos.y}, {pos.x, pos.y + size.y}, {pos.x + size.x, pos.y + size.y}, {pos.x + size.x, pos.y}}; - - glLineWidth(thickness); - glColor4ubv(color.ptr()); + if (instance_count <= 0) + return; if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRect); - glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices); + glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::square_origin_topleft_vertex_data->Handle()); + glLineWidth(thickness); + + if (instance_count == 1 || !supports_instanced || !current_state.current_shader) { + glVertexPointer(2, GL_FLOAT, sizeof(Vector2), nullptr); + + for (size_t i = 0; i < instance_count; i++) { + glPushMatrix(); + glColor4ubv(instances[i].color.ptr()); + glTranslatef(instances[i].position.x, instances[i].position.y, 0); + glScalef(instances[i].size.x, instances[i].size.y, 1); + glDrawArrays(GL_LINE_LOOP, 0, 4); + glPopMatrix(); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + else { + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", true); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), nullptr); + glVertexAttribDivisorARB(0, 0); + + VRamList instance_buffer((GLfloat*) instances, instance_count * (sizeof(Instance2D) / sizeof(GLfloat)), Stream); + + glBindBuffer(GL_ARRAY_BUFFER, instance_buffer.Handle()); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, position)); + glVertexAttribDivisorARB(1, 1); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, size)); + glVertexAttribDivisorARB(2, 1); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Instance2D), (GLvoid*) offsetof(Instance2D, color)); + glVertexAttribDivisorARB(3, 1); + + glDrawArraysInstancedARB(GL_LINE_LOOP, 0, 4, instance_count); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + current_state.current_shader->SetBool("JGL_INSTANCED_RENDERING", false); + } if (current_state.current_shader) current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0); - glDrawArrays(GL_LINE_LOOP, 0, 4); glColor4fv(default_state.draw_color); } From ee90e7f95b4f8d53e2e2496bfd2006565b7a0dea Mon Sep 17 00:00:00 2001 From: Redacted Date: Thu, 29 May 2025 22:58:25 -0400 Subject: [PATCH 18/18] Fix measure string. --- include/JGL/types/FontCache.h | 6 +-- main.cpp | 16 +++++--- src/renderer/OpenGL/TextRendering.cpp | 7 +++- src/types/Font.cpp | 57 +++++++-------------------- src/types/FontCache.cpp | 4 +- 5 files changed, 35 insertions(+), 55 deletions(-) diff --git a/include/JGL/types/FontCache.h b/include/JGL/types/FontCache.h index 01a52c7..8d7a0e1 100644 --- a/include/JGL/types/FontCache.h +++ b/include/JGL/types/FontCache.h @@ -22,11 +22,11 @@ private: std::array texcoords; public: int x2offset = 0, y2offset = 0, w = 0, h = 0; - float advanceX = 0, advanceY = 0; + float advanceX = 0, advanceY = 0, ascent = 0, descent = 0; //CachedGlyph(GLuint texture_id, char c); - CachedGlyph(char c, std::array texcoords, float x2o, float y2o, float w, float h, float advX, float advY); - char getCharacter() const; + CachedGlyph(char c, std::array texcoords, float x2o, float y2o, float w, float h, float advX, float advY, float asc, float desc); + [[nodiscard]] char getCharacter() const; [[nodiscard]] std::array getTexCoords() const; }; diff --git a/main.cpp b/main.cpp index 0fffe2d..fa88421 100644 --- a/main.cpp +++ b/main.cpp @@ -113,6 +113,7 @@ Texture* image; Texture* image_mask; RenderTarget* j2d_render_target; Shader* shader; +Vector2 result; class JGLDemoWindow : public ReWindow::OpenGLWindow { @@ -140,6 +141,7 @@ public: shader = new Shader(std::filesystem::path("assets/shader_programs/test_vertex.glsl"), std::filesystem::path("assets/shader_programs/test_fragment.glsl"), {{"a_vertex_position", 0}, {"a_instance_position", 1}, {"a_instance_size", 2}, {"a_instance_color", 3}}); + result = Jupiteroid.MeasureString("The quick black fox jumps over the lazy dog.", 16); for (unsigned int i = 0; i < 100; i++) rect_instances.emplace_back(Colors::Red, Vector2(420, 420), Vector2(20, 20)); } @@ -207,6 +209,7 @@ public: J2D::FillRoundedRect(Colors::Purples::BlueViolet, {300, 52}, {100, 100}, 8, 4); J2D::FillCircle(Colors::White, {52, 204}, 50, 24); J2D::OutlineCircle(Colors::White, {153, 204}, 50, 24); + auto box = JGL::Fonts::Jupiteroid.MeasureString("Hello g", 16); J2D::FillChamferRect(Colors::Reds::LightSalmon, {150, 400}, {64, 64}, 5); J2D::OutlineRoundedRect(Colors::Reds::LightCoral, {250, 350}, {128, 128}, 10, 2); @@ -214,12 +217,13 @@ public: J2D::FillGradientTriangle(Color4(Colors::Red), Color4(Colors::Green), Color4(Colors::Blue), {{0, 275}, {0, 375}, {100, 375}}); J2D::OutlineTriangle(Colors::Blue, {{100, 275}, {0, 275}, {100, 375}}); J2D::DrawGradientLine(Colors::Red, Colors::Blue, {105, 375}, {200, 275}, 2); - auto result = Jupiteroid.MeasureString("Jupiteroid Font", 16); + J2D::DrawString(Colors::Green, "The quick black fox jumps over the lazy dog.", 0, 20, 1, 16); + J2D::OutlineRect(Colors::Red, {0, 20}, result, 1); - J2D::DrawString(Colors::Green, "Jupteroid Font", 0.f, 0, 1.f, 16, Jupiteroid); - J2D::DrawString(Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 16, 1,16, Jupiteroid); - J2D::DrawString(Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 33, 1,16, Jupiteroid); - J2D::DrawString(Colors::White, "Framerate: " + std::to_string((int) fps), 0, 48, 1, 16, Jupiteroid); + //J2D::DrawString(Colors::Green, "Jupteroid Font", 0.f, 0, 1.f, 16, Jupiteroid); + //J2D::DrawString(Colors::White, "Position: " + std::to_string(camera->position.x) + " " + std::to_string(camera->position.y) + " " + std::to_string(camera->position.z), 0, 16, 1,16, Jupiteroid); + //J2D::DrawString(Colors::White, "ViewAngle: " + std::to_string(camera->angle.x) + " " + std::to_string(camera->angle.y) + " " + std::to_string(camera->angle.z), 0, 33, 1,16, Jupiteroid); + //J2D::DrawString(Colors::White, "Framerate: " + std::to_string((int) fps), 0, 48, 1, 16, Jupiteroid); std::array polygon = {Vector2(200, 400), {220, 420}, {220, 430}, {230, 410}, {200, 400}}; J2D::OutlinePolygon(Colors::White, polygon.data(), polygon.size()); J2D::DrawCubicBezierCurve(Colors::Blues::CornflowerBlue, @@ -239,7 +243,7 @@ public: J2D::Begin(nullptr, nullptr, true); J2D::DrawRenderTarget(j2d_render_target, {0, 0}); - J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); + //J2D::DrawSprite(image, image_mask, {0, 0}, 0.25, {0.5, 0.5}, {1,1}); J2D::End(); } diff --git a/src/renderer/OpenGL/TextRendering.cpp b/src/renderer/OpenGL/TextRendering.cpp index 6e4556c..bfdf2d5 100644 --- a/src/renderer/OpenGL/TextRendering.cpp +++ b/src/renderer/OpenGL/TextRendering.cpp @@ -86,7 +86,10 @@ namespace JGL { u1, v0 }; - cachedFont->appendGlyph(new CachedGlyph((char)charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6))); + float ascent = font.face->size->metrics.ascender / 64.0f; + float descent = -font.face->size->metrics.descender / 64.0f; + + cachedFont->appendGlyph(new CachedGlyph((char) charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6), ascent, descent)); xoffset += g->bitmap.width; charcode = FT_Get_Next_Char(font.face, charcode, &gindex); @@ -134,7 +137,7 @@ namespace JGL { continue; x2 = x + glyph->x2offset * scale; - y2 = y - glyph->y2offset * scale; // Adjust y-coordinate + y2 = y - glyph->y2offset * scale; w = glyph->w * scale; h = glyph->h * scale; x += glyph->advanceX * scale; diff --git a/src/types/Font.cpp b/src/types/Font.cpp index 309498e..216e396 100644 --- a/src/types/Font.cpp +++ b/src/types/Font.cpp @@ -108,61 +108,32 @@ namespace JGL { return Font(path); } - Vector2 Font::MeasureString(const std::string &text, unsigned int ptSize) { - Vector2 extents = Vector2(0,0); - bool font_of_size_in_cache = false; + Vector2 Font::MeasureString(const std::string& text, unsigned int ptSize) { + Vector2 extents = Vector2::Zero; - for(const auto& f : fontCache.getFonts()) { - if (f->getFontSize() == ptSize) { - font_of_size_in_cache = true; - break; + for(auto& f : fontCache.getFonts()) { + if (f->getFontIndex() == this->index) { + for (const char &c: text) { + auto glyph = f->getGlyph(c); + extents.x += glyph->advanceX; + extents.y = glyph->ascent + glyph->descent; + } + return extents; } } - if (font_of_size_in_cache) { - CachedFont* font; - - for (auto* f: fontCache.getFonts()) - if (f->getFontSize() == ptSize) - font = f; - - for (const char& c : text) - extents.x += font->getGlyph(c)->advanceX; - - extents.y = ptSize; - return extents; - } - - jlog::Warning("Measuring a font size that is not cached, Defaulting to Jupiteroid."); - FT_Set_Pixel_Sizes(Fonts::Jupiteroid.face, ptSize, ptSize); - + // No cache + FT_Set_Pixel_Sizes(this->face, ptSize, ptSize); for (const char& c : text) { - // TODO: Fix segfault - //FT_GlyphSlot slot = Fonts::Jupiteroid.face->glyph; - //auto glyph_index = FT_Get_Char_Index(Fonts::Jupiteroid.face, c); FT_GlyphSlot slot = face->glyph; auto glyph_index = FT_Get_Char_Index(this->face, c); - - //auto error = FT_Load_Glyph(Fonts::Jupiteroid.face, glyph_index, FT_LOAD_DEFAULT); auto error = FT_Load_Glyph(this->face, glyph_index, FT_LOAD_DEFAULT); if (error) continue; - - Vector2 advance = {static_cast(slot->advance.x >> 6), - static_cast(slot->advance.y >> 6)}; - - - extents += advance; - - // Gives smaller results than we'd want. - if (extents.y < slot->metrics.height / 64) - extents.y = slot->metrics.height / 64; - - // Just fucking hardcode it, we know the glyph height is roughly always the ptSize anyway. - if (extents.y < ptSize) - extents.y = ptSize; + extents.x += static_cast(slot->advance.x >> 6); + extents.y = (face->size->metrics.ascender / 64.0f) + (-face->size->metrics.descender / 64.0f); } return extents; } diff --git a/src/types/FontCache.cpp b/src/types/FontCache.cpp index 4ccbbde..6a42216 100644 --- a/src/types/FontCache.cpp +++ b/src/types/FontCache.cpp @@ -10,7 +10,7 @@ std::array CachedGlyph::getTexCoords() const { return texcoords; } -CachedGlyph::CachedGlyph(char c, std::array texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) { +CachedGlyph::CachedGlyph(char c, std::array texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY, float asc, float desc) { character = c; this->x2offset = x2offset; this->y2offset = y2offset; @@ -19,6 +19,8 @@ CachedGlyph::CachedGlyph(char c, std::array texcoords, float x2offs this->advanceX = advanceX; this->advanceY = advanceY; this->texcoords = texcoords; + this->ascent = asc; + this->descent = desc; } void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {