improving rendering structure a little bit and started with chat impl.

This commit is contained in:
Dennis Eichhorn 2024-10-04 22:10:24 +02:00
parent 3494641cac
commit 85b77f442c
7 changed files with 204 additions and 123 deletions

View File

@ -4,16 +4,17 @@
#include "../stdlib/Types.h"
#include "../memory/BufferMemory.h"
enum TextAlignH {
TEXT_ALIGN_H_LEFT,
TEXT_ALIGN_H_CENTER,
TEXT_ALIGN_H_RIGHT,
// @todo Move this somewhere else, it doesn't belong here
enum UIAlignH {
UI_ALIGN_H_LEFT,
UI_ALIGN_H_CENTER,
UI_ALIGN_H_RIGHT,
};
enum TextAlignV {
TEXT_ALIGN_V_BOTTOM,
TEXT_ALIGN_V_CENTER,
TEXT_ALIGN_V_TOP,
enum UIAlignV {
UI_ALIGN_V_BOTTOM,
UI_ALIGN_V_CENTER,
UI_ALIGN_V_TOP,
};
struct GlyphMetrics {

View File

@ -12,64 +12,187 @@
#include <stdio.h>
#include <string.h>
#include "../math/matrix/MatrixFloat32.h"
#include "../font/Font.h"
void make_character(
f32 *data,
f32 x, f32 y, f32 n, f32 m, char c)
{
f32 *d = data;
inline
void vertex_rect_create(
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
uint32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
) {
if (align_h == UI_ALIGN_H_RIGHT) {
x -= width;
} else if (align_h == UI_ALIGN_H_CENTER) {
x -= width / 2;
}
// Texture atlas is 16 characters
// 1 / 16 = 0.0625
f32 a = 0.0625;
f32 b = 0.0625 * 2;
if (align_v == UI_ALIGN_V_TOP) {
y -= height;
} else if (align_v == UI_ALIGN_V_CENTER) {
y -= height / 2;
}
// ascii offset
int32 w = c - 32;
// Degenerate triangles
// They are alternating every loop BUT since we use references they look the same in code
// WARNING: Before using we must make sure that the 0 index is defined
// The easiest way is to just define a "degenerate" starting point
vertices[*index].position.x = vertices[*index - 1].position.x;
vertices[*index].position.y = vertices[*index - 1].position.y;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = 0;
vertices[*index].tex_coord.y = 0;
vertices[*index].color = 0;
++(*index);
f32 du = (w % 16) * a;
f32 dv = 1 - (w / 16) * b - b;
vertices[*index].position.x = x;
vertices[*index].position.y = y;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = 0;
vertices[*index].tex_coord.y = 0;
vertices[*index].color = 0;
++(*index);
// Quad data (2 triangles)
*(d++) = x - n; *(d++) = y - m;
*(d++) = du + 0; *(d++) = dv;
*(d++) = x + n; *(d++) = y - m;
*(d++) = du + a; *(d++) = dv;
*(d++) = x + n; *(d++) = y + m;
*(d++) = du + a; *(d++) = dv + b;
*(d++) = x - n; *(d++) = y - m;
*(d++) = du + 0; *(d++) = dv;
*(d++) = x + n; *(d++) = y + m;
*(d++) = du + a; *(d++) = dv + b;
*(d++) = x - n; *(d++) = y + m;
*(d++) = du + 0; *(d++) = dv + b;
// Rectangle
vertices[*index].position.x = x;
vertices[*index].position.y = y;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = tex_x1;
vertices[*index].tex_coord.y = tex_y1;
vertices[*index].color = color_index;
++(*index);
// Depending on the orientation we either need to add or subtract height -> we use branchless code for that
vertices[*index].position.x = x;
vertices[*index].position.y = y + height;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = tex_x1;
vertices[*index].tex_coord.y = tex_y2;
vertices[*index].color = color_index;
++(*index);
vertices[*index].position.x = x + width;
vertices[*index].position.y = y;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = tex_x2;
vertices[*index].tex_coord.y = tex_y1;
vertices[*index].color = color_index;
++(*index);
vertices[*index].position.x = x + width;
vertices[*index].position.y = y + height;
vertices[*index].position.z = zindex;
vertices[*index].tex_coord.x = tex_x2;
vertices[*index].tex_coord.y = tex_y2;
vertices[*index].color = color_index;
++(*index);
}
void font_string_dimension(const char *str, v2_int32* dim, const int* width_lookup)
{
size_t length = strlen(str);
int32 width = 0;
void text_calculate_dimensions(
f32* __restrict width, f32* __restrict height,
const Font* __restrict font, const char* text, f32 scale, int32 length
) {
f32 x = 0;
f32 y = font->line_height * scale;
for (int32 i = 0; i < length; ++i) {
if (str[i] == '\n') {
if (width > dim->x) {
dim->x = width;
}
f32 offset_x = 0;
width = 0;
++dim->y;
// @todo remember to restrict to width/height if value > 0 -> force width to remain below certain value
for (int i = 0; i < length; ++i) {
int32 character = utf8_get_char_at(text, i);
if (character == '\n') {
x = OMS_MAX(x, offset_x);
y += font->line_height * scale;
offset_x = 0;
continue;
}
width += width_lookup[str[i]];
Glyph* glyph = NULL;
for (int j = 0; j < font->glyph_count; ++j) {
if (font->glyphs[j].codepoint == character) {
glyph = &font->glyphs[j];
break;
}
}
if (!glyph) {
continue;
}
offset_x += (glyph->metrics.width + glyph->metrics.offset_x) * scale;
}
if (width > dim->x) {
dim->x = width;
*width = OMS_MAX(x, offset_x);
*height = y;
}
void vertex_text_create(
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
const Font* __restrict font, const char* __restrict text, f32 size, uint32 color_index = 0
) {
int32 length = utf8_strlen(text);
float scale = size / font->size;
// If we do a different alignment we need to pre-calculate the width and height
if (align_h != 0 || align_v != 0) {
text_calculate_dimensions(&width, &height, font, text, scale, length);
if (align_h == UI_ALIGN_H_RIGHT) {
x -= width;
} else if (align_h == UI_ALIGN_H_CENTER) {
x -= width / 2;
}
if (align_v == UI_ALIGN_V_TOP) {
y -= height;
} else if (align_v == UI_ALIGN_V_CENTER) {
y -= height / 2;
}
}
if (width > 0) {
++dim->y;
f32 offset_x = x;
for (int i = 0; i < length; ++i) {
int32 character = utf8_get_char_at(text, i);
if (character == '\n') {
y += font->line_height * scale;
offset_x = x;
continue;
}
Glyph* glyph = NULL;
for (int j = 0; j < font->glyph_count; ++j) {
if (font->glyphs[j].codepoint == character) {
glyph = &font->glyphs[j];
break;
}
}
if (!glyph) {
continue;
}
vertex_rect_create(
vertices, index, zindex,
offset_x, y, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
color_index, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
);
offset_x += (glyph->metrics.width + glyph->metrics.offset_x) * scale;
}
// @question How and where to cut off text out of view (here or somewhere else)
// We could just prepare the entire text here but then decide what to render later?
// @todo If width or height (usually just width) > 0 we use those values for automatic wrapping
// This way we can ensure no overflow easily
// @todo implement line alignment, currently only total alignment is considered
}
inline

View File

@ -559,77 +559,6 @@ int get_gpu_free_memory()
return available;
}
struct TextRender {
uint32 align;
uint32 x;
uint32 y;
f32 scale;
VertexRef vertices;
char* text;
uint32 sampler_id;
TextShader shader_data;
};
#define TEXT_ALIGN_LEFT 0
#define TEXT_ALIGN_CENTER 1
#define TEXT_ALIGN_RIGHT 2
// @performance This needs to handle batched randering, isolated rendering is WAY to inefficient
inline
void render_text_batched(
RingMemory* ring,
int32 width, int32 height,
TextRender* text_data
) {
glUseProgram(text_data->shader_data.program_id);
glUniform1i(text_data->shader_data.sampler_addr, text_data->vertices.sampler_id);
// @performance Instead of re-creating the matrix every render call just use the id to activate it
// 2d projection
if (text_data->shader_data.matrix_id == 0) {
f32 matrix[16] = {};
mat4_ortho_sparse_rh(matrix, 0.0f, (f32) width, 0.0f, (f32) height, -1.0f, 1.0f);
glUniformMatrix4fv(text_data->shader_data.matrix_addr, 1, GL_FALSE, matrix);
text_data->shader_data.matrix_id = 1;
// @bug this is wrong, we need to make buffer instead. We don't want to upload the matrix every time
// Isn't this buffer the same for every text?
// If yes, consider to save the orth projection matrix globally and change it whenever we change the resolution/window dimensions
} else {
// @question Do we even need to bind it if we never change it?
// We also never bind our projection matrix apart from the first time and it works. This should be the same no?
//glBindVertexArray(text_data->shader_data.matrix_id);
}
int32 length = (int32) strlen(text_data->text);
f32 x = text_data->x - text_data->scale * text_data->align * (length - 1) / 2;
// @performance Only create when the text got removed from memory
if (text_data->vertices.data_id == 0) {
GLfloat *data = (GLfloat *) ring_get_memory(ring, sizeof(GLfloat) * 6 * 4 * length);
for (int32 i = 0; i < length; i++) {
make_character(data + i * 24, x, (f32) text_data->y, text_data->scale / 2, text_data->scale, text_data->text[i]);
x += text_data->scale;
}
text_data->vertices.data_id = gpuapi_buffer_generate(sizeof(GLfloat) * 6 * 4 * length, data);
} else {
glBindBuffer(GL_ARRAY_BUFFER, text_data->vertices.data_id);
//glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); Is this required?
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw_triangles_2d(&text_data->vertices, text_data->vertices.data_id, length * 6);
glDisable(GL_BLEND);
// @performance We should only delete the buffer, when the text becomes invisible
// @todo remember to implement a remove logic for all the buffers
//glDeleteBuffers(1, &text_data->vertices.data_id);
}
/*
void render_9_patch(GLuint texture,
int32 imgWidth, int32 imgHeight,

View File

@ -11,6 +11,7 @@
#include "../stdlib/Types.h"
#include "../utils/BitUtils.h"
#include "../memory/BufferMemory.h"
#include "ControllerInput.h"
// How many concurrent mouse/secondary input device presses to we recognize
@ -526,6 +527,9 @@ void input_set_controller_state(Input* input, ControllerInput* controller, uint6
void
input_hotkey_state(Input* input)
{
uint8 old_hotkeys[MAX_KEY_PRESSES];
memcpy(old_hotkeys, input->state.state_hotkeys, sizeof(uint8) * MAX_KEY_PRESSES);
memset(input->state.state_hotkeys, 0, sizeof(uint8) * MAX_KEY_PRESSES);
int32 active_hotkeys = 0;
@ -576,15 +580,27 @@ input_hotkey_state(Input* input)
return;
}
// Hotkey already active
// @question Do we even need this? This shouldn't happen anyway?!
if (hotkey_is_active(&input->state, hotkeys_for_key[possible_hotkey_idx])) {
continue;
}
bool is_pressed = hotkey_keys_are_active(&input->state, mapping, hotkeys_for_key[possible_hotkey_idx]);
// store active hotkey, if it is not already active
if (is_pressed && !hotkey_is_active(&input->state, hotkeys_for_key[possible_hotkey_idx])) {
if (is_pressed) {
input->state.state_hotkeys[active_hotkeys] = hotkeys_for_key[possible_hotkey_idx];
++active_hotkeys;
// Run callback if defined
if (input->input_mapping1.callbacks[hotkeys_for_key[possible_hotkey_idx]] != 0) {
if (input->input_mapping1.callbacks[hotkeys_for_key[possible_hotkey_idx]] != 0
&& old_hotkeys[0] != hotkeys_for_key[possible_hotkey_idx]
&& old_hotkeys[1] != hotkeys_for_key[possible_hotkey_idx]
&& old_hotkeys[2] != hotkeys_for_key[possible_hotkey_idx]
&& old_hotkeys[3] != hotkeys_for_key[possible_hotkey_idx]
&& old_hotkeys[4] != hotkeys_for_key[possible_hotkey_idx]
) {
input->input_mapping1.callbacks[hotkeys_for_key[possible_hotkey_idx]](input->callback_data);
}
}

View File

@ -289,6 +289,10 @@ struct CSettings {
byte game_chat_size = 128;
int32 game_chat_pos[2] = { -1, -1 };
// @todo replace settings below with bit flag
// UI
uint64 ui_visibility_flags = 0;
// HUD
bool game_show_health_bar_self = false;
bool game_show_health_bar_player = false;

View File

@ -74,4 +74,6 @@
#define SETTING_TYPE_DISABLED 0x00
#define SETTING_TYPE_UNLIMITED 0x00
#define SETTING_UI_VISIBILITY_FPS 1
#endif

View File

@ -18,6 +18,12 @@ struct Vertex3D {
v4_f32 color;
};
struct Vertex3DTextureColor {
v3_f32 position;
v2_f32 tex_coord;
v4_f32 color;
};
struct Vertex3DTextureColorIndex {
v3_f32 position;
v2_f32 tex_coord;