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

221
src/rendering/twn_text.c Normal file
View File

@ -0,0 +1,221 @@
#include "twn_rendering_c.h"
#include "townengine/util.h"
#include "townengine/config.h"
#include "townengine/context.h"
#include "townengine/twn_rendering.h"
#include "twn_rendering_platform.h"
#include <stb_truetype.h>
#define ASCII_START 32
#define ASCII_END 128
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
struct font_data {
stbtt_packedchar char_data[NUM_DISPLAY_ASCII];
stbtt_fontinfo info;
const char *file_path;
unsigned char *file_bytes;
size_t file_bytes_len;
gpu_texture texture;
int height_px;
float scale_factor;
int ascent;
int descent;
int line_gap;
};
static struct font_data *text_load_font_data(const char *path, int height_px) {
struct font_data *font_data = ccalloc(1, sizeof *font_data);
font_data->file_path = path;
font_data->height_px = height_px;
unsigned char* bitmap = ccalloc(TEXT_FONT_TEXTURE_SIZE * TEXT_FONT_TEXTURE_SIZE, 1);
{
unsigned char *buf = NULL;
int64_t buf_len = file_to_bytes(path, &buf);
stbtt_InitFont(&font_data->info, buf, stbtt_GetFontOffsetForIndex(buf, 0));
/* might as well get these now, for later */
font_data->file_bytes = buf;
font_data->file_bytes_len = buf_len;
font_data->scale_factor = stbtt_ScaleForPixelHeight(&font_data->info, (float)height_px);
stbtt_GetFontVMetrics(
&font_data->info,
&font_data->ascent,
&font_data->descent,
&font_data->line_gap
);
font_data->ascent = (int)((float)font_data->ascent * font_data->scale_factor);
font_data->descent = (int)((float)font_data->descent * font_data->scale_factor);
font_data->line_gap = (int)((float)font_data->line_gap * font_data->scale_factor);
stbtt_pack_context pctx;
stbtt_PackBegin(&pctx, bitmap, TEXT_FONT_TEXTURE_SIZE, TEXT_FONT_TEXTURE_SIZE, 0, 1, NULL);
stbtt_PackSetOversampling(&pctx, TEXT_FONT_OVERSAMPLING, TEXT_FONT_OVERSAMPLING);
stbtt_PackFontRange(&pctx, buf, 0, (float)height_px, ASCII_START, NUM_DISPLAY_ASCII, font_data->char_data);
stbtt_PackEnd(&pctx);
}
font_data->texture = create_gpu_texture(TEXT_FONT_FILTERING, true);
specify_gpu_texture(
font_data->texture,
bitmap,
1,
TEXT_FONT_TEXTURE_SIZE,
TEXT_FONT_TEXTURE_SIZE
);
free(bitmap);
return font_data;
}
static void text_destroy_font_data(struct font_data *font_data) {
free(font_data->file_bytes);
delete_gpu_texture(font_data->texture);
free(font_data);
}
static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) {
static vertex_buffer vertex_array = 0;
if (vertex_array == 0)
vertex_array = create_vertex_buffer();
const size_t len = SDL_strlen(text);
vertex_buffer_builder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len);
for (size_t i = 0; i < len; ++i) {
const char c = text[i];
/* outside the range of what we want to display */
//if (c < ASCII_START || c > ASCII_END)
if (c < ASCII_START)
continue;
/* stb_truetype.h conveniently provides everything we need to draw here! */
stbtt_aligned_quad quad;
stbtt_GetPackedQuad(
font_data->char_data,
TEXT_FONT_TEXTURE_SIZE,
TEXT_FONT_TEXTURE_SIZE,
c - ASCII_START,
&position.x,
&position.y,
&quad,
true
);
/* have to do this so the "origin" is at the top left */
/* maybe there's a better way, or maybe this isn't a good idea... */
/* who knows */
quad.y0 += (float)font_data->ascent;
quad.y1 += (float)font_data->ascent;
push_text_payload_to_vertex_buffer_builder(font_data, &payload, quad);
}
finally_draw_text(font_data, len, color, vertex_array);
}
static void ensure_font_cache(const char *font_path, int height_px) {
/* HACK: stupid, bad, don't do this */
bool is_cached = false;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
struct font_data *font_data = ctx.text_cache.data[i];
if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
is_cached = true;
break;
}
}
if (!is_cached) {
struct font_data *new_font_data = text_load_font_data(font_path, height_px);
arrput(ctx.text_cache.data, new_font_data);
}
}
static struct font_data *get_font_data(const char *font_path, int height_px) {
struct font_data *font_data = NULL;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
font_data = ctx.text_cache.data[i];
if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
break;
}
}
return font_data;
}
void render_text(const struct text_primitive *text) {
struct font_data *font_data = get_font_data(text->font, text->height_px);
text_draw_with(font_data, text->text, text->position, text->color);
}
void text_cache_init(struct text_cache *cache) {
arrsetlen(cache->data, 0);
}
void text_cache_deinit(struct text_cache *cache) {
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
text_destroy_font_data(ctx.text_cache.data[i]);
}
arrfree(cache->data);
}
void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path) {
ensure_font_cache(font_path, height_px);
/* the string might not be around by the time it's used, so copy it */
/* TODO: arena */
/* NOTE: can we trust strlen? */
char *dup_string = cmalloc(strlen(string) + 1);
strcpy(dup_string, string);
struct text_primitive text = {
.color = color,
.position = position,
.text = dup_string,
.font = font_path,
.height_px = height_px,
};
struct primitive_2d primitive = {
.type = PRIMITIVE_2D_TEXT,
.text = text,
};
arrput(ctx.render_queue_2d, primitive);
}
int get_text_width(char *string, int height_px, const char *font_path) {
ensure_font_cache(font_path, height_px);
struct font_data *font_data = get_font_data(font_path, height_px);
int length = 0;
for (const char *p = string; *p != '\0'; ++p) {
const char c = *p;
int advance_width = 0;
int left_side_bearing = 0;
stbtt_GetCodepointHMetrics(&font_data->info, (int)c, &advance_width, &left_side_bearing);
length += advance_width;
}
return (int)((float)length * font_data->scale_factor);
}