#include "twn_gl_15_rendering_c.h" #include "twn_rendering_c.h" #include "townengine/util.h" #include "townengine/config.h" #include "townengine/context.h" #include "twn_text_c.h" #include /* interleaved vertex array data */ /* TODO: use int16_t for uvs */ /* TODO: use packed types? */ /* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ struct element_indexed_quad { /* upper-left */ t_fvec2 v0; t_fvec2 uv0; t_color c0; /* bottom-left */ t_fvec2 v1; t_fvec2 uv1; t_color c1; /* bottom-right */ t_fvec2 v2; t_fvec2 uv2; t_color c2; /* upper-right */ t_fvec2 v3; t_fvec2 uv3; t_color c3; }; struct element_indexed_quad_without_color { /* upper-left */ t_fvec2 v0; t_fvec2 uv0; /* bottom-left */ t_fvec2 v1; t_fvec2 uv1; /* bottom-right */ t_fvec2 v2; t_fvec2 uv2; /* upper-right */ t_fvec2 v3; t_fvec2 uv3; }; typedef enum { PIPELINE_NO, PIPELINE_SPACE, PIPELINE_2D, } pipeline; static pipeline pipeline_last_used = PIPELINE_NO; void use_space_pipeline(void) { glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glShadeModel(GL_SMOOTH); if (GLAD_GL_ARB_depth_clamp) glDisable(GL_DEPTH_CLAMP); glMatrixMode(GL_PROJECTION); glLoadMatrixf(&camera_projection_matrix.row[0].x); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(&camera_look_at_matrix.row[0].x); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); glDepthRange(0, 1); glDisable(GL_BLEND); glEnable(GL_ALPHA_TEST); /* TODO: infer its usage? */ glAlphaFunc(GL_EQUAL, 1.0f); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); /* solid white, no modulation */ glColor4ub(255, 255, 255, 255); pipeline_last_used = PIPELINE_SPACE; } void use_2d_pipeline(void) { if (pipeline_last_used == PIPELINE_SPACE) { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glFlush(); } glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glShadeModel(GL_FLAT); /* removes near/far plane comparison and discard */ if (GLAD_GL_ARB_depth_clamp) glDisable(GL_DEPTH_CLAMP); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glDisable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); pipeline_last_used = PIPELINE_2D; } void upload_quad_vertices(t_frect rect) { /* client memory needs to be reachable on glDraw*, so */ static float vertices[6 * 2]; vertices[0] = rect.x; vertices[1] = rect.y; vertices[2] = rect.x; vertices[3] = rect.y + rect.h; vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h; vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h; vertices[8] = rect.x + rect.w; vertices[9] = rect.y; vertices[10] = rect.x; vertices[11] = rect.y; glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices); } void render_rectangle(const struct rect_primitive *rectangle) { glColor4ub(rectangle->color.r, rectangle->color.g, rectangle->color.b, rectangle->color.a); glEnableClientState(GL_VERTEX_ARRAY); upload_quad_vertices(rectangle->rect); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableClientState(GL_VERTEX_ARRAY); } void render_circle(const struct circle_primitive *circle) { SDL_Vertex *vertices = NULL; int *indices = NULL; int num_vertices = (int)circle->radius; 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); free(vertices); free(indices); } void use_texture_mode(enum texture_mode mode) { if (mode == TEXTURE_MODE_GHOSTLY) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthFunc(GL_ALWAYS); glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); } else if (mode == TEXTURE_MODE_SEETHROUGH) { glDisable(GL_BLEND); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_EQUAL, 1.0f); } else { glDisable(GL_BLEND); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); glDisable(GL_ALPHA_TEST); } } vertex_buffer_builder build_vertex_buffer(vertex_buffer buffer, size_t bytes) { glBindBuffer(GL_ARRAY_BUFFER, buffer); void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!mapping) CRY("build_vertex_buffer", "Error mapping a vertex array buffer"); return (vertex_buffer_builder) { .mapping = mapping, .bytes_left = bytes, }; } bool push_to_vertex_buffer_builder(vertex_buffer_builder *builder, void *bytes, size_t size) { if (builder->bytes_left == 0) return false; memcpy(builder->mapping, bytes, size); builder->bytes_left -= size; /* trigger data send */ if (builder->bytes_left == 0) { glUnmapBuffer(GL_ARRAY_BUFFER); return false; } return true; } void finally_render_sprites(const struct primitive_2d primitives[], const struct sprite_batch batch, const vertex_buffer vertex_buffer) { /* TODO: maybe do, dunno */ // glBindBuffer(GL_VERTEX_ARRAY, vertex_buffer); (void)vertex_buffer; GLsizei off; GLsizei voff; GLsizei uvoff; if (!batch.constant_colored) { off = offsetof(struct element_indexed_quad, v1); voff = offsetof(struct element_indexed_quad, v0); uvoff = offsetof(struct element_indexed_quad, uv0); } else { off = offsetof(struct element_indexed_quad_without_color, v1); voff = offsetof(struct element_indexed_quad_without_color, v0); uvoff = offsetof(struct element_indexed_quad_without_color, uv0); } /* vertex specification */ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, off, (void *)(size_t)voff); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, off, (void *)(size_t)uvoff); if (!batch.constant_colored) { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_UNSIGNED_BYTE, off, (void *)offsetof(struct element_indexed_quad, c0)); } else glColor4ub(primitives[0].sprite.color.r, primitives[0].sprite.color.g, primitives[0].sprite.color.b, primitives[0].sprite.color.a); if (!batch.repeat) textures_bind(&ctx.texture_cache, primitives->sprite.texture_key); else textures_bind_repeating(&ctx.texture_cache, primitives->sprite.texture_key); bind_quad_element_buffer(); glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, 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); glDisableClientState(GL_COLOR_ARRAY); glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } size_t get_sprite_payload_size(struct sprite_batch batch) { if (batch.constant_colored) return sizeof (struct element_indexed_quad_without_color); else return sizeof (struct element_indexed_quad); } bool push_sprite_payload_to_vertex_buffer_builder(struct sprite_batch batch, vertex_buffer_builder *builder, t_fvec2 v0, t_fvec2 v1, t_fvec2 v2, t_fvec2 v3, t_fvec2 uv0, t_fvec2 uv1, t_fvec2 uv2, t_fvec2 uv3, t_color color) { if (!batch.constant_colored) { struct element_indexed_quad buffer_element = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, .uv0 = uv0, .uv1 = uv1, .uv2 = uv2, .uv3 = uv3, /* equal for all (flat shaded) */ .c0 = color, .c1 = color, .c2 = color, .c3 = color, }; return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); } else { struct element_indexed_quad_without_color buffer_element = { .v0 = v0, .v1 = v1, .v2 = v2, .v3 = v3, .uv0 = uv0, .uv1 = uv1, .uv2 = uv2, .uv3 = uv3, }; return push_to_vertex_buffer_builder(builder, &buffer_element, sizeof buffer_element); } } void finally_draw_uncolored_space_traingle_batch(const struct mesh_batch *batch, const t_texture_key texture_key, const vertex_buffer vertex_buffer) { const size_t primitives_len = arrlenu(batch->primitives); textures_bind(&ctx.texture_cache, texture_key); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); /* vertex specification*/ glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, offsetof(struct uncolored_space_triangle_payload, v1), (void *)offsetof(struct uncolored_space_triangle_payload, v0)); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2, GL_FLOAT, offsetof(struct uncolored_space_triangle_payload, v1), (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); /* commit for drawing */ glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); /* invalidate the buffer immediately */ glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } bool push_text_payload_to_vertex_buffer_builder(struct font_data const *font_data, vertex_buffer_builder *builder, stbtt_aligned_quad quad) { (void)font_data; glTexCoord2f(quad.s0, quad.t0); glVertex2f(quad.x0, quad.y0); glTexCoord2f(quad.s1, quad.t0); glVertex2f(quad.x1, quad.y0); glTexCoord2f(quad.s1, quad.t1); glVertex2f(quad.x1, quad.y1); glTexCoord2f(quad.s0, quad.t1); glVertex2f(quad.x0, quad.y1); } void finally_draw_text(struct font_data const *font_data, size_t len, t_color color, vertex_buffer buffer) { use_texture_mode(TEXTURE_MODE_GHOSTLY); glBindTexture(GL_TEXTURE_2D, font_data->texture); glColor4ub(color.r, color.g, color.b, color.a); } size_t get_text_payload_size(void) { return sizeof (struct element_indexed_quad_without_color); }