townengine/src/rendering/twn_circles.c

128 lines
3.8 KiB
C

#include "twn_engine_context_c.h"
#include "twn_draw_c.h"
#include "twn_draw.h"
#include "twn_util_c.h"
#include <SDL2/SDL.h>
#include <stb_ds.h>
void draw_circle(Vec2 position, float radius, Color color) {
CirclePrimitive circle = {
.radius = radius,
.color = color,
.position = position,
};
Primitive2D primitive = {
.type = PRIMITIVE_2D_CIRCLE,
.circle = circle,
};
arrput(ctx.render_queue_2d, primitive);
}
void create_circle_geometry(Vec2 position,
float radius,
size_t num_vertices,
Vec2 vertices[])
{
SDL_assert(num_vertices <= CIRCLE_VERTICES_MAX);
/* the angle (in radians) to rotate by on each iteration */
float seg_rotation_angle = (360.0f / (float)(num_vertices - 2)) * ((float)M_PI / 180);
vertices[0].x = (float)position.x;
vertices[0].y = (float)position.y;
/* this point will rotate around the center */
float start_x = 0.0f - radius;
float start_y = 0.0f;
for (size_t i = 1; i < num_vertices - 1; ++i) {
float final_seg_rotation_angle = (float)i * seg_rotation_angle;
float c, s;
sincosf(final_seg_rotation_angle, &s, &c);
vertices[i].x = c * start_x - s * start_y;
vertices[i].y = c * start_y + s * start_x;
vertices[i].x += position.x;
vertices[i].y += position.y;
}
// place a redundant vertex to make proper circling over shared element buffer
{
float final_seg_rotation_angle = (float)1 * seg_rotation_angle;
float c, s;
sincosf(final_seg_rotation_angle, &s, &c);
vertices[num_vertices - 1].x = c * start_x - s * start_y;
vertices[num_vertices - 1].y = c * start_y + s * start_x;
vertices[num_vertices - 1].x += position.x;
vertices[num_vertices - 1].y += position.y;
}
}
void render_circle(const CirclePrimitive *circle) {
static Vec2 vertices[CIRCLE_VERTICES_MAX];
static int prev_num_vertices = 0;
static Vec2 prev_position = {0};
int const num_vertices = MIN((int)circle->radius, CIRCLE_VERTICES_MAX);
if (prev_num_vertices != num_vertices) {
create_circle_geometry(circle->position,
circle->radius,
num_vertices,
vertices);
prev_num_vertices = num_vertices;
prev_position = circle->position;
} else {
/* reuse the data, but offset it by difference with previously generated position */
/* no evil cos sin ops this way, if radius is shared in sequential calls */
Vec2 const d = { prev_position.x - circle->position.x, prev_position.y - circle->position.y };
for (int i = 0; i < num_vertices; ++i)
vertices[i] = (Vec2){ vertices[i].x - d.x, vertices[i].y - d.y };
prev_position = circle->position;
}
VertexBuffer buffer = get_scratch_vertex_array();
specify_vertex_buffer(buffer, vertices, sizeof (Vec2) * num_vertices);
DeferredCommandDraw command = {0};
command.vertices = (AttributeArrayPointer) {
.arity = 2,
.type = TWN_FLOAT,
.stride = sizeof (Vec2),
.offset = 0,
.buffer = buffer
};
command.constant_colored = true;
command.color = circle->color;
command.element_buffer = get_circle_element_buffer();
command.element_count = (num_vertices - 2) * 3;
command.range_end = (num_vertices - 2) * 3;
command.texture_mode = circle->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY;
command.pipeline = PIPELINE_2D;
command.depth_range_high = depth_range_high;
command.depth_range_low = depth_range_low;
DeferredCommand final_command = {
.type = DEFERRED_COMMAND_TYPE_DRAW,
.draw = command
};
arrpush(deferred_commands, final_command);
}