#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 ClientContextStruct { GLFWwindow* window; RenderContext* render; UIContext* ui; HexContext* hex; uint32_t clicked_container; uint32_t clicked_element; double cursor[2]; 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; uint32_t selected_region; uint32_t last_selected_region; } 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; } char region_info_strs[][20] = { "Region XXXX", "Q: XXXX", "R: XXXX", "Y: XXXX", }; uint32_t region_info_str_off[] = { 0, 11, 18, 25, }; VkResult region_info_ui(ClientContext* context) { GPUString strings[] = { { .pos = {0, 33}, .color = {1, 1, 1, 1}, .size = 32, .offset = region_info_str_off[0], .length = strlen(region_info_strs[0]), .font = 0, }, { .pos = {0, 33 + 1*40}, .color = {1, 1, 1, 1}, .size = 32, .offset = region_info_str_off[1], .length = strlen(region_info_strs[1]), .font = 0, }, { .pos = {0, 33 + 2*40}, .color = {1, 1, 1, 1}, .size = 32, .offset = region_info_str_off[2], .length = strlen(region_info_strs[2]), .font = 0, }, { .pos = {0, 33 + 3*40}, .color = {1, 1, 1, 1}, .size = 32, .offset = region_info_str_off[3], .length = strlen(region_info_strs[3]), .font = 0, }, }; uint32_t codes[43/4]; VkResult result; VK_RESULT(map_string(region_info_strs[0], codes, region_info_str_off[0], 0, context->ui)); VK_RESULT(map_string(region_info_strs[1], codes, region_info_str_off[1], 0, context->ui)); VK_RESULT(map_string(region_info_strs[2], codes, region_info_str_off[2], 0, context->ui)); VK_RESULT(map_string(region_info_strs[3], codes, region_info_str_off[3], 0, context->ui)); GPUDrawable drawables[] = { { .pos = {0, 0}, .size = {225, 155}, .color = {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, .codes = codes, .num_codes = sizeof(codes)/sizeof(uint32_t), .max_codes = sizeof(codes)/sizeof(uint32_t) + 100, .drawables = drawables, .num_drawables = sizeof(drawables)/sizeof(GPUDrawable), }; ContainerInput region_info = { .id = 0x01, .size = {225, 155}, .offset = {0, 0}, .anchor = ANCHOR_TOP_RIGHT, .layers = &layer, .layer_count = 1, }; return create_container(®ion_info, context->render, context->ui); } VkResult update_region_info_ui(ClientContext* context) { VkResult result; HexRegion* selected_region = context->hex->regions[context->selected_region]; snprintf(region_info_strs[0], sizeof(region_info_strs[0]), "Region %4d", context->selected_region); VK_RESULT(update_ui_string(region_info_strs[0], 0, 0, 0, context->ui, context->render)); if(selected_region == NULL) { snprintf( region_info_strs[1], sizeof(region_info_strs[1]), ""); VK_RESULT(update_ui_string(region_info_strs[1], 0, 0, 1, context->ui, context->render)); snprintf( region_info_strs[2], sizeof(region_info_strs[2]), ""); VK_RESULT(update_ui_string(region_info_strs[2], 0, 0, 2, context->ui, context->render)); snprintf( region_info_strs[3], sizeof(region_info_strs[3]), ""); VK_RESULT(update_ui_string(region_info_strs[3], 0, 0, 3, context->ui, context->render)); } else { snprintf( region_info_strs[1], sizeof(region_info_strs[1]), "Q: %4d", selected_region->data.q); VK_RESULT(update_ui_string(region_info_strs[1], 0, 0, 1, context->ui, context->render)); snprintf( region_info_strs[2], sizeof(region_info_strs[2]), "R: %4d", selected_region->data.r); VK_RESULT(update_ui_string(region_info_strs[2], 0, 0, 2, context->ui, context->render)); snprintf( region_info_strs[3], sizeof(region_info_strs[3]), "Y: %4d", selected_region->data.y); VK_RESULT(update_ui_string(region_info_strs[3], 0, 0, 3, context->ui, context->render)); } return VK_SUCCESS; } VkResult main_thread(ClientContext* context) { VkResult result; VK_RESULT(region_info_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->clicked_element = 0x00000000; context->clicked_container = 0x00000000; context->zoom = 0; context->cur_spin[0] = 0; context->cur_spin[1] = 0; glfwPollEvents(); if(context->selected_region != context->last_selected_region) { context->last_selected_region = context->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) { ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); (void)scancode; (void)mods; 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->selected_region = add_hex_region(context); } break; case GLFW_KEY_LEFT_BRACKET: if(action == GLFW_PRESS) { context->selected_region -= 1; if(context->selected_region > MAX_LOADED_REGIONS) { context->selected_region = MAX_LOADED_REGIONS; } } break; case GLFW_KEY_RIGHT_BRACKET: if(action == GLFW_PRESS) { context->selected_region += 1; if(context->selected_region > MAX_LOADED_REGIONS) { context->selected_region = 0; } } break; case GLFW_KEY_I: if(action == GLFW_PRESS) { if(context->hex->regions[context->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.q += 1; add_transfer( &context->hex->regions[context->selected_region]->data.q, context->hex->regions[context->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->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.q -= 1; add_transfer( &context->hex->regions[context->selected_region]->data.q, context->hex->regions[context->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->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.r += 1; add_transfer( &context->hex->regions[context->selected_region]->data.r, context->hex->regions[context->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->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.r -= 1; add_transfer( &context->hex->regions[context->selected_region]->data.r, context->hex->regions[context->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->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.y += 1; add_transfer( &context->hex->regions[context->selected_region]->data.y, context->hex->regions[context->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->selected_region] != NULL) { context->hex->regions[context->selected_region]->data.y -= 1; add_transfer( &context->hex->regions[context->selected_region]->data.y, context->hex->regions[context->selected_region]->region, offsetof(GPUHexRegion, y), sizeof(int32_t), context->render->current_frame, context->render); } update_region_info_ui(context); } break; } } bool contains(double* point, GPUContainer* container, GPUDrawable* rect) { vec2 pos = { container->offset[0] + rect->pos[0], container->offset[1] + rect->pos[1], }; return (point[0] >= pos[0] && point[0] <= pos[0] + rect->size[0]) && (point[1] >= pos[1] && point[1] <= pos[1] + rect->size[1]) && (point[0] >= container->offset[0] && point[0] <= container->offset[0] + container->size[0]) && (point[1] >= container->offset[1] && point[1] <= container->offset[1] + container->size[1]); } void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); (void)mods; (void)context; double cursor[2]; glfwGetCursorPos(window, &cursor[0], &cursor[1]); switch(button) { // Handle camera hover case GLFW_MOUSE_BUTTON_RIGHT: 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; } break; case GLFW_MOUSE_BUTTON_LEFT: if(action == GLFW_PRESS) { // Hex intersection update_hex_click(cursor, context->hex, context->render); // UI intersections for(uint32_t c = 0; c < context->ui->max_containers; c++) { if(context->ui->containers[c].id == 0x00000000) { continue; } for(uint32_t l = 0; l < context->ui->containers[c].layer_count; l++) { for(uint32_t d = 0; d < context->ui->containers[c].layers[l].data.num_drawables; d++) { if(context->ui->containers[c].layers[l].drawables_buffer[d].id == 0x00000000) { continue; } if(contains( cursor, &context->ui->containers[c].data, &context->ui->containers[c].layers[l].drawables_buffer[d])) { context->clicked_container = context->ui->containers[c].id; context->clicked_element = context->ui->containers[c].layers[l].drawables_buffer[d].id; } } } } } break; } } void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); (void)xoffset; context->zoom = -yoffset; } void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { ClientContext* context = (ClientContext*)glfwGetWindowUserPointer(window); if(context->camera_mode == true) { context->cur_spin[0] = (xpos - context->cursor[0])/context->render->swapchain_extent.width*-100; context->cur_spin[1] = (ypos - context->cursor[1])/context->render->swapchain_extent.height*100; context->cursor[0] = xpos; context->cursor[1] = ypos; } else { context->cursor[0] = xpos; context->cursor[1] = ypos; update_hex_hover(context->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, .selected_region = MAX_LOADED_REGIONS, .last_selected_region = 0, }; 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, mouse_button_callback); glfwSetScrollCallback(context.window, scroll_callback); glfwSetCursorPosCallback(context.window, cursor_pos_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; }