Files
JGL/src/renderer/OpenGL/J2D.cpp
josh 819539247e
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 26m34s
Merge pull request 'shader_preprocessor' (#43) from shader_preprocessor into shaders_again
Reviewed-on: #43
2025-06-06 13:38:29 -04:00

1527 lines
65 KiB
C++

#include <JGL/JGL.h>
#include <JGL/logger/logger.h>
#include <JGL/types/Instance.h>
#include <J3ML/Algorithm/Bezier.hpp>
#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));
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
if (render_target) {
if (!render_target->GetTexture()->Inverted())
Logger::Warning("You're rendering onto a texture that is upside-down. Your draw commands won't work how you'd expect.");
new_state.current_fbo = render_target->GetGLFramebufferObjectHandle();
new_state.viewport[2] = render_target->GetDimensions().x;
new_state.viewport[3] = render_target->GetDimensions().y;
new_state.clear_color[0] = render_target->GetClearColor().RN();
new_state.clear_color[1] = render_target->GetClearColor().GN();
new_state.clear_color[2] = render_target->GetClearColor().BN();
new_state.clear_color[3] = render_target->GetClearColor().AN();
new_state.current_render_target = render_target;
}
else {
new_state.viewport[2] = window_size.x;
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);
}
glOrtho(0, new_state.viewport[2], new_state.viewport[3], 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
}
void J2D::End() {
if (current_state.current_render_target) {
current_state.current_render_target->MSAABlit();
FilteringMode filtering_mode = current_state.current_render_target->GetTexture()->GetFilteringMode();
if (filtering_mode == FilteringMode::MIPMAP_NEAREST ||
filtering_mode == FilteringMode::MIPMAP_BILINEAR ||
filtering_mode == FilteringMode::MIPMAP_TRILINEAR)
current_state.current_render_target->RegenerateMipMaps();
}
//Change back to the previous projection.
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
auto previous_state = state_stack.PreviousState();
if (!previous_state)
Logger::Fatal("Calling J2D::End before J2D::Begin.");
State::RestoreState(*previous_state);
if (previous_state->current_render_target)
JGL::RenderTarget::SetActiveGLRenderTarget(*current_state.current_render_target);
current_state = *previous_state;
state_stack.Pop();
}
void J2D::DrawPoint(const Color4& color, const Vector2& coordinates, float radius) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
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);
}
void J2D::DrawPoint(const Color4& color, float x, float y, float radius) {
DrawPoint(color, {x, y}, radius);
}
void J2D::DrawLine(const Color4& color, const Vector2& A, const Vector2& B, float thickness) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
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);
}
void J2D::DrawLine(const Color4& color, float x, float y, float w, float h, float thickness) {
J2D::DrawLine(color, {x, y}, {w, h}, thickness);
}
void J2D::DrawLines(const Color4& color, const Vector2* points, const size_t& point_count, float thickness) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
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);
}
void J2D::DrawDottedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing, float thickness) {
float distance = Vector2::Distance(A, B);
Vector2 direction = (B - A).Normalized();
unsigned int point_count = distance / spacing;
std::vector<Vector2> points(point_count);
if (spacing > distance)
Logger::Error("Drawing a dotted line that would have no dots?");
for (unsigned int i = 0; i < point_count; ++i)
points[i] = A + direction * (i * spacing);
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) {
return J2D::DrawDottedLine(color, {x1, y1}, {x2, y2}, spacing, thickness);
}
void J2D::DrawDashedLine(const Color4& color, const Vector2& A, const Vector2& B, float spacing, float dash_length, float thickness) {
float distance = Vector2::Distance(A, B);
Vector2 direction = (B - A).Normalized();
float length_of_dash_and_gap = dash_length + spacing;
unsigned int dash_count = distance / length_of_dash_and_gap;
if (spacing > distance)
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);
J2D::DrawLine(color, A_current, B_current, thickness);
}
// For the little piece at the end.
float distance_left = distance - (dash_count * length_of_dash_and_gap);
if (distance_left > 0) {
A_current = A + direction * (dash_count * length_of_dash_and_gap);
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) {
return J2D::DrawDashedLine(color, {x1, y1}, {x2, y2}, spacing, dash_length, thickness);
}
void J2D::DrawGradientLine(const Color4& color1, const Color4& color2, const Vector2& A, const Vector2& B, float thickness) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {A, B};
GLfloat colors[8] = {color1.RedChannelNormalized(), color1.GreenChannelNormalized(), color1.BlueChannelNormalized(), color1.AlphaChannelNormalized(),
color2.RedChannelNormalized(), color2.GreenChannelNormalized(), color2.BlueChannelNormalized(), color2.AlphaChannelNormalized() };
glEnableClientState(GL_COLOR_ARRAY);
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);
}
void DrawGradientLine(const Color4& color1, const Color4& color2, float x, float y, float w, float h, float thickness) {
J2D::DrawGradientLine(color1, color2, {x, y}, {w, h}, thickness);
}
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.");
if (instance_count <= 0)
return;
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_OutlineRect);
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);
glColor4fv(default_state.draw_color);
}
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.");
if (instance_count <= 0)
return;
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->Handle());
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_QUADS, 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_QUADS, 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);
glColor4fv(default_state.draw_color);
}
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<GLfloat> 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);
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);
}
void J2D::BatchFillRoundedRect(const Color4* colors, const Vector2* positions, const Vector2* sizes, float radius, unsigned int subdivisions, const size_t& count) {
std::vector<Instance2D> rect_instances(count * 2);
std::vector<Instance2D> circle_instances(count * 4);
if (current_state.current_shader)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", RENDERING_ROUTINE_ID::J2D_FillRoundedRect);
for (unsigned int i = 0; i < count; i++) {
unsigned int rect_i = i * 2;
unsigned int circle_i = i * 4;
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());
if (current_state.current_shader)
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) {
DrawSprite(*texture, {positionX, positionY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawSprite(const Texture* texture, const Vector2& position, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
DrawSprite(*texture, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawPartialSprite(const Texture* texture, const Vector2& position, const Vector2& sub_texture_position,
const Vector2& sub_texture_size, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
DrawPartialSprite(*texture, position, sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawPartialSprite(const Texture* texture, float positionX, float positionY, float sub_texture_positionX,
float sub_texture_positionY, unsigned int sub_texture_sizeX,
unsigned int sub_texture_sizeY, float rad_rotation, float originX, float originY,
float scaleX, float scaleY, const Color4& color, Direction inversion) {
DrawPartialSprite(*texture, {positionX, positionY}, {sub_texture_positionX, sub_texture_positionY}, {(float) sub_texture_sizeX, (float) sub_texture_sizeY},
rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawMirrorSprite(const Texture* texture, const Vector2& position, Direction mirror_axis, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color) {
DrawMirrorSprite(*texture, position, mirror_axis, rad_rotation, origin, scale, color);
}
void J2D::DrawSprite(const RenderTarget* render_target, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
DrawSprite(*render_target, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawRenderTarget(const RenderTarget* render_target, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
DrawSprite(*render_target, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawRenderTarget(const RenderTarget& render_target, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
DrawSprite(render_target, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawSprite(const RenderTarget& rt, const Vector2& position, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
//Correct for the render-target being upside-down.
Direction d{};
if (inversion == Direction::None && !rt.GetTexture()->Inverted())
d = Direction::Vertical;
else if (inversion == Direction::Horizontal) {
d = Direction::Horizontal;
if (!rt.GetTexture()->Inverted())
d = Direction::Horizontal | Direction::Vertical;
}
else if (inversion& Direction::Horizontal && inversion& Direction::Vertical) {
d = Direction::Horizontal;
if (!rt.GetTexture()->Inverted())
d = Direction::Horizontal | Direction::Vertical;
}
//Change the blending mode such that the alpha doesn't get multiplied again.
if (rt.OwnsTexture())
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);
}
void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale,const Color4& color, Direction inversion) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (texture.GetDimensions() != alpha_mask.GetDimensions())
Logger::Warning("The alpha mask and the texture are not the same size.");
const Vector2 size = Vector2(texture.GetDimensions());
std::array<Vector2, 4> textureCoordinates = { Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0) };
if (texture.Inverted())
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)};
// 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 = position;
Vector2 scaled_size = scale * size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left vertex
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y}, // Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y
};
if (inversion == Direction::Vertical)
std::swap(textureCoordinates[0], textureCoordinates[1]),
std::swap(textureCoordinates[3], textureCoordinates[2]);
if (inversion == Direction::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[3]),
std::swap(textureCoordinates[1], textureCoordinates[2]);
glColor4ubv(color.ptr());
// Texture 0.
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, texture.GetHandle());
glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data());
glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), textureCoordinates.data());
// Texture 1.
glActiveTexture(GL_TEXTURE1);
glClientActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, alpha_mask.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", 2),
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);
// Reset Texture 1.
glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
// Reset Texture 0.
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glColor4fv(default_state.draw_color);
}
void J2D::DrawSprite(const Texture* texture, const Texture* alpha_mask, const Vector2& position, float rad_rotation,
const Vector2& origin, const Vector2& scale,const Color4& color, Direction inversion) {
DrawSprite(*texture, *alpha_mask, position, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawSprite(const Texture& texture, const Texture& alpha_mask, float positionX, float positionY, float rad_rotation,
float originX, float originY,float scaleX, float scaleY,const Color4& color, Direction inversion) {
DrawSprite(texture, alpha_mask, {positionX, positionY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawSprite(const Texture* texture, const Texture* alpha_mask, float positionX, float positionY, float rad_rotation,
float originX, float originY,float scaleX, float scaleY,const Color4& color, Direction inversion) {
DrawSprite(*texture, *alpha_mask, {positionX, positionY}, rad_rotation, {originX, originY}, {scaleX, scaleY}, color, inversion);
}
void J2D::DrawPartialRenderTarget(const RenderTarget& rt, const Vector2& position, const Vector2& sub_texture_position, const Vector2& sub_texture_size,
float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
//Correct for the render-target being upside-down.
Direction d{};
if (inversion == Direction::None && !rt.GetTexture()->Inverted())
d = Direction::Vertical;
else if (inversion == Direction::Horizontal) {
d = Direction::Horizontal;
if (!rt.GetTexture()->Inverted())
d = Direction::Horizontal | Direction::Vertical;
}
else if (inversion& Direction::Horizontal && inversion& Direction::Vertical) {
d = Direction::Horizontal;
if (!rt.GetTexture()->Inverted())
d = Direction::Horizontal | Direction::Vertical;
}
//Change the blending mode such that the alpha doesn't get multiplied again.
if (rt.OwnsTexture())
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
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);
}
void J2D::DrawPartialRenderTarget(const RenderTarget* rt, const Vector2& position,const Vector2& sub_texture_position,const Vector2& sub_texture_size, float rad_rotation,
const Vector2& origin, const Vector2& scale, const Color4& color, Direction inversion) {
DrawPartialRenderTarget(*rt, position, sub_texture_position, sub_texture_size, rad_rotation, origin, scale, color, inversion);
}
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
const Vector2 size = Vector2(texture.GetDimensions());
std::array<Vector2, 4> textureCoordinates{};
if (texture.Inverted())
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)};
else
textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};
const Vector2 offset = origin * size;
Vector2 pos2 = pos;
Vector2 scaled_size = scale * size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left vertex
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y}, // Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y
};
if (inversion == Direction::Vertical)
std::swap(textureCoordinates[0], textureCoordinates[1]),
std::swap(textureCoordinates[3], textureCoordinates[2]);
if (inversion == Direction::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[3]),
std::swap(textureCoordinates[1], textureCoordinates[2]);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glColor4ubv(color.ptr());
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("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);
}
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) {
DrawSprite(texture,
{positionX, positionY},
rad_rotation,
{originX, originY},
{scaleX, scaleY},
color, inversion);
}
void J2D::DrawPartialSprite(const Texture& texture, const Vector2& position, const Vector2& sub_texture_position,
const Vector2& sub_texture_size, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
const Vector2 textureSize = Vector2(texture.GetDimensions());
std::array<GLfloat, 8> textureCoordinates{};
// Calculate texture coordinates (relative to the whole texture)
if (!texture.Inverted())
textureCoordinates = {
sub_texture_position.x / textureSize.x,
sub_texture_position.y / textureSize.y,
sub_texture_position.x / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
(sub_texture_position.y + sub_texture_size.y) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
sub_texture_position.y / textureSize.y
};
else {
textureCoordinates = {
sub_texture_position.x / textureSize.x,
(textureSize.y - sub_texture_position.y) / textureSize.y,
sub_texture_position.x / textureSize.x,
(textureSize.y - (sub_texture_position.y + sub_texture_size.y)) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
(textureSize.y - (sub_texture_position.y + sub_texture_size.y)) / textureSize.y,
(sub_texture_position.x + sub_texture_size.x) / textureSize.x,
(textureSize.y - sub_texture_position.y) / textureSize.y
};
}
if (inversion& Direction::Vertical)
std::swap(textureCoordinates[1], textureCoordinates[3]),
std::swap(textureCoordinates[5], textureCoordinates[7]);
if (inversion& Direction::Horizontal)
std::swap(textureCoordinates[0], textureCoordinates[6]),
std::swap(textureCoordinates[2], textureCoordinates[4]);
const Vector2 offset = origin * sub_texture_size;
Vector2 pos2 = position;
Vector2 scaled_size = scale * sub_texture_size;
Vector2 size2 = scaled_size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2, // Top-left
{pos2.x, pos2.y + size2.y}, // Bottom-left
{pos2.x + size2.x, pos2.y + size2.y},// Bottom-right
{pos2.x + size2.x, pos2.y} // Top-right
};
//Rotate the vertices about the origin by float rad_rotation.
if (rad_rotation != 0)
for (auto& v: vertices)
v = {(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta +
pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta +
pos2.y + offset.y * scale.y};
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glColor4ubv(color.ptr());
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("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);
glColor4fv(default_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
void J2D::DrawPartialSprite(const Texture& texture, float positionX, float positionY, float sub_texture_positionX,
float sub_texture_positionY, unsigned int sub_texture_sizeX,
unsigned int sub_texture_sizeY, float originX, float originY, float rad_rotation,
float scaleX, float scaleY, const Color4& color, Direction inversion) {
J2D::DrawPartialSprite(texture, {positionX, positionY}, {sub_texture_positionX, sub_texture_positionY},
{(float) sub_texture_sizeX, (float) sub_texture_sizeY}, rad_rotation, {originX, originY},
{scaleX, scaleY}, color, inversion);
}
void J2D::DrawMirrorSprite(const Texture& texture, const Vector2& position, Direction mirror_axis, float rad_rotation, const Vector2& origin, const Vector2& scale, const Color4& color) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (mirror_axis == Direction::None)
Logger::Warning("Drawing non-mirrored sprite with J2D::DrawMirrorSprite?");
glBindTexture(GL_TEXTURE_2D, texture.GetHandle());
Vector2 size = Vector2(texture.GetDimensions());
std::array<Vector2, 4> textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0)};
if (mirror_axis == Direction::Horizontal) {
size.x *= 2;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
if (texture.Inverted())
textureCoordinates = {Vector2(0, 1), Vector2(0, 0), Vector2(2, 0), Vector2(2, 1)};
else
textureCoordinates = {Vector2(0, 0), Vector2(0, 1), Vector2(2, 1), Vector2(2, 0)};
}
else if (mirror_axis == Direction::Vertical) {
size.y *= 2;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
if (texture.Inverted())
textureCoordinates = {Vector2(0, 2), Vector2(0, 0), Vector2(1, 0), Vector2(1, 2)};
else
textureCoordinates = {Vector2(0, 0), Vector2(0, 2), Vector2(1, 2), Vector2(1, 0)};
}
else if ((mirror_axis& Direction::Horizontal) && (mirror_axis& Direction::Vertical)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
size *= 2;
if (texture.Inverted())
textureCoordinates = {Vector2(0, 2), Vector2(0, 0), Vector2(2, 0), Vector2(2, 2)};
else
textureCoordinates = {Vector2(0, 0), Vector2(0, 2), Vector2(2, 2), Vector2(2, 0)};
}
const Vector2 offset = origin * size;
Vector2 pos2 = position;
Vector2 size2 = scale * size;
float cos_theta = std::cos(rad_rotation);
float sin_theta = std::sin(rad_rotation);
std::array<Vector2, 4> vertices =
{
pos2,
{pos2.x, pos2.y + size2.y},
{pos2.x + size2.x, pos2.y + size2.y},
{pos2.x + size2.x, pos2.y}
};
if (rad_rotation != 0)
for (auto& v : vertices)
v = {
(v.x - pos2.x - offset.x * scale.x) * cos_theta - (v.y - pos2.y - offset.y * scale.y) * sin_theta + pos2.x + offset.x * scale.x,
(v.x - pos2.x - offset.x * scale.x) * sin_theta + (v.y - pos2.y - offset.y * scale.y) * cos_theta + pos2.y + offset.y * scale.y
};
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
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("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.
if (texture.GetWrappingMode() == WrappingMode::CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
else if (texture.GetWrappingMode() == WrappingMode::REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else if (texture.GetWrappingMode() == WrappingMode::CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER),
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glBindTexture(GL_TEXTURE_2D, 0);
glColor4fv(default_state.draw_color);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
void J2D::OutlineCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions, float thickness) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
GLfloat angle, x, y;
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step)
{
x = radius * std::sin(angle) + center.x;
y = radius * std::cos(angle) + center.y;
if (i < subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
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);
}
void J2D::FillCircle(const Color4& color, const Vector2& center, float radius, unsigned int subdivisions) {
Instance2D circle(color, center, Vector2(1, 1), Vector2(radius, radius));
BatchFillCircle(&circle, subdivisions, 1);
}
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 (instance_count <= 0)
return;
GLfloat angle, x, y;
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
/* Most of the time the vector "vertices" is either the same size or size + 1 of the number of subdivisions.
* Because a float is a decimal, It'd take too long to get rid of the emplace-back making us
* wait around for the container to resize. This gets rid of it for what we can guarantee. */
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = std::sin(angle);
y = std::cos(angle);
if (i < subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
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);
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();
}
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)
current_state.current_shader->SetInt("JGL_RENDERING_ROUTINE", 0);
glColor4fv(default_state.draw_color);
}
void J2D::OutlineTriangle(const Color4& color, const Triangle2D& tri, float thickness) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
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);
}
void J2D::FillTriangle(const Color4& color, const Triangle2D& tri) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
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);
}
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Triangle2D& tri) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
Vector2 vertices[] = {{tri.A.x, tri.A.y}, {tri.B.x, tri.B.y}, {tri.C.x, tri.C.y}};
GLfloat colors[] = {a_color.RedChannelNormalized(), a_color.GreenChannelNormalized(), a_color.BlueChannelNormalized(), a_color.AlphaChannelNormalized(),
b_color.RedChannelNormalized(),b_color.GreenChannelNormalized(), b_color.BlueChannelNormalized(), b_color.AlphaChannelNormalized(),
c_color.RedChannelNormalized(), c_color.GreenChannelNormalized(), c_color.BlueChannelNormalized(),c_color.AlphaChannelNormalized()};
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);
}
void J2D::DrawCubicBezierCurve(const Color4& color, const Vector2& controlA, const Vector2& pointA, const Vector2& pointB, const Vector2& controlB,
int subdivisions, float thickness) {
Vector2 last = controlA;
const Vector2& first = controlB;
std::vector<Vector2> vertices(2 * subdivisions + 2);
for (int i = 0; i < subdivisions; ++i) {
float alpha = (float) i / (float) subdivisions;
Vector2 step = J3ML::Algorithm::Bezier(alpha, controlA, pointA, pointB, controlB);
vertices[2 * i] = last;
vertices[2 * i + 1] = step;
last = step;
}
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) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (points[0] != points[points_size -1])
throw std::runtime_error("J2D::OutlinePolygon: The first point and the last point must connect.");
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);
}
void J2D::DrawGradientLine(const Color4& color_a, const Color4& color_b, float x, float y, float w, float h,
float thickness) {
DrawGradientLine(color_a, color_b, {x, y}, {w, h}, thickness);
}
void J2D::DrawArc(const Color4& color, const Vector2& center, float radius, float arc_begin, float arc_end, unsigned int subdivisions, float thickness)
{
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
if (arc_begin == arc_end)
Logger::Error("WTF are you even drawing???");
if ( J3ML::Math::Mod(J3ML::Math::Abs(arc_begin-arc_end), J3ML::Math::Pi*2.f) == 0)
Logger::Error("Use OutlineCircle instead!!");
float step = Math::Abs(arc_end-arc_begin) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
GLfloat angle, x, y;
int i = 0;
for (angle = arc_begin; angle <= arc_end; angle += step) {
x = center.x + (radius * std::cos(angle)); // TODO: Why is it fine here, but must be x,y flipped for sin,cos in Circle code?
y = center.y + (radius * std::sin(angle));
if (i < subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
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);
}
void J2D::OutlineRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius, float thickness)
{
// A rounded rectangle of size 2a x 2b with rounding radius r is given by
// f(x; a, r) + f(y; b, r) = 1
// where
// f(x; a, r) = {
// if |x| >= a - r: (|x| - (a-r) / r )^2
// otherwise: 0
// }
//std::vector<Vector2> vertices;
// TODO: Calculate vertices for top-left quarter-circle.
Vector2 tr = pos + Vector2(size.x, 0);
Vector2 br = pos + size;
Vector2 bl = pos + Vector2(0, size.y);
// Center-point around which to calculate each 'quarter-circle'.
Vector2 anchor_tl = pos + Vector2(radius, radius);
Vector2 anchor_tr = pos + Vector2(size.x-radius, radius);
Vector2 anchor_br = pos + size - Vector2(radius, radius);
Vector2 anchor_bl = pos + Vector2(radius, size.y - radius);
//J2D::Begin();
//J2D::DrawPoints(Colors::Red, {tl, tr, br, bl}, 2);
//J2D::DrawPoints(Colors::Blue, {anchor_tl, anchor_tr, anchor_br, anchor_bl}, 2);
//J2D::End();
Vector2 anchor_topleft_top = pos + Vector2(radius, 0);
Vector2 anchor_topright_top = pos + Vector2(size.x - radius, 0);
// TODO: Calculate vertices for top-right quarter-circle.
Vector2 anchor_topright_right = pos + Vector2(size.x, radius);
Vector2 anchor_bottomright_right = pos + Vector2(size.x, size.y - radius);
// TODO: Calculate vertices for bottom-right quarter-circle.
Vector2 anchor_bottomright_bottom = pos + Vector2(size.x - radius, size.y);
Vector2 anchor_bottomleft_bottom = pos + Vector2(radius, size.y);
// TODO: Calculate vertices for bottom-left quarter-circle.
Vector2 anchor_bottomleft_left = pos + Vector2(0, size.y - radius);
Vector2 anchor_topleft_left = pos + Vector2(0, radius);
//J2D::Begin();
// The 3.01f, etc is a tiny-bit of overshoot to compensate for the fact that
// this is not being plotted as a continuous line-loop.
// Once i'm done working this out I expect bill will want to make it such.
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);
J2D::DrawLine(color, anchor_topright_right, anchor_bottomright_right, thickness);
J2D::DrawArc(color, anchor_br, radius, 0.0f, 1.01f*Math::Pi/2, subdivisions, thickness);
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) {
Vector2 anchor_topleft_top = pos + Vector2(radius, 0);
Vector2 anchor_topright_top = pos + Vector2(size.x - radius, 0);
Vector2 anchor_topright_right = pos + Vector2(size.x, radius);
Vector2 anchor_bottomright_right = pos + Vector2(size.x, size.y - radius);
Vector2 anchor_bottomright_bottom = pos + Vector2(size.x - radius, size.y);
Vector2 anchor_bottomleft_bottom = pos + Vector2(radius, size.y);
Vector2 anchor_bottomleft_left = pos + Vector2(0, size.y - radius);
Vector2 anchor_topleft_left = pos + Vector2(0, radius);
Vector2 vertices[] = {anchor_topleft_top, anchor_topright_top, anchor_topright_right, anchor_bottomright_right, anchor_bottomright_bottom, anchor_bottomleft_bottom, anchor_bottomleft_left, anchor_topleft_left};
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);
}
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->Handle());
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Color4), nullptr);
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle());
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);
}
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());
glBindBuffer(GL_ARRAY_BUFFER, ShapeCache::draw_points_positions->Handle());
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);
}
void J2D::FillTriangle(const Color4& color, const Vector2& triA, const Vector2& triB, const Vector2& triC) {
FillTriangle(color, {triA, triB, triC});
}
void J2D::FillGradientTriangle(const Color4& a_color, const Color4& b_color, const Color4& c_color, const Vector2& tri_a,
const Vector2& tri_b, const Vector2& tri_c) {
FillGradientTriangle(a_color, b_color, c_color, {tri_a, tri_b, tri_c});
}
void J2D::OutlineEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, float thickness,
int subdivisions) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
GLfloat angle, x, y;
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = radius_x * std::sin(angle) + position.x;
y = radius_y * std::cos(angle) + position.y;
if (i < subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
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);
}
void J2D::FillEllipse(const Color4& color, const Vector2& position, float radius_x, float radius_y, int subdivisions) {
if (!state_stack.Size())
Logger::Error("Drawing J2D element before J2D begin.");
GLfloat angle, x, y;
float step = (2.f * Math::Pi) / (float) subdivisions;
std::vector<Vector2> vertices(subdivisions);
/* Most of the time the vector "vertices" is either the same size or size + 1 of the number of subdivisions.
* Because a float is a decimal, It'd take too long to get rid of the emplace-back making us
* wait around for the container to resize. This gets rid of it for what we can guarantee. */
int i = 0;
for (angle = 0.0f; angle < (2.f * Math::Pi); angle += step) {
x = radius_x * std::sin(angle) + position.x;
y = radius_y * std::cos(angle) + position.y;
if (i < subdivisions)
vertices[i] = {x, y};
else
vertices.emplace_back(x, y);
i++;
}
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);
}
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::DrawPartialSprite(texture_atlas, position, Vector2(atlas_region.position), Vector2(atlas_region.size), rad_rotation, origin, scale, color);
}
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);
}