128 lines
3.8 KiB
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);
|
|
}
|