BatchFillCircle Instanced Rendering.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m51s

Added JGL::ClearScreen as-well to clear fbo 0 at any time.
This commit is contained in:
2025-05-17 17:04:27 -04:00
parent 6f99689add
commit a5424eb370
4 changed files with 110 additions and 25 deletions

View File

@@ -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();

View File

@@ -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.

View File

@@ -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();

View File

@@ -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<Color4, 4> colors = { color, color, color, color };
std::array<float, 4> circle_radii = { radius, radius, radius, radius };
std::array<Vector2, 4> 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<Instance2D, 2> 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<Instance2D, 4> 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<Instance2D, 2> 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, &center, &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)