roleplay/client/src/main.c

1393 lines
40 KiB
C

#include "GLFW/glfw3.h"
#include <stdio.h>
#define CGLM_PRINT_PRECISION 10
#define CGLM_DEFINE_PRINTS 1
#include "ui.h"
#include "gpu.h"
#include "draw.h"
#include "hex.h"
#include "arpa/inet.h"
#include "vk_mem_alloc.h"
#include "vulkan/vk_enum_string_helper.h"
#include "vulkan/vulkan_core.h"
#include "pthread.h"
#include <math.h>
#include <stdlib.h>
typedef struct EditorContextStruct {
uint32_t selected_region;
uint32_t last_selected_region;
uint32_t last_clicked_region;
uint32_t last_clicked_hex;
uint32_t last_clicked_vertex;
} EditorContext;
typedef struct ClientContextStruct {
GLFWwindow* window;
RenderContext* render;
UIContext* ui;
HexContext* hex;
vec3 position;
vec3 velocity;
vec2 rotation;
int32_t key_spin[2];
vec2 cur_spin;
double distance;
int32_t zoom;
bool camera_mode;
float key_spin_speed;
float cur_spin_speed;
float zoom_speed;
float move_speed;
EditorContext editor;
} ClientContext;
void* network_thread(void* data) {
ClientContext* context = (ClientContext*)data;
(void)context;
return NULL;
}
uint32_t add_hex_region(ClientContext* context) {
HexRegion* region;
allocate_hex_region(0, 0, 0, context->hex->data.current_map, &region, context->hex, context->render);
for(uint32_t hex = 0; hex < REGION_HEX_COUNT; hex++) {
region->data.hexes[hex].color[0] = 0xFFFFFFFF;
region->data.hexes[hex].color[1] = 0xFFFFFFFF;
region->data.hexes[hex].color[2] = 0xFFFFFFFF;
region->data.hexes[hex].color[3] = 0xFFFFFFFF;
region->data.hexes[hex].color[4] = 0xFFFFFFFF;
region->data.hexes[hex].color[5] = 0xFFFFFFFF;
region->data.hexes[hex].color[6] = 0xFFFFFFFF;
region->data.hexes[hex].height[0] = 0;
region->data.hexes[hex].height[1] = 0;
region->data.hexes[hex].height[2] = 0;
region->data.hexes[hex].height[3] = 0;
region->data.hexes[hex].height[4] = 0;
region->data.hexes[hex].height[5] = 0;
}
set_hex_region(region, context->hex, context->render);
uint32_t i = 0;
for(; i < MAX_LOADED_REGIONS; i++) {
if(region == context->hex->regions[i]) {
break;
}
}
return i;
}
#define COLOR_PICK_CONTAINER_ID 0x03
void hsv_to_rgb(vec3 hsv, vec3 rgb) {
float C = hsv[1] * hsv[2];
float H = hsv[0]*6;
float X = C * (1 - fabs((H - 2 * floor( H / 2 )) - 1));
vec4 temp = {0, 0, 0, 0};
if(0 <= H && H <= 1) {
temp[0] = C;
temp[1] = X;
} else if(1 <= H && H <= 2) {
temp[0] = X;
temp[1] = C;
} else if(2 <= H && H <= 3) {
temp[1] = C;
temp[2] = X;
} else if(3 <= H && H <= 4) {
temp[1] = X;
temp[2] = C;
} else if(4 <= H && H <= 5) {
temp[0] = X;
temp[2] = C;
} else if(5 <= H && H <= 6) {
temp[0] = C;
temp[2] = X;
}
float m = hsv[2] - C;
rgb[0] = temp[0] + m;
rgb[1] = temp[1] + m;
rgb[2] = temp[2] + m;
}
void rgb_string_set(UIContext* ui, RenderContext* gpu, vec3 hsv) {
vec3 rgb;
hsv_to_rgb(hsv, rgb);
char temp[10];
snprintf(temp, 10, "#%02x%02x%02x",
(uint)(rgb[0]*255),
(uint)(rgb[1]*255),
(uint)(rgb[2]*255));
update_ui_string(temp, COLOR_PICK_CONTAINER_ID, 0, 0, ui, gpu);
}
void sv_square_pick(UIContext* ui, RenderContext* gpu, float s, float v) {
if(s < 0) s = 0;
if(s > 1) s = 1;
if(v < 0) v = 0;
if(v > 1) v = 1;
Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui);
Layer* layer = &container->layers[0];
GPUDrawable* select_outline = &layer->drawables_buffer[3];
GPUDrawable* select = &layer->drawables_buffer[4];
select->pos[0] = s*130 - 2;
select->pos[1] = v*130 - 2;
select->color[0][1] = s;
select->color[0][2] = 1-v;
select->color[1][1] = s;
select->color[1][2] = 1-v;
select->color[2][1] = s;
select->color[2][2] = 1-v;
select->color[3][1] = s;
select->color[3][2] = 1-v;
select_outline->pos[0] = s*130 - 3;
select_outline->pos[1] = v*130 - 3;
add_transfers(
&select_outline->pos[0],
layer->drawables,
3*sizeof(GPUDrawable) + offsetof(GPUDrawable, pos),
1*sizeof(vec2),
gpu);
add_transfers(
&select->pos[0],
layer->drawables,
4*sizeof(GPUDrawable) + offsetof(GPUDrawable, pos),
1*sizeof(vec2),
gpu);
add_transfers(
&select->color[0],
layer->drawables,
4*sizeof(GPUDrawable) + offsetof(GPUDrawable, color),
4*sizeof(vec4),
gpu);
rgb_string_set(ui, gpu, select->color[0]);
}
void sv_square_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) {
(void)mods;
(void)x;
if(action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) {
set_active_element(COLOR_PICK_CONTAINER_ID, 0, 1, ui);
sv_square_pick(ui, gpu, x, y);
} else if(action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) {
clear_active_element(ui, gpu);
}
}
void sv_square_cursor_callback(UIContext* ui, RenderContext* gpu, float x, float y) {
if(ui->active_element == 1
&& ui->active_layer == 0
&& ui->active_container == COLOR_PICK_CONTAINER_ID) {
sv_square_pick(ui, gpu, x, y);
}
}
void hue_bar_set(UIContext* ui, RenderContext* gpu, float y) {
if(y < 0) y = 0;
if(y > 1) y = 1;
Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui);
Layer* layer = &container->layers[0];
GPUDrawable* sv_square = &layer->drawables_buffer[1];
GPUDrawable* select = &layer->drawables_buffer[4];
sv_square->color[0][0] = y;
sv_square->color[1][0] = y;
sv_square->color[2][0] = y;
sv_square->color[3][0] = y;
select->color[0][0] = y;
select->color[1][0] = y;
select->color[2][0] = y;
select->color[3][0] = y;
add_transfers(
&sv_square->color[0],
layer->drawables,
1*sizeof(GPUDrawable) + offsetof(GPUDrawable, color),
4*sizeof(vec4),
gpu);
add_transfers(
&select->color[0],
layer->drawables,
4*sizeof(GPUDrawable) + offsetof(GPUDrawable, color),
4*sizeof(vec4),
gpu);
rgb_string_set(ui, gpu, select->color[0]);
}
void hue_bar_scroll_callback(UIContext* ui, RenderContext* gpu, double x, double y) {
(void)x;
Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui);
hue_bar_set(ui, gpu, y*0.01 + container->layers[0].drawables_buffer[1].color[0][0]);
}
void hue_bar_cursor_callback(UIContext* ui, RenderContext* gpu, float x, float y) {
(void)x;
if(ui->active_element == 2
&& ui->active_layer == 0
&& ui->active_container == COLOR_PICK_CONTAINER_ID) {
hue_bar_set(ui, gpu, y);
}
}
void hue_bar_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) {
(void)mods;
(void)x;
if(action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) {
set_active_element(COLOR_PICK_CONTAINER_ID, 0, 2, ui);
hue_bar_set(ui, gpu, y);
} else if(action == GLFW_RELEASE && button == GLFW_MOUSE_BUTTON_LEFT) {
clear_active_element(ui, gpu);
}
}
void hex_string_set_color(UIContext* ui, RenderContext* gpu, float color) {
Container* container = context_container(COLOR_PICK_CONTAINER_ID, ui);
container->layers[0].drawables_buffer[5].color[0][2] = color;
container->layers[0].drawables_buffer[5].color[0][3] = color;
container->layers[0].drawables_buffer[5].color[1][2] = color;
container->layers[0].drawables_buffer[5].color[1][3] = color;
container->layers[0].drawables_buffer[5].color[2][2] = color;
container->layers[0].drawables_buffer[5].color[2][3] = color;
container->layers[0].drawables_buffer[5].color[3][2] = color;
container->layers[0].drawables_buffer[5].color[3][3] = color;
add_transfers(
&container->layers[0].drawables_buffer[5].color[0],
container->layers[0].drawables,
5*sizeof(GPUDrawable) + offsetof(GPUDrawable, color),
4*sizeof(vec4),
gpu);
}
bool hex_string_key_callback(UIContext* ui, RenderContext* gpu, int key, int action, int mods) {
(void)mods;
if(action == GLFW_PRESS && key == GLFW_KEY_ESCAPE) {
clear_active_element(ui, gpu);
return true;
}
return false;
}
void hex_string_button_callback(UIContext* ui, RenderContext* gpu, float x, float y, int button, int action, int mods) {
(void)mods;
(void)x;
(void)y;
if(action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) {
set_active_element(COLOR_PICK_CONTAINER_ID, 0, 5, ui);
hex_string_set_color(ui, gpu, 1);
}
}
void hex_string_deselect_callback(UIContext* ui, RenderContext* gpu) {
hex_string_set_color(ui, gpu, 0);
}
VkResult color_ui(ClientContext* context) {
GPUString strings[] = {
{
.pos = {2, 150},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 0,
.length = 9,
.font = 0,
},
};
GPUDrawable drawables[] = {
{
.pos = {0, 0},
.size = {190, 150},
.color = {{0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}},
},
{
.type = DRAWABLE_TYPE_RECT_HSV,
.pos = {2, 2},
.size = {130, 130},
.color = {{0, 0, 1, 1}, {0, 1, 1, 1}, {0, 0, 0, 1}, {0, 1, 0, 1}},
.events = UI_EVENT_BUTTON,
},
{
.type = DRAWABLE_TYPE_RECT_HSV,
.pos = {134, 2},
.size = {10, 130},
.color = {{0, 1, 1, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
.events = UI_EVENT_BUTTON | UI_EVENT_SCROLL,
},
{
.pos = {9, 9},
.size = {7, 7},
.color = {{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}},
},
{
.type = DRAWABLE_TYPE_RECT_HSV,
.pos = {10, 10},
.size = {5, 5},
.color = {{1, 0, 0, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}},
},
{
.pos = {20, 134},
.size = {95, 15},
.events = UI_EVENT_BUTTON | UI_EVENT_CURSOR,
},
{
.pos = {146, 2},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 2},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {146, 24},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 24},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {146, 46},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 46},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {146, 68},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 68},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {146, 90},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 90},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {146, 112},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
{
.pos = {168, 112},
.size = {20, 20},
.color = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
},
};
uint32_t codes[9];
VkResult result;
VK_RESULT(map_string("#00000000", codes, 0, 0, context->ui));
LayerInput layer = {
.strings = strings,
.num_strings = sizeof(strings)/sizeof(GPUString),
.codes = codes,
.num_codes = sizeof(codes)/sizeof(uint32_t),
.drawables = drawables,
.num_drawables = sizeof(drawables)/sizeof(GPUDrawable),
};
UICallbacks callbacks[] = {
{
.layer = 0,
.element = 1,
.button = sv_square_button_callback,
.cursor = sv_square_cursor_callback,
},
{
.layer = 0,
.element = 2,
.button = hue_bar_button_callback,
.cursor = hue_bar_cursor_callback,
.scroll = hue_bar_scroll_callback,
},
{
.layer = 0,
.element = 5,
.button = hex_string_button_callback,
.key = hex_string_key_callback,
.deselect = hex_string_deselect_callback,
},
};
ContainerInput container = {
.layers = &layer,
.layer_count = 1,
.anchor = ANCHOR_BOTTOM_LEFT,
.id = COLOR_PICK_CONTAINER_ID,
.offset = {0, 0},
.size = {190, 150},
.callbacks = callbacks,
.callback_count = sizeof(callbacks)/sizeof(UICallbacks),
};
return create_container(&container, context->render, context->ui);
}
VkResult hex_info_ui(ClientContext* context) {
GPUString strings[] = {
{
.pos = {0, 33},
.color = {1, 1, 1, 1},
.size = 32,
.offset = 0,
.length = 0,
.font = 0,
},
{
.pos = {2, 50},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 20,
.length = 0,
.font = 0,
},
{
.pos = {2, 73},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 40,
.length = 0,
.font = 0,
},
{
.pos = {2, 93},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 60,
.length = 0,
.font = 0,
},
{
.pos = {2, 113},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 80,
.length = 0,
.font = 0,
},
{
.pos = {2, 133},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 100,
.length = 0,
.font = 0,
},
{
.pos = {2, 153},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 120,
.length = 0,
.font = 0,
},
{
.pos = {2, 173},
.color = {1, 1, 1, 1},
.size = 16,
.offset = 140,
.length = 0,
.font = 0,
},
};
GPUDrawable drawables[] = {
{
.pos = {0, 0},
.size = {150, 175},
.color = {{0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}},
},
};
LayerInput layer = {
.strings = strings,
.num_strings = sizeof(strings)/sizeof(GPUString),
.max_strings = sizeof(strings)/sizeof(GPUString) + 4,
.max_codes = 200,
.drawables = drawables,
.num_drawables = sizeof(drawables)/sizeof(GPUDrawable),
};
ContainerInput container = {
.id = 0x02,
.size = {150, 175},
.offset = {0, 0},
.anchor = ANCHOR_BOTTOM_RIGHT,
.layers = &layer,
.layer_count = 1,
};
return create_container(&container, context->render, context->ui);
}
VkResult region_info_ui(ClientContext* context) {
GPUString strings[] = {
{
.pos = {0, 33},
.color = {1, 1, 1, 1},
.size = 32,
.offset = 0,
.length = 0,
.font = 0,
},
{
.pos = {0, 33 + 1*40},
.color = {1, 1, 1, 1},
.size = 32,
.offset = 11,
.length = 0,
.font = 0,
},
{
.pos = {0, 33 + 2*40},
.color = {1, 1, 1, 1},
.size = 32,
.offset = 18,
.length = 0,
.font = 0,
},
{
.pos = {0, 33 + 3*40},
.color = {1, 1, 1, 1},
.size = 32,
.offset = 25,
.length = 0,
.font = 0,
},
};
GPUDrawable drawables[] = {
{
.pos = {0, 0},
.size = {225, 155},
.color = {{0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}, {0.4, 0.4, 0.4, 0.4}},
},
};
LayerInput layer = {
.strings = strings,
.num_strings = sizeof(strings)/sizeof(GPUString),
.max_strings = sizeof(strings)/sizeof(GPUString) + 4,
.max_codes = 100,
.drawables = drawables,
.num_drawables = sizeof(drawables)/sizeof(GPUDrawable),
};
ContainerInput container = {
.id = 0x01,
.size = {225, 155},
.offset = {0, 0},
.anchor = ANCHOR_TOP_RIGHT,
.layers = &layer,
.layer_count = 1,
};
return create_container(&container, context->render, context->ui);
}
VkResult update_region_info_ui(ClientContext* context) {
char temp[20];
VkResult result;
HexRegion* selected_region = context->hex->regions[context->editor.selected_region];
snprintf(temp,
sizeof(temp),
"Region %4d", context->editor.selected_region);
VK_RESULT(update_ui_string(temp, 0x01, 0, 0, context->ui, context->render));
if(selected_region == NULL) {
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x01, 0, 1, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x01, 0, 2, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x01, 0, 3, context->ui, context->render));
} else {
snprintf(
temp,
sizeof(temp),
"Q: %4d", selected_region->data.q);
VK_RESULT(update_ui_string(temp, 0x01, 0, 1, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"R: %4d", selected_region->data.r);
VK_RESULT(update_ui_string(temp, 0x01, 0, 2, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"Y: %4d", selected_region->data.y);
VK_RESULT(update_ui_string(temp, 0x01, 0, 3, context->ui, context->render));
}
return VK_SUCCESS;
}
VkResult update_hex_info_ui(ClientContext* context) {
char temp[20];
VkResult result;
HexRegion* region = context->hex->regions[context->hex->data.clicked_region];
if(region == NULL) {
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 0, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 1, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 2, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 3, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 4, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 5, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 6, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"");
VK_RESULT(update_ui_string(temp, 0x02, 0, 7, context->ui, context->render));
} else {
GPUHex* hex = &region->data.hexes[context->hex->data.clicked_hex];
snprintf(temp,
sizeof(temp),
"%d-%d",
context->hex->data.clicked_region,
context->hex->data.clicked_hex);
VK_RESULT(update_ui_string(temp, 0x02, 0, 0, context->ui, context->render));
int32_t hex_index = context->hex->data.clicked_hex;
float radius = 0;
float ring = 0;
int side = 0;
if(hex_index != 0) {
radius = floor(0.5 + sqrt(12*hex_index-3)/6);
ring = hex_index - (3*radius*radius - 3*radius + 1);
side = floor(ring/radius);
}
// TODO: fix, these are wrong lmao
int32_t hex_q = hex_starts_qr[side][0]*radius + hex_directions_qr[side][0]*(ring-(radius*side));
int32_t hex_r = hex_starts_qr[side][1]*radius + hex_directions_qr[side][1]*(ring-(radius*side));
int32_t world_q = hex_q + region->data.q*(REGION_SIZE*2 + 1) + region->data.r*REGION_SIZE;
int32_t world_r = hex_r + region->data.q*(REGION_SIZE + 0.5) + region->data.r*(REGION_SIZE*2+1);
snprintf(
temp,
sizeof(temp),
"%d, %d, %d, %d",
world_q,
world_r,
region->data.y,
context->hex->data.clicked_vertex);
VK_RESULT(update_ui_string(temp, 0x02, 0, 1, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%02.02f %02.02f %02.02f",
hex->height[0], hex->height[1], hex->height[2]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 2, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%02.02f %02.02f %02.02f",
hex->height[3], hex->height[4], hex->height[5]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 3, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%08X",
hex->color[0]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 4, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%08X %08X",
hex->color[1], hex->color[2]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 5, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%08X %08X",
hex->color[3], hex->color[4]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 6, context->ui, context->render));
snprintf(
temp,
sizeof(temp),
"%08X %08X",
hex->color[5], hex->color[6]);
VK_RESULT(update_ui_string(temp, 0x02, 0, 7, context->ui, context->render));
}
return VK_SUCCESS;
}
VkResult main_thread(ClientContext* context) {
VkResult result;
VK_RESULT(region_info_ui(context));
VK_RESULT(hex_info_ui(context));
VK_RESULT(color_ui(context));
//
double last_frame_time = 0;
while(glfwWindowShouldClose(context->window) == 0) {
double frame_time = glfwGetTime();
double delta_time = (frame_time - last_frame_time);
// Reset callback variables
context->zoom = 0;
context->cur_spin[0] = 0;
context->cur_spin[1] = 0;
glfwPollEvents();
if(context->editor.last_clicked_region != context->hex->data.clicked_region ||
context->editor.last_clicked_hex != context->hex->data.clicked_hex ||
context->editor.last_clicked_vertex != context->hex->data.clicked_vertex) {
context->editor.last_clicked_region = context->hex->data.clicked_region;
context->editor.last_clicked_hex = context->hex->data.clicked_hex;
context->editor.last_clicked_vertex = context->hex->data.clicked_vertex;
update_hex_info_ui(context);
}
if(context->editor.selected_region != context->editor.last_selected_region) {
context->editor.last_selected_region = context->editor.selected_region;
update_region_info_ui(context);
}
if((context->key_spin[0] != 0 || context->key_spin[1] != 0 ||
context->velocity[0] != 0 || context->velocity[1] != 0 || context->velocity[2] != 0 ||
context->zoom != 0 ||
context->cur_spin[0] != 0 || context->cur_spin[1] != 0 ||
context->render->framebuffer_recreated == true)) {
if(context->render->framebuffer_recreated == true) {
context->render->framebuffer_recreated = false;
VK_RESULT(update_hex_proj(context->render, context->hex));
VK_RESULT(update_ui_context_resolution(context->ui, context->render));
}
context->rotation[0] += (float)context->key_spin[0]*delta_time*context->key_spin_speed;
context->rotation[0] += (float)context->cur_spin[0]*delta_time*context->cur_spin_speed;
if(context->rotation[0] > 2*M_PI) {
context->rotation[0] -= 2*M_PI;
} else if(context->rotation[0] < 0) {
context->rotation[0] += 2*M_PI;
}
context->rotation[1] += (float)context->key_spin[1]*delta_time*context->key_spin_speed;
context->rotation[1] += (float)context->cur_spin[1]*delta_time*context->cur_spin_speed;
if(context->rotation[1] > (M_PI/2 - 0.1)) {
context->rotation[1] = (M_PI/2 - 0.1);
} else if(context->rotation[1] < 0) {
context->rotation[1] = 0;
}
context->position[0] += - context->velocity[2]*context->move_speed*cos(context->rotation[0])
- context->velocity[0]*context->move_speed*sin(context->rotation[0]);
context->position[2] += context->velocity[0]*context->move_speed*cos(context->rotation[0])
- context->velocity[2]*context->move_speed*sin(context->rotation[0]);
context->position[1] += context->velocity[1]*context->move_speed;
context->distance += context->zoom*delta_time*context->zoom_speed;
if(context->distance < 1) {
context->distance = 1;
}
VK_RESULT(update_hex_view(
context->position,
context->rotation,
context->distance,
context->render,
context->hex));
}
//
VkResult result = draw_frame(context->render, context->ui, context->hex, frame_time);
if(result != VK_SUCCESS) {
fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result));
glfwDestroyWindow(context->window);
}
last_frame_time = frame_time;
}
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
(void)scancode;
(void)mods;
ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window);
if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->key != NULL) {
if(context->ui->active_callbacks->key(context->ui, context->render, key, action, mods)) {
return;
}
}
switch(key) {
case GLFW_KEY_A:
if(action == GLFW_PRESS) {
context->key_spin[0] -= 1;
} else if(action == GLFW_RELEASE) {
context->key_spin[0] += 1;
}
break;
case GLFW_KEY_D:
if(action == GLFW_PRESS) {
context->key_spin[0] += 1;
} else if(action == GLFW_RELEASE) {
context->key_spin[0] -= 1;
}
break;
case GLFW_KEY_W:
if(action == GLFW_PRESS) {
context->key_spin[1] += 1;
} else if(action == GLFW_RELEASE) {
context->key_spin[1] -= 1;
}
break;
case GLFW_KEY_S:
if(action == GLFW_PRESS) {
context->key_spin[1] -= 1;
} else if(action == GLFW_RELEASE) {
context->key_spin[1] += 1;
}
break;
case GLFW_KEY_UP:
if(action == GLFW_PRESS) {
context->velocity[2] += 1;
} else if(action == GLFW_RELEASE) {
context->velocity[2] -= 1;
}
break;
case GLFW_KEY_DOWN:
if(action == GLFW_PRESS) {
context->velocity[2] -= 1;
} else if(action == GLFW_RELEASE) {
context->velocity[2] += 1;
}
break;
case GLFW_KEY_LEFT:
if(action == GLFW_PRESS) {
context->velocity[0] -= 1;
} else if(action == GLFW_RELEASE) {
context->velocity[0] += 1;
}
break;
case GLFW_KEY_RIGHT:
if(action == GLFW_PRESS) {
context->velocity[0] += 1;
} else if(action == GLFW_RELEASE) {
context->velocity[0] -= 1;
}
break;
case GLFW_KEY_LEFT_SHIFT:
if(action == GLFW_PRESS) {
context->velocity[1] -= 1;
} else if(action == GLFW_RELEASE) {
context->velocity[1] += 1;
}
break;
case GLFW_KEY_SPACE:
if(action == GLFW_PRESS) {
context->velocity[1] += 1;
} else if(action == GLFW_RELEASE) {
context->velocity[1] -= 1;
}
break;
case GLFW_KEY_EQUAL:
if(action == GLFW_PRESS) {
context->editor.selected_region = add_hex_region(context);
}
break;
case GLFW_KEY_LEFT_BRACKET:
if(action == GLFW_PRESS) {
context->editor.selected_region -= 1;
if(context->editor.selected_region > MAX_LOADED_REGIONS) {
context->editor.selected_region = MAX_LOADED_REGIONS;
}
}
break;
case GLFW_KEY_RIGHT_BRACKET:
if(action == GLFW_PRESS) {
context->editor.selected_region += 1;
if(context->editor.selected_region > MAX_LOADED_REGIONS) {
context->editor.selected_region = 0;
}
}
break;
case GLFW_KEY_I:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.q += 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.q,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, q),
sizeof(uint32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_K:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.q -= 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.q,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, q),
sizeof(uint32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_J:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.r += 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.r,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, r),
sizeof(uint32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_L:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.r -= 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.r,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, r),
sizeof(uint32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_O:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.y += 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.y,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, y),
sizeof(int32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_U:
if(action == GLFW_PRESS) {
if(context->hex->regions[context->editor.selected_region] != NULL) {
context->hex->regions[context->editor.selected_region]->data.y -= 1;
add_transfer(
&context->hex->regions[context->editor.selected_region]->data.y,
context->hex->regions[context->editor.selected_region]->region,
offsetof(GPUHexRegion, y),
sizeof(int32_t),
context->render->current_frame,
context->render);
}
update_region_info_ui(context);
}
break;
case GLFW_KEY_Y:
if(action == GLFW_PRESS) {
HexRegion* region = context->hex->regions[context->hex->data.clicked_region];
if(region != NULL) {
region->data.hexes[context->hex->data.clicked_hex].height[context->hex->data.clicked_vertex-1] += 0.1;
add_transfer(
&region->data.hexes[context->hex->data.clicked_hex].height[context->hex->data.clicked_vertex-1],
region->region,
offsetof(GPUHexRegion, hexes)
+ sizeof(GPUHex)*context->hex->data.clicked_hex
+ offsetof(GPUHex, height)
+ sizeof(float)*(context->hex->data.clicked_vertex-1),
sizeof(float),
context->render->current_frame,
context->render);
update_hex_info_ui(context);
}
}
break;
case GLFW_KEY_H:
if(action == GLFW_PRESS) {
HexRegion* region = context->hex->regions[context->hex->data.clicked_region];
if(region != NULL) {
region->data.hexes[context->hex->data.clicked_hex].height[context->hex->data.clicked_vertex-1] -= 0.1;
add_transfer(
&region->data.hexes[context->hex->data.clicked_hex].height[context->hex->data.clicked_vertex-1],
region->region,
offsetof(GPUHexRegion, hexes)
+ sizeof(GPUHex)*context->hex->data.clicked_hex
+ offsetof(GPUHex, height)
+ sizeof(float)*(context->hex->data.clicked_vertex-1),
sizeof(float),
context->render->current_frame,
context->render);
update_hex_info_ui(context);
}
}
break;
}
}
void button_callback(GLFWwindow* window, int button, int action, int mods) {
double cursor[2];
uint32_t container;
uint32_t layer;
uint32_t element;
vec2 position;
ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window);
glfwGetCursorPos(window, &cursor[0], &cursor[1]);
if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->button != NULL) {
Container* container_ptr = context_container(context->ui->active_container, context->ui);
GPUDrawable* drawable_ptr = &container_ptr->layers[context->ui->active_layer].drawables_buffer[context->ui->active_element];
vec2 element_pos = {
drawable_ptr->pos[0] + container_ptr->data.offset[0],
drawable_ptr->pos[1] + container_ptr->data.offset[1],
};
anchor_offset(context->render, container_ptr, element_pos);
vec2 element_size = {
drawable_ptr->size[0],
drawable_ptr->size[1],
};
vec2 point = {
(cursor[0] - element_pos[0])/element_size[0],
(cursor[1] - element_pos[1])/element_size[1],
};
if(point[0] <= 1 && point[0] >= 0 && point[1] <= 1 && point[1] >= 0) {
context->ui->active_callbacks->button(
context->ui,
context->render,
point[0],
point[1],
button,
action,
mods);
return;
} else {
clear_active_element(context->ui, context->render);
}
}
if(ui_intersect(cursor, context->render, context->ui, UI_EVENT_BUTTON, &container, &layer, &element, position)) {
Container* container_ptr = context_container(container, context->ui);
for(uint32_t c = 0; c < container_ptr->callback_count; c++) {
if(container_ptr->callbacks[c].element == element) {
if(container_ptr->callbacks[c].button != NULL) {
container_ptr->callbacks[c].button(context->ui, context->render, position[0], position[1], button, action, mods);
}
break;
}
}
} else {
}
update_hex_click(cursor, context->hex, context->render);
if(button == GLFW_MOUSE_BUTTON_MIDDLE) {
if(action == GLFW_PRESS) {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
context->camera_mode = true;
} else {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
context->camera_mode = false;
}
}
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
(void)xoffset;
uint32_t container;
uint32_t layer;
uint32_t element;
vec2 position;
ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window);
if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->scroll != NULL) {
context->ui->active_callbacks->scroll(context->ui, context->render, xoffset, yoffset);
} else if(ui_intersect(
context->ui->cursor,
context->render,
context->ui,
UI_EVENT_SCROLL,
&container,
&layer,
&element,
position)) {
Container* container_ptr = context_container(container, context->ui);
for(uint32_t c = 0; c < container_ptr->callback_count; c++) {
if(container_ptr->callbacks[c].element == element) {
if(container_ptr->callbacks[c].scroll != NULL) {
container_ptr->callbacks[c].scroll(context->ui, context->render, xoffset, yoffset);
}
break;
}
}
} else {
context->zoom = -yoffset;
}
}
void cursor_callback(GLFWwindow* window, double xpos, double ypos) {
ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window);
uint32_t container;
uint32_t element;
uint32_t layer;
vec2 position;
vec2 last_cursor = {
context->ui->cursor[0],
context->ui->cursor[1],
};
context->ui->cursor[0] = xpos;
context->ui->cursor[1] = ypos;
if(context->camera_mode == true) {
context->cur_spin[0] = (xpos - last_cursor[0])/context->render->swapchain_extent.width*-100;
context->cur_spin[1] = (ypos - last_cursor[1])/context->render->swapchain_extent.height*100;
} else {
if(context->ui->active_callbacks != NULL && context->ui->active_callbacks->cursor != NULL) {
Container* container_ptr = context_container(context->ui->active_container, context->ui);
GPUDrawable* drawable_ptr = &container_ptr->layers[context->ui->active_layer].drawables_buffer[context->ui->active_element];
vec2 element_pos = {
drawable_ptr->pos[0] + container_ptr->data.offset[0],
drawable_ptr->pos[1] + container_ptr->data.offset[1],
};
anchor_offset(context->render, container_ptr, element_pos);
vec2 element_size = {
drawable_ptr->size[0],
drawable_ptr->size[1],
};
context->ui->active_callbacks->cursor(
context->ui,
context->render,
(context->ui->cursor[0] - element_pos[0])/element_size[0],
(context->ui->cursor[1] - element_pos[1])/element_size[1]);
} else if(ui_intersect(context->ui->cursor, context->render, context->ui, UI_EVENT_CURSOR, &container, &layer, &element, position)) {
Container* container_ptr = context_container(container, context->ui);
for(uint32_t c = 0; c < container_ptr->callback_count; c++) {
if(container_ptr->callbacks[c].element == element) {
if(container_ptr->callbacks[c].cursor != NULL) {
container_ptr->callbacks[c].cursor(context->ui, context->render, position[0], position[1]);
}
break;
}
}
} else {
update_hex_hover(context->ui->cursor, context->hex, context->render);
}
}
}
int main() {
ClientContext context = {
.render = malloc(sizeof(RenderContext)),
.ui = malloc(sizeof(UIContext)),
.hex = malloc(sizeof(HexContext)),
.window = init_window(),
.position = {0, 0, 0},
.rotation = {3*M_PI/2, M_PI/4},
.distance = 25,
.key_spin_speed = 1.0,
.cur_spin_speed = 1.0,
.zoom_speed = 1.0,
.move_speed = 0.1,
.editor = {
.selected_region = MAX_LOADED_REGIONS,
.last_selected_region = 0,
.last_clicked_region = MAX_LOADED_REGIONS,
},
};
if(context.window == NULL || context.render == NULL || context.ui == NULL || context.hex == NULL) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
memset(context.render, 0, sizeof(RenderContext));
memset(context.ui, 0, sizeof(UIContext));
memset(context.hex, 0, sizeof(HexContext));
glfwSetWindowUserPointer(context.window, &context);
glfwSetKeyCallback(context.window, key_callback);
glfwSetMouseButtonCallback(context.window, button_callback);
glfwSetScrollCallback(context.window, scroll_callback);
glfwSetCursorPosCallback(context.window, cursor_callback);
int error;
VkResult result;
VK_RESULT(init_vulkan(context.window, context.render));
// TODO: make # of fonts/textures/containers scaling, recreate GPU buffers as necessary
VK_RESULT(create_ui_context(10, 10, 10, context.render, context.ui));
VK_RESULT(create_hex_context(context.render, context.hex));
pthread_t network_thread_handle;
error = pthread_create(&network_thread_handle, NULL, &network_thread, &context);
if(error != 0) {
return error;
}
error = main_thread(&context);
if(error != 0) {
return error;
}
error = pthread_join(network_thread_handle, NULL);
if(error != 0) {
return error;
}
return 0;
}