diff --git a/CMakeLists.txt b/CMakeLists.txt index d3702b8..ecceff2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ set(TWN_NONOPT_SOURCE_FILES src/rendering/twn_circles.c src/rendering/twn_skybox.c src/rendering/twn_models.c + src/rendering/twn_lines.c ) set(TWN_SOURCE_FILES diff --git a/apps/examples/circle-raster/game.c b/apps/examples/circle-raster/game.c index f33868a..6e75e91 100644 --- a/apps/examples/circle-raster/game.c +++ b/apps/examples/circle-raster/game.c @@ -121,6 +121,8 @@ void game_tick(void) { if (input_action_pressed("down") && state->r > 2) state->r -= 1; + draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125}); + int32_t const rsi = (int32_t)state->r * (int32_t)state->r; int32_t acc = 1; for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) { @@ -133,8 +135,6 @@ void game_tick(void) { } } - draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125}); - /* uncomment to see performance difference between variants */ // benchmark(state); } diff --git a/src/rendering/twn_deferred_commands.h b/src/rendering/twn_deferred_commands.h index 5e8429e..897980e 100644 --- a/src/rendering/twn_deferred_commands.h +++ b/src/rendering/twn_deferred_commands.h @@ -4,6 +4,7 @@ #include "twn_types.h" #include "twn_gpu_texture_c.h" #include "twn_textures_c.h" +#include "twn_types_c.h" #include #include @@ -49,6 +50,11 @@ typedef struct { uint32_t element_count; uint32_t range_start, range_end; + enum { + DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_TRIANGLES = 0, + DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_LINES = 1, + } geometry_mode; + bool constant_colored; bool textured, texture_repeat, uses_gpu_key; } DeferredCommandDraw; diff --git a/src/rendering/twn_draw.c b/src/rendering/twn_draw.c index c13d738..cf7af86 100644 --- a/src/rendering/twn_draw.c +++ b/src/rendering/twn_draw.c @@ -278,7 +278,7 @@ static void render_2d(void) { } /* TODO: batching */ - case PRIMITIVE_2D_LINE: { + case PRIMITIVE_2D_LINES: { struct Render2DInvocation const invocation = { .primitive = current, .layer = layer, @@ -327,8 +327,8 @@ static void render_2d(void) { case PRIMITIVE_2D_CIRCLE: render_circle(&invocation.primitive->circle); break; - case PRIMITIVE_2D_LINE: - render_line(&invocation.primitive->line); + case PRIMITIVE_2D_LINES: + render_lines(&invocation.primitive->line); break; case PRIMITIVE_2D_TEXT: default: @@ -359,8 +359,8 @@ static void render_2d(void) { case PRIMITIVE_2D_TEXT: render_text(&invocation.primitive->text); break; - case PRIMITIVE_2D_LINE: - render_line(&invocation.primitive->line); + case PRIMITIVE_2D_LINES: + render_lines(&invocation.primitive->line); break; default: SDL_assert(false); @@ -538,31 +538,6 @@ void issue_deferred_draw_commands(void) { } -/* TODO: Support thickness */ -void draw_line(Vec2 start, - Vec2 finish, - float thickness, - Color color) -{ - if (fabsf(1.0f - thickness) >= 0.00001f) - log_warn("Thickness isn't yet implemented for line drawing (got %f)", (double)thickness); - - LinePrimitive line = { - .start = start, - .finish = finish, - .thickness = thickness, - .color = color, - }; - - Primitive2D primitive = { - .type = PRIMITIVE_2D_LINE, - .line = line, - }; - - arrput(ctx.render_queue_2d, primitive); -} - - void draw_box(Rect rect, float thickness, Color color) diff --git a/src/rendering/twn_draw_c.h b/src/rendering/twn_draw_c.h index 15c4220..9ffc99f 100644 --- a/src/rendering/twn_draw_c.h +++ b/src/rendering/twn_draw_c.h @@ -69,9 +69,12 @@ typedef struct SpritePrimitive { bool repeat; } SpritePrimitive; +/* batched in place */ typedef struct LinePrimitive { - Vec2 start; - Vec2 finish; + struct LineVertex { + Vec3 position; + Color color; + } *vertices; float thickness; Color color; } LinePrimitive; @@ -97,7 +100,7 @@ typedef struct TextPrimitive { typedef enum Primitive2DType { PRIMITIVE_2D_SPRITE, - PRIMITIVE_2D_LINE, + PRIMITIVE_2D_LINES, PRIMITIVE_2D_RECT, PRIMITIVE_2D_CIRCLE, PRIMITIVE_2D_TEXT, @@ -319,7 +322,7 @@ IndexBuffer get_circle_element_buffer(void); void render_circle(const CirclePrimitive *circle); -void render_line(const LinePrimitive *line); +void render_lines(LinePrimitive *line); void render_rectangle(const RectPrimitive *rectangle); diff --git a/src/rendering/twn_gl_15_rendering.c b/src/rendering/twn_gl_15_rendering.c index 0b38856..6fe7dec 100644 --- a/src/rendering/twn_gl_15_rendering.c +++ b/src/rendering/twn_gl_15_rendering.c @@ -630,12 +630,24 @@ void finally_draw_command(DeferredCommandDraw command) { textures_bind(&ctx.texture_cache, command.texture_key); } + GLenum geometry_mode; + switch (command.geometry_mode) { + case DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_TRIANGLES: + geometry_mode = GL_TRIANGLES; + break; + case DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_LINES: + geometry_mode = GL_LINES; + break; + default: + SDL_assert(false); + } + if (command.element_buffer) { SDL_assert(command.element_count != 0); if (command.range_start == command.range_end) - glDrawElements(GL_TRIANGLES, command.element_count, GL_UNSIGNED_SHORT, NULL); + glDrawElements(geometry_mode, command.element_count, GL_UNSIGNED_SHORT, NULL); else - glDrawRangeElements(GL_TRIANGLES, + glDrawRangeElements(geometry_mode, command.range_start, command.range_end, command.element_count, @@ -643,7 +655,7 @@ void finally_draw_command(DeferredCommandDraw command) { NULL); } else { SDL_assert(command.primitive_count != 0); - glDrawArrays(GL_TRIANGLES, 0, command.primitive_count); + glDrawArrays(geometry_mode, 0, command.primitive_count); } /* state clearing */ @@ -662,14 +674,3 @@ void finally_draw_command(DeferredCommandDraw command) { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - - -void render_line(const LinePrimitive *line) { - finally_use_2d_pipeline(); - glBegin(GL_LINES); - glColor4ub(line->color.r, line->color.g, line->color.b, line->color.a); - glVertex2f(line->start.x, line->start.y); - glColor4ub(line->color.r, line->color.g, line->color.b, line->color.a); - glVertex2f(line->finish.x, line->finish.y); - glEnd(); -} diff --git a/src/rendering/twn_lines.c b/src/rendering/twn_lines.c new file mode 100644 index 0000000..c5e282b --- /dev/null +++ b/src/rendering/twn_lines.c @@ -0,0 +1,87 @@ +#include "twn_draw_c.h" +#include "twn_draw.h" +#include "twn_engine_context_c.h" +#include "twn_util.h" + +#include + +/* TODO: Support thickness */ +void draw_line(Vec2 start, + Vec2 finish, + float thickness, + Color color) +{ + if (fabsf(1.0f - thickness) >= 0.00001f) + log_warn("Thickness isn't yet implemented for line drawing (got %f)", (double)thickness); + + struct LineVertex const v0 = { .position = (Vec3){start.x, start.y, 0}, .color = color }; + struct LineVertex const v1 = { .position = (Vec3){finish.x, finish.y, 0}, .color = color }; + + /* combine with existing position if it's compatible */ + if (arrlenu(ctx.render_queue_2d) != 0) { + Primitive2D *const primitive = &ctx.render_queue_2d[arrlenu(ctx.render_queue_2d) - 1]; + if (primitive->type == PRIMITIVE_2D_LINES && fabsf(primitive->line.thickness - thickness) < 0.00001f && + primitive->line.color.a == color.a && primitive->line.color.b == color.b && + primitive->line.color.r == color.r && primitive->line.color.g == color.g) { + + arrput(primitive->line.vertices, v0); + arrput(primitive->line.vertices, v1); + return; + } + } + + LinePrimitive line = { + .thickness = thickness, + .color = color, + }; + + Primitive2D primitive = { + .type = PRIMITIVE_2D_LINES, + .line = line, + }; + + arrput(primitive.line.vertices, v0); + arrput(primitive.line.vertices, v1); + arrput(ctx.render_queue_2d, primitive); +} + + +void render_lines(LinePrimitive *line) { + DeferredCommandDraw command = {0}; + + VertexBuffer buffer = get_scratch_vertex_array(); + specify_vertex_buffer(buffer, line->vertices, arrlenu(line->vertices) * sizeof (*line->vertices)); + + command.vertices = (AttributeArrayPointer) { + .arity = 3, + .type = TWN_FLOAT, + .stride = sizeof (struct LineVertex), + .offset = offsetof (struct LineVertex, position), + .buffer = buffer + }; + + command.colors = (AttributeArrayPointer) { + .arity = 4, + .type = TWN_UNSIGNED_BYTE, + .stride = sizeof (struct LineVertex), + .offset = offsetof (struct LineVertex, color), + .buffer = buffer + }; + + command.primitive_count = arrlenu(line->vertices); + + command.geometry_mode = DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_LINES; + 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 + }; + + /* TODO: should it be deleted here? */ + arrfree(line->vertices); + arrpush(deferred_commands, final_command); +} diff --git a/src/twn_amalgam.c b/src/twn_amalgam.c index 6cfa3a5..dee1e81 100644 --- a/src/twn_amalgam.c +++ b/src/twn_amalgam.c @@ -23,3 +23,4 @@ #include "rendering/twn_triangles.c" #include "rendering/twn_billboards.c" #include "rendering/twn_models.c" +#include "rendering/twn_lines.c"