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

View File

@ -12,64 +12,187 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "../math/matrix/MatrixFloat32.h" #include "../math/matrix/MatrixFloat32.h"
#include "../font/Font.h"
void make_character( inline
f32 *data, void vertex_rect_create(
f32 x, f32 y, f32 n, f32 m, char c) Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
{ f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
f32 *d = data; 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 if (align_v == UI_ALIGN_V_TOP) {
// 1 / 16 = 0.0625 y -= height;
f32 a = 0.0625; } else if (align_v == UI_ALIGN_V_CENTER) {
f32 b = 0.0625 * 2; y -= height / 2;
}
// ascii offset // Degenerate triangles
int32 w = c - 32; // 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; vertices[*index].position.x = x;
f32 dv = 1 - (w / 16) * b - b; 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) // Rectangle
*(d++) = x - n; *(d++) = y - m; vertices[*index].position.x = x;
*(d++) = du + 0; *(d++) = dv; vertices[*index].position.y = y;
*(d++) = x + n; *(d++) = y - m; vertices[*index].position.z = zindex;
*(d++) = du + a; *(d++) = dv; vertices[*index].tex_coord.x = tex_x1;
*(d++) = x + n; *(d++) = y + m; vertices[*index].tex_coord.y = tex_y1;
*(d++) = du + a; *(d++) = dv + b; vertices[*index].color = color_index;
*(d++) = x - n; *(d++) = y - m; ++(*index);
*(d++) = du + 0; *(d++) = dv;
*(d++) = x + n; *(d++) = y + m; // Depending on the orientation we either need to add or subtract height -> we use branchless code for that
*(d++) = du + a; *(d++) = dv + b; vertices[*index].position.x = x;
*(d++) = x - n; *(d++) = y + m; vertices[*index].position.y = y + height;
*(d++) = du + 0; *(d++) = dv + b; 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) void text_calculate_dimensions(
{ f32* __restrict width, f32* __restrict height,
size_t length = strlen(str); const Font* __restrict font, const char* text, f32 scale, int32 length
int32 width = 0; ) {
f32 x = 0;
f32 y = font->line_height * scale;
for (int32 i = 0; i < length; ++i) { f32 offset_x = 0;
if (str[i] == '\n') {
if (width > dim->x) {
dim->x = width;
}
width = 0; // @todo remember to restrict to width/height if value > 0 -> force width to remain below certain value
++dim->y;
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) { *width = OMS_MAX(x, offset_x);
dim->x = width; *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) { f32 offset_x = x;
++dim->y; 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 inline

View File

@ -559,77 +559,6 @@ int get_gpu_free_memory()
return available; 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, void render_9_patch(GLuint texture,
int32 imgWidth, int32 imgHeight, int32 imgWidth, int32 imgHeight,

View File

@ -11,6 +11,7 @@
#include "../stdlib/Types.h" #include "../stdlib/Types.h"
#include "../utils/BitUtils.h" #include "../utils/BitUtils.h"
#include "../memory/BufferMemory.h"
#include "ControllerInput.h" #include "ControllerInput.h"
// How many concurrent mouse/secondary input device presses to we recognize // 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 void
input_hotkey_state(Input* input) 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); memset(input->state.state_hotkeys, 0, sizeof(uint8) * MAX_KEY_PRESSES);
int32 active_hotkeys = 0; int32 active_hotkeys = 0;
@ -576,15 +580,27 @@ input_hotkey_state(Input* input)
return; 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]); 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 // 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]; input->state.state_hotkeys[active_hotkeys] = hotkeys_for_key[possible_hotkey_idx];
++active_hotkeys; ++active_hotkeys;
// Run callback if defined // 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); 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; byte game_chat_size = 128;
int32 game_chat_pos[2] = { -1, -1 }; int32 game_chat_pos[2] = { -1, -1 };
// @todo replace settings below with bit flag
// UI
uint64 ui_visibility_flags = 0;
// HUD // HUD
bool game_show_health_bar_self = false; bool game_show_health_bar_self = false;
bool game_show_health_bar_player = false; bool game_show_health_bar_player = false;

View File

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

View File

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