mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-11 19:28:40 +00:00
improving rendering structure a little bit and started with chat impl.
This commit is contained in:
parent
3494641cac
commit
85b77f442c
17
font/Font.h
17
font/Font.h
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user