diff --git a/client/include/hex.h b/client/include/hex.h index 6e7ab1d..4ba3065 100644 --- a/client/include/hex.h +++ b/client/include/hex.h @@ -3,9 +3,15 @@ #include "gpu.h" -#define REGION_SIZE (10) +#define REGION_SIZE 10 #define REGION_HEX_COUNT (3*REGION_SIZE*(REGION_SIZE-1)+1) #define MAX_LOADED_REGIONS (51*51) +#define HEX_X 0.75 +#define SQRT3 1.732050807568877193176604123436845839023590087890625 +#define HEX_Z (SQRT3/2) +#define REGION_DIAMETER (2*REGION_SIZE-1) +#define REGION_WIDTH (HEX_X*REGION_DIAMETER) +#define REGION_HEIGHT (HEX_Z*REGION_DIAMETER) typedef struct GPUHexStruct { float height[6]; @@ -53,7 +59,7 @@ typedef struct HexContextStruct { GraphicsPipeline ray_pipeline; GraphicsPipeline highlight_pipeline; - HexRegion regions[MAX_LOADED_REGIONS]; + HexRegion* regions[MAX_LOADED_REGIONS]; GPUHexContext data; } HexContext; diff --git a/client/shader/hex_highlight.vert b/client/shader/hex_highlight.vert index 6adff12..45ba1c9 100644 --- a/client/shader/hex_highlight.vert +++ b/client/shader/hex_highlight.vert @@ -33,7 +33,7 @@ void main() { if(hex_index != 0) { radius = floor(0.5 + sqrt(12*hex_index-3)/6); ring = hex_index - (3*radius*radius - 3*radius + 1); - side = int(floor(ring/(radius))); + side = int(floor(ring/radius)); } vec4 position = vertices[indices[gl_VertexIndex]] diff --git a/client/src/hex.c b/client/src/hex.c index b0d10ce..fd1fa3d 100644 --- a/client/src/hex.c +++ b/client/src/hex.c @@ -497,8 +497,9 @@ VkResult set_hex_region(int32_t q, int32_t r, int32_t y, uint32_t map, HexRegion uint32_t i = 0; for(; i < MAX_LOADED_REGIONS; i++) { - if(hex->regions[i].address == 0) { - *region = &hex->regions[i]; + if(hex->regions[i] == NULL) { + hex->regions[i] = malloc(sizeof(GPUHexRegion)); + *region = hex->regions[i]; break; } } diff --git a/client/src/main.c b/client/src/main.c index 929e649..d3deceb 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -18,6 +18,7 @@ #include #define max(a, b) ((a > b) ? a : b) +#define min(a, b) ((a < b) ? a : b) typedef struct ClientContextStruct { GLFWwindow* window; @@ -55,7 +56,157 @@ void* network_thread(void* data) { return NULL; } -vec3 zero = {0, 0, 0}; +// cos(I*PI/3)/2, sin(I*PI/3)/2 +vec3 hex_vertices[] = { + { 1.0/2, 0, 0}, + { 1.0/4, 0, SQRT3/4}, + {-1.0/4, 0, SQRT3/4}, + {-1.0/2, 0, 0}, + {-1.0/4, 0, -SQRT3/4}, + { 1.0/4, 0, -SQRT3/4}, +}; + +vec3 hex_starts[] = { + { 0, 0, HEX_Z}, + {-HEX_X, 0, HEX_Z/2}, + {-HEX_X, 0, -HEX_Z/2}, + { 0, 0, -HEX_Z}, + { HEX_X, 0, -HEX_Z/2}, + { HEX_X, 0, HEX_Z/2}, +}; + +vec3 hex_directions[] = { + {-HEX_X, 0, -HEX_Z/2}, + { 0, 0, -HEX_Z}, + { HEX_X, 0, -HEX_Z/2}, + { HEX_X, 0, HEX_Z/2}, + { 0, 0, HEX_Z}, + {-HEX_X, 0, HEX_Z/2}, +}; + +int hex_indices[] = { + 0, 2, 1, + 0, 3, 2, + 0, 4, 3, + 0, 5, 4, + 0, 6, 5, + 0, 1, 6, +}; + +bool ray_hex_intersect(float* distance, vec4 ray_start, vec4 ray_end, uint32_t region_index, uint32_t hex_index, HexContext* context) { + /* + Ray-hexagon intersection + 1. For each triangle in the hexagon, check for intersection + 2. If intersections, return the closest along the ray + */ + GPUHexRegion* region = &context->regions[region_index]->data; + GPUHex* hex = ®ion->hexes[hex_index]; + + vec3 start; + start[0] = ray_start[0]/ray_start[3]; + start[1] = ray_start[1]/ray_start[3]; + start[2] = ray_start[2]/ray_start[3]; + + vec3 end; + end[0] = ray_end[0]/ray_end[3]; + end[1] = ray_end[1]/ray_end[3]; + end[2] = ray_end[2]/ray_end[3]; + + vec3 dir; + dir[0] = end[0] - start[0]; + dir[1] = end[1] - start[1]; + dir[2] = end[2] - start[2]; + + + float dir_len = glm_vec3_norm(dir); + glm_vec3_divs(dir, dir_len, dir); + + vec3 region_offset = { + ((float)region->q + (float)region->r/2)*REGION_WIDTH - region->r*HEX_X/2, + 0, + 0.75*region->r*REGION_HEIGHT + 0.25*region->r*HEX_Z + 0.5*region->q*HEX_Z, + }; + + float center_height = (hex->height[0] + + hex->height[1] + + hex->height[2] + + hex->height[3] + + hex->height[4] + + hex->height[5])/6; + + vec3 vertices[7] = { + {0, 0, 0}, + }; + glm_vec3_add(vertices[0], region_offset, vertices[0]); + glm_vec3_add(hex_vertices[0], region_offset, vertices[1]); + glm_vec3_add(hex_vertices[1], region_offset, vertices[2]); + glm_vec3_add(hex_vertices[2], region_offset, vertices[3]); + glm_vec3_add(hex_vertices[3], region_offset, vertices[4]); + glm_vec3_add(hex_vertices[4], region_offset, vertices[5]); + glm_vec3_add(hex_vertices[5], region_offset, vertices[6]); + + vertices[0][1] += center_height; + vertices[1][1] += hex->height[0]; + vertices[2][1] += hex->height[1]; + vertices[3][1] += hex->height[2]; + vertices[4][1] += hex->height[3]; + vertices[5][1] += hex->height[4]; + vertices[6][1] += hex->height[5]; + + vec3 hex_offset = {0, 0, 0}; + 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); + } + + glm_vec3_muladds(hex_starts[side], radius, hex_offset); + glm_vec3_muladds(hex_directions[side], ring-(radius*side), hex_offset); + for(uint32_t vertex = 0; vertex < 7; vertex++) { + glm_vec3_add(vertices[vertex], hex_offset, vertices[vertex]); + } + + *distance = INFINITY; + bool intersect = false; + + for(uint32_t triangle = 0; triangle < 6; triangle++) { + vec3 vert[3]; + for(int v_i = 0; v_i < 3; v_i++) { + vert[v_i][0] = vertices[hex_indices[triangle*3+v_i]][0]; + vert[v_i][1] = vertices[hex_indices[triangle*3+v_i]][1]; + vert[v_i][2] = vertices[hex_indices[triangle*3+v_i]][2]; + } + + vec3 v0v1; + glm_vec3_sub(vert[1], vert[0], v0v1); + vec3 v0v2; + glm_vec3_sub(vert[2], vert[0], v0v2); + vec3 pvec; + glm_vec3_cross(dir, v0v2, pvec); + float det = glm_vec3_dot(v0v1, pvec); + + float det_inv = 1/det; + + vec3 t; + glm_vec3_sub(start, vert[0], t); + float u = glm_vec3_dot(t, pvec) * det_inv; + if(u < 0 || u > 1) continue; + + vec3 q; + glm_vec3_cross(t, v0v1, q); + float v = glm_vec3_dot(dir, q) * det_inv; + if(v < 0 || (u+v) > 1) continue; + + intersect = true; + *distance = min(*distance, glm_vec3_dot(v0v2, q) * det_inv); + } + + return intersect; +} + vec3 up = {0, 1, 0}; VkResult main_thread(ClientContext* context) { @@ -103,20 +254,18 @@ VkResult main_thread(ClientContext* context) { add_transfer(&context->hex->data.current_map, context->hex->context, offsetof(GPUHexContext, current_map), sizeof(uint32_t), context->render->current_frame, context->render); uint32_t region = 0; - for(int32_t y = -2; y < 2; y++) { - for(int32_t q = -5; q < 5; q++) { - for(int32_t r = -5; r < 5; r++) { - VK_RESULT(set_hex_region(q, r, y, 0x01, ®ions[region], context->hex, context->render)); - for(uint32_t i = 0; i < REGION_HEX_COUNT; i++) { - for(uint32_t h = 0; h < 6; h++) { - regions[region]->data.hexes[i].color[h] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))]; - regions[region]->data.hexes[i].height[h] = (float)i/REGION_HEX_COUNT; - } - regions[region]->data.hexes[i].color[6] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))]; + for(int32_t q = -5; q < 5; q++) { + for(int32_t r = -5; r < 5; r++) { + VK_RESULT(set_hex_region(q, r, 0, 0x01, ®ions[region], context->hex, context->render)); + for(uint32_t i = 0; i < REGION_HEX_COUNT; i++) { + for(uint32_t h = 0; h < 6; h++) { + regions[region]->data.hexes[i].color[h] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))]; + regions[region]->data.hexes[i].height[h] = (float)i/REGION_HEX_COUNT; } - VK_RESULT(add_transfer(®ions[region]->data.hexes, regions[region]->region, offsetof(GPUHexRegion, hexes), sizeof(GPUHex)*REGION_HEX_COUNT, 0, context->render)); - region++; + regions[region]->data.hexes[i].color[6] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))]; } + VK_RESULT(add_transfer(®ions[region]->data.hexes, regions[region]->region, offsetof(GPUHexRegion, hexes), sizeof(GPUHex)*REGION_HEX_COUNT, 0, context->render)); + region++; } } @@ -127,7 +276,6 @@ VkResult main_thread(ClientContext* context) { int frame = 0; double last_frame_time = 0; - double test = glfwGetTime(); while(glfwWindowShouldClose(context->window) == 0) { double frame_time = glfwGetTime(); double delta_time = (frame_time - last_frame_time); @@ -140,19 +288,7 @@ VkResult main_thread(ClientContext* context) { context->zoom = 0; context->cur_spin[0] = 0; context->cur_spin[1] = 0; - glfwPollEvents(); - if((frame_time - test) > 0.05) { - test = frame_time; - context->hex->data.clicked_region = 210; - context->hex->data.hovered_region = 210; - context->hex->data.hovered_hex = context->hex->data.clicked_hex; - context->hex->data.clicked_hex += 1; - if(context->hex->data.clicked_hex >= REGION_HEX_COUNT) { - context->hex->data.clicked_hex = 0; - } - add_transfer(&context->hex->data.clicked_region, context->hex->context, offsetof(GPUHexContext, clicked_region), 2*sizeof(uint32_t) + 2*sizeof(int32_t), context->render->current_frame, context->render); - } if((context->key_spin[0] != 0 || context->key_spin[1] != 0 || context->zoom != 0 || @@ -292,16 +428,21 @@ bool contains(double* point, GPUContainer* container, GPUDrawable* rect) { } void cursor_to_world_ray(ClientContext* context, double cursor[2], vec4 start, vec4 end) { + double cursor_scaled[2] = { + 2*(cursor[0]*context->render->window_scale[0]/context->render->swapchain_extent.width - 0.5), + 2*(cursor[1]*context->render->window_scale[1]/context->render->swapchain_extent.height - 0.5), + }; + vec4 transformed_start = { - cursor[0]*context->render->window_scale[0]*2/context->render->swapchain_extent.width - 1, - cursor[1]*context->render->window_scale[1]*2/context->render->swapchain_extent.height - 1, - 0, - 1.0 + cursor_scaled[0], + cursor_scaled[1], + PERSPECTIVE_NEARZ, + 1.0, }; vec4 transformed_end = { - PERSPECTIVE_FARZ*(cursor[0]*context->render->window_scale[0]*2/context->render->swapchain_extent.width - 1), - PERSPECTIVE_FARZ*(cursor[1]*context->render->window_scale[1]*2/context->render->swapchain_extent.height - 1), + PERSPECTIVE_FARZ*cursor_scaled[0], + PERSPECTIVE_FARZ*cursor_scaled[1], PERSPECTIVE_FARZ, PERSPECTIVE_FARZ, }; @@ -333,7 +474,25 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) cursor_to_world_ray(context, cursor, context->hex->data.click_start, context->hex->data.click_end); add_transfer(&context->hex->data.click_start, context->hex->context, offsetof(GPUHexContext, click_start), sizeof(vec4)*2, context->render->current_frame, context->render); - for(int32_t c = context->ui->max_containers - 1; c >= 0; c--) { + // Hex intersections + float distance; + for(uint32_t r = 0; r < MAX_LOADED_REGIONS; r++) { + if(context->hex->regions[r] == NULL) { + continue; + } else if(context->hex->regions[r]->data.map != context->hex->data.current_map) { + continue; + } + for(uint32_t h = 0; h < REGION_HEX_COUNT; h++) { + if(ray_hex_intersect(&distance, context->hex->data.click_start, context->hex->data.click_end, r, h, context->hex)) { + context->hex->data.clicked_region = r; + context->hex->data.clicked_hex = h; + add_transfer(&context->hex->data.clicked_region, context->hex->context, offsetof(GPUHexContext, clicked_region), sizeof(uint32_t)*2, context->render->current_frame, context->render); + } + } + } + + // UI intersections + for(uint32_t c = 0; c < context->ui->max_containers; c++) { if(context->ui->containers[c].id == 0x00000000) { continue; } @@ -375,6 +534,7 @@ void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { context->cursor[1] = ypos; cursor_to_world_ray(context, context->cursor, context->hex->data.hover_start, context->hex->data.hover_end); add_transfer(&context->hex->data.hover_start, context->hex->context, offsetof(GPUHexContext, hover_start), sizeof(vec4)*2, context->render->current_frame, context->render); + } int main() { @@ -395,6 +555,10 @@ int main() { 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); @@ -404,7 +568,8 @@ int main() { 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));