#include "GLFW/glfw3.h" #include #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 #include 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, ®ion, 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 = ®ion->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( ®ion->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( ®ion->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; }