#include "twn_engine_context_c.h" #include "twn_draw_c.h" #include "twn_draw.h" #include "twn_util_c.h" #include #include 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); }