diff --git a/src/rendering/twn_circles.c b/src/rendering/twn_circles.c index 16eb8fe..8cab9b2 100644 --- a/src/rendering/twn_circles.c +++ b/src/rendering/twn_circles.c @@ -1,4 +1,3 @@ -#include "twn_util.h" #include "twn_engine_context_c.h" #include "twn_draw_c.h" #include "twn_draw.h" @@ -24,22 +23,15 @@ void draw_circle(Vec2 position, float radius, Color color) { void create_circle_geometry(Vec2 position, - Color color, float radius, size_t num_vertices, - SDL_Vertex vertices[], - int indices[]) + Vec2 vertices[]) { /* the angle (in radians) to rotate by on each iteration */ float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); - vertices[0].position.x = (float)position.x; - vertices[0].position.y = (float)position.y; - vertices[0].color.r = color.r; - vertices[0].color.g = color.g; - vertices[0].color.b = color.b; - vertices[0].color.a = color.a; - vertices[0].tex_coord = (SDL_FPoint){ 0, 0 }; + 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; @@ -48,34 +40,13 @@ void create_circle_geometry(Vec2 position, for (size_t i = 1; i < num_vertices + 1; ++i) { float final_seg_rotation_angle = (float)i * seg_rotation_angle; - vertices[i].position.x = - cosf(final_seg_rotation_angle) * start_x - - sinf(final_seg_rotation_angle) * start_y; - vertices[i].position.y = - cosf(final_seg_rotation_angle) * start_y + - sinf(final_seg_rotation_angle) * start_x; + float c, s; + sincosf(final_seg_rotation_angle, &s, &c); - vertices[i].position.x += position.x; - vertices[i].position.y += position.y; + vertices[i].x = c * start_x - s * start_y; + vertices[i].y = c * start_y + s * start_x; - vertices[i].color.r = color.r; - vertices[i].color.g = color.g; - vertices[i].color.b = color.b; - vertices[i].color.a = color.a; - - vertices[i].tex_coord = (SDL_FPoint){ 0, 0 }; - - - size_t triangle_offset = 3 * (i - 1); - - /* center point index */ - indices[triangle_offset] = 0; - /* generated point index */ - indices[triangle_offset + 1] = (int)i; - - size_t index = (i + 1) % num_vertices; - if (index == 0) - index = num_vertices; - indices[triangle_offset + 2] = (int)index; + vertices[i].x += position.x; + vertices[i].y += position.y; } } diff --git a/src/rendering/twn_draw_c.h b/src/rendering/twn_draw_c.h index be2be9e..18db197 100644 --- a/src/rendering/twn_draw_c.h +++ b/src/rendering/twn_draw_c.h @@ -20,7 +20,10 @@ extern Matrix4 camera_projection_matrix; extern Matrix4 camera_look_at_matrix; + #define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) +#define CIRCLE_VERTICES_MAX 2048 +#define CIRCLE_ELEMENT_BUFFER_LENGTH (CIRCLE_VERTICES_MAX * 3) typedef GLuint VertexBuffer; @@ -129,11 +132,9 @@ void render_queue_clear(void); /* fills two existing arrays with the geometry data of a circle */ /* the size of indices must be at least 3 times the number of vertices */ void create_circle_geometry(Vec2 position, - Color color, float radius, size_t num_vertices, - SDL_Vertex vertices[], - int indices[]); + Vec2 vertices[]); struct QuadBatch { size_t size; /* how many primitives are in current batch */ @@ -191,6 +192,8 @@ void set_depth_range(double low, double high); VertexBuffer get_quad_element_buffer(void); +VertexBuffer get_circle_element_buffer(void); + void render_circle(const CirclePrimitive *circle); 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 c800734..e83a586 100644 --- a/src/rendering/twn_gl_15_rendering.c +++ b/src/rendering/twn_gl_15_rendering.c @@ -96,13 +96,16 @@ typedef struct { Color color; }; - bool textured, texture_repeat; - TextureKey texture; + bool textured, texture_repeat, uses_gpu_key; + TextureKey texture_key; + GPUTexture gpu_texture; GLuint element_buffer; GLsizei element_count; GLsizei range_start, range_end; -} DeferredCommandDrawElements; + + double depth_range_low, depth_range_high; +} DeferredCommandDraw; typedef struct { @@ -130,19 +133,26 @@ typedef struct { } DeferredCommandUseTextureMode; +typedef struct { + double low, high; +} DeferredCommandDepthRange; + + typedef struct { enum DeferredCommandType { - DEFERRED_COMMAND_TYPE_DRAW_ELEMENTS, + DEFERRED_COMMAND_TYPE_DRAW, DEFERRED_COMMAND_TYPE_CLEAR, DEFERRED_COMMAND_TYPE_USE_PIPIELINE, DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE, + DEFERRED_COMMAND_TYPE_DEPTH_RANGE, } type; union { - DeferredCommandDrawElements draw_elements; + DeferredCommandDraw draw; DeferredCommandClear clear; DeferredCommandUsePipeline use_pipeline; DeferredCommandUseTextureMode use_texture_mode; + DeferredCommandDepthRange depth_range; }; } DeferredCommand; @@ -151,9 +161,6 @@ static TextureMode texture_mode_last_used = -1; static Pipeline pipeline_last_used = PIPELINE_NO; -#define CIRCLE_VERTICES_MAX 2048 - - /* potentially double buffered array of vertex array handles */ /* we assume they will be refilled fully each frame */ static size_t scratch_va_front_used, scratch_va_back_used; @@ -195,6 +202,10 @@ static DeferredCommand *deferred_commands; static void issue_deferred_draw_commands(void) { for (size_t i = 0; i < arrlenu(deferred_commands); ++i) { switch (deferred_commands[i].type) { + case DEFERRED_COMMAND_TYPE_DEPTH_RANGE: { + glDepthRange(deferred_commands[i].depth_range.low, deferred_commands[i].depth_range.high); + break; + } case DEFERRED_COMMAND_TYPE_CLEAR: { glClearColor((1.0f / 255) * deferred_commands[i].clear.color.r, (1.0f / 255) * deferred_commands[i].clear.color.g, @@ -211,8 +222,8 @@ static void issue_deferred_draw_commands(void) { break; } - case DEFERRED_COMMAND_TYPE_DRAW_ELEMENTS: { - DeferredCommandDrawElements const command = deferred_commands[i].draw_elements; + case DEFERRED_COMMAND_TYPE_DRAW: { + DeferredCommandDraw const command = deferred_commands[i].draw; /* TODO: don't assume a single vertex array ? */ SDL_assert(command.vertices.arity != 0); @@ -255,10 +266,12 @@ static void issue_deferred_draw_commands(void) { command.color.a); if (command.textured) { - if (command.texture_repeat) - textures_bind_repeating(&ctx.texture_cache, command.texture); + if (command.uses_gpu_key) + glBindTexture(GL_TEXTURE_2D, command.gpu_texture); + else if (command.texture_repeat) + textures_bind_repeating(&ctx.texture_cache, command.texture_key); else - textures_bind(&ctx.texture_cache, command.texture); + textures_bind(&ctx.texture_cache, command.texture_key); } if (command.range_start == command.range_end) @@ -459,40 +472,6 @@ static void finally_use_2d_pipeline(void) { } -void render_circle(const CirclePrimitive *circle) { - static SDL_Vertex vertices[CIRCLE_VERTICES_MAX]; - static int indices[CIRCLE_VERTICES_MAX * 3]; - int num_vertices = MIN((int)circle->radius, CIRCLE_VERTICES_MAX-1); - - create_circle_geometry(circle->position, - circle->color, - circle->radius, - num_vertices, - vertices, - indices); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, - GL_FLOAT, - sizeof (SDL_Vertex), - &vertices->position); - - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, - GL_UNSIGNED_BYTE, - sizeof (SDL_Vertex), - &vertices->color); - - glDrawElements(GL_TRIANGLES, - num_vertices * 3, - GL_UNSIGNED_INT, - indices); - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); -} - - void use_texture_mode(TextureMode mode) { DeferredCommand const command = { .type = DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE, @@ -589,7 +568,7 @@ void finally_render_quads(const Primitive2D primitives[], const struct QuadBatch batch, const VertexBuffer buffer) { - DeferredCommandDrawElements command = {0}; + DeferredCommandDraw command = {0}; GLsizei off = 0, voff = 0, uvoff = 0, coff = 0; @@ -644,7 +623,7 @@ void finally_render_quads(const Primitive2D primitives[], if (batch.textured) { /* TODO: bad, don't */ command.textured = true; - command.texture = primitives->sprite.texture_key; + command.texture_key = primitives->sprite.texture_key; command.texture_repeat = batch.repeat; } @@ -655,8 +634,8 @@ void finally_render_quads(const Primitive2D primitives[], use_texture_mode(batch.mode); DeferredCommand final_command = { - .type = DEFERRED_COMMAND_TYPE_DRAW_ELEMENTS, - .draw_elements = command + .type = DEFERRED_COMMAND_TYPE_DRAW, + .draw = command }; arrpush(deferred_commands, final_command); @@ -817,44 +796,46 @@ void finally_draw_text(FontData const *font_data, Color color, VertexBuffer buffer) { - (void)buffer; + DeferredCommandDraw command = {0}; - /* vertex specification */ - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, - GL_FLOAT, - offsetof(ElementIndexedQuadWithoutColor, v1), - (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, v0)); + command.vertices = (AttributeArrayPointer) { + .arity = 2, + .type = GL_FLOAT, + .stride = offsetof(ElementIndexedQuadWithoutColor, v1), + .offset = offsetof(ElementIndexedQuadWithoutColor, v0), + .buffer = buffer + }; - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glClientActiveTexture(GL_TEXTURE0); - glTexCoordPointer(2, - GL_FLOAT, - offsetof(ElementIndexedQuadWithoutColor, v1), - (void *)(size_t)offsetof(ElementIndexedQuadWithoutColor, uv0)); + command.texcoords = (AttributeArrayPointer) { + .arity = 2, + .type = GL_FLOAT, + .stride = offsetof(ElementIndexedQuadWithoutColor, v1), + .offset = offsetof(ElementIndexedQuadWithoutColor, uv0), + .buffer = buffer + }; - get_quad_element_buffer(); + command.constant_colored = true; + command.color = color; + + command.gpu_texture = font_data->texture; + command.uses_gpu_key = true; + command.textured = true; + + command.element_buffer = get_quad_element_buffer(); + command.element_count = 6 * (GLsizei)len; + command.range_end = 6 * (GLsizei)len; use_texture_mode(TEXTURE_MODE_GHOSTLY); - glBindTexture(GL_TEXTURE_2D, font_data->texture); + DeferredCommand final_command = { + .type = DEFERRED_COMMAND_TYPE_DRAW, + .draw = command + }; - glColor4ub(color.r, color.g, color.b, color.a); - - glDrawElements(GL_TRIANGLES, 6 * (GLsizei)len, GL_UNSIGNED_SHORT, NULL); - - /* clear the state */ - glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + arrpush(deferred_commands, final_command); /* TODO: why doesn't it get restored if not placed here? */ - glDepthMask(GL_TRUE); + // glDepthMask(GL_TRUE); } @@ -883,6 +864,47 @@ static void load_cubemap_side(const char *path, GLenum target) { SDL_FreeSurface(surface); } + +void render_circle(const CirclePrimitive *circle) { + static Vec2 vertices[CIRCLE_VERTICES_MAX]; + int num_vertices = CIRCLE_VERTICES_MAX - 1; + + create_circle_geometry(circle->position, + circle->radius, + num_vertices, + vertices); + + VertexBuffer buffer = get_scratch_vertex_array(); + specify_vertex_buffer(buffer, vertices, sizeof (Vec2) * num_vertices); + + DeferredCommandDraw command = {0}; + + command.vertices = (AttributeArrayPointer) { + .arity = 2, + .type = GL_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 * 3; + command.range_end = num_vertices * 3; + + use_texture_mode(circle->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY); + + DeferredCommand final_command = { + .type = DEFERRED_COMMAND_TYPE_DRAW, + .draw = command + }; + + arrpush(deferred_commands, final_command); +} + + void finally_render_skybox(char *paths) { static GLuint cubemap = 0; static char *paths_cache = NULL; @@ -1049,3 +1071,16 @@ void finally_apply_fog(float start, float end, float density, Color color) { void finally_pop_fog(void) { glDisable(GL_FOG); } + + +void set_depth_range(double low, double high) { + DeferredCommand const command = { + .type = DEFERRED_COMMAND_TYPE_DEPTH_RANGE, + .depth_range = { + .low = low, + .high = high + } + }; + + arrpush(deferred_commands, command); +} diff --git a/src/rendering/twn_gl_any_rendering.c b/src/rendering/twn_gl_any_rendering.c index 5b49cb9..c0510ab 100644 --- a/src/rendering/twn_gl_any_rendering.c +++ b/src/rendering/twn_gl_any_rendering.c @@ -33,40 +33,59 @@ void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) VertexBuffer get_quad_element_buffer(void) { - static GLuint buffer = 0; + static VertexBuffer buffer = 0; /* it's only generated once at runtime */ + /* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */ if (buffer == 0) { - glGenBuffers(1, &buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - QUAD_ELEMENT_BUFFER_LENGTH * 6 * sizeof(uint16_t), - NULL, - GL_STATIC_DRAW); + buffer = create_vertex_buffer(); + VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); - uint16_t *const indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, - GL_WRITE_ONLY); - if (!indices) - CRY("Quad indices generation", "glMapBuffer() failed"); + for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { + GLshort indices[6]; + indices[0] = (GLshort)(i * 4 + 0); + indices[1] = (GLshort)(i * 4 + 1); + indices[2] = (GLshort)(i * 4 + 2); + indices[3] = (GLshort)(i * 4 + 2); + indices[4] = (GLshort)(i * 4 + 3); + indices[5] = (GLshort)(i * 4 + 0); - for (uint16_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { - indices[i * 6 + 0] = (uint16_t)(i * 4 + 0); - indices[i * 6 + 1] = (uint16_t)(i * 4 + 1); - indices[i * 6 + 2] = (uint16_t)(i * 4 + 2); - indices[i * 6 + 3] = (uint16_t)(i * 4 + 2); - indices[i * 6 + 4] = (uint16_t)(i * 4 + 3); - indices[i * 6 + 5] = (uint16_t)(i * 4 + 0); + push_to_vertex_buffer_builder(&builder, indices, sizeof indices); } + } - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - - } else - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + SDL_assert_always(buffer); return buffer; } -void set_depth_range(double low, double high) { - glDepthRange(low, high); +VertexBuffer get_circle_element_buffer(void) { + static VertexBuffer buffer = 0; + + if (buffer == 0) { + buffer = create_vertex_buffer(); + VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * CIRCLE_ELEMENT_BUFFER_LENGTH); + + for (size_t i = 1; i < CIRCLE_VERTICES_MAX; ++i) { + /* first one is center point index, always zero */ + GLshort indices[3]; + + indices[0] = 0; + + /* generated point index */ + indices[1] = (GLshort)i; + + size_t index = (i + 1) % (CIRCLE_VERTICES_MAX - 1); + if (index == 0) /* don't use center for outer ring */ + index = (CIRCLE_VERTICES_MAX - 1); + indices[2] = (GLshort)index; + + push_to_vertex_buffer_builder(&builder, indices, sizeof indices); + } + } + + SDL_assert_always(buffer); + + return buffer; } diff --git a/src/twn_util_c.h b/src/twn_util_c.h index a094ef9..c963b2a 100644 --- a/src/twn_util_c.h +++ b/src/twn_util_c.h @@ -39,7 +39,7 @@ static inline float fast_sqrt(float x) static inline Vec2 fast_cossine(float a) { - const float s = SDL_sinf(a); + const float s = sinf(a); return (Vec2){ .x = fast_sqrt(1.0f - s * s) * (a >= (float)M_PI_2 && a < (float)(M_PI + M_PI_2) ? -1 : 1), .y = s