partially done work on total source tree rework, separation of engine context and game context, generalization of renderer for different backends as well as web platform target

This commit is contained in:
2024-09-16 09:07:01 +03:00
parent ca0305feab
commit 551d60ef85
59 changed files with 2892 additions and 890 deletions

View File

@ -0,0 +1,429 @@
#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 <glad/glad.h>
/* 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);
}