diff --git a/client/include/hex.h b/client/include/hex.h index b6c1595..23b7043 100644 --- a/client/include/hex.h +++ b/client/include/hex.h @@ -53,8 +53,10 @@ typedef struct GPUHexContextStruct { uint32_t current_map; uint32_t clicked_region; uint32_t clicked_hex; + uint32_t clicked_vertex; uint32_t hovered_region; uint32_t hovered_hex; + uint32_t hovered_vertex; VkDeviceAddress regions[MAX_LOADED_REGIONS]; } GPUHexContext; @@ -67,6 +69,7 @@ typedef struct HexContextStruct { GraphicsPipeline graphics; GraphicsPipeline highlight_pipeline; + GraphicsPipeline point_pipeline; #ifdef DRAW_HEX_RAYS GraphicsPipeline ray_pipeline; #endif @@ -98,6 +101,7 @@ VkResult allocate_hex_region( bool ray_world_intersect( float* distance, + uint32_t* vertex, uint32_t* rid, uint32_t* hid, vec4 ray_start, diff --git a/client/shader/hex_common.glsl b/client/shader/hex_common.glsl index 6e19dca..da6d929 100644 --- a/client/shader/hex_common.glsl +++ b/client/shader/hex_common.glsl @@ -20,9 +20,11 @@ layout(std430, buffer_reference) readonly buffer HexContext { vec4 hover_end; uint current_map; uint clicked_region; - int clicked_hex; + uint clicked_hex; + uint clicked_vertex; uint hovered_region; - int hovered_hex; + uint hovered_hex; + uint hovered_vertex; Region regions[]; }; diff --git a/client/shader/hex_highlight.vert b/client/shader/hex_highlight.vert index 815b5e7..313e965 100644 --- a/client/shader/hex_highlight.vert +++ b/client/shader/hex_highlight.vert @@ -6,19 +6,17 @@ layout(location = 0) flat out vec4 color; void main() { - int hex_index; + uint hex_index; Region region; - float raise; + float raise = 0.01; if(gl_InstanceIndex == 0) { hex_index = pc.context.clicked_hex; region = pc.context.regions[pc.context.clicked_region]; color = vec4(0.5, 0.5, 0.5, 0.3); - raise = 0.015; } else { hex_index = pc.context.hovered_hex; region = pc.context.regions[pc.context.hovered_region]; color = vec4(0.25, 0.25, 0.25, 0.3); - raise = 0.01; } vec2 region_qr = vec2(region.q, region.r); diff --git a/client/shader/point.frag b/client/shader/point.frag new file mode 100644 index 0000000..66d8420 --- /dev/null +++ b/client/shader/point.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 color; +layout(location = 0) flat in vec4 color_in; + +void main() { + if (length(gl_PointCoord - vec2(0.5)) > 0.5) { + discard; + } + + color = color_in; +} diff --git a/client/shader/point.vert b/client/shader/point.vert new file mode 100644 index 0000000..0bc59ff --- /dev/null +++ b/client/shader/point.vert @@ -0,0 +1,54 @@ +#version 450 +#extension GL_EXT_buffer_reference : require + +#include "hex_common.glsl" + +layout(location = 0) flat out vec4 color; + +void main() { + uint hex_index; + uint vertex_index; + Region region; + float raise; + + + if(gl_InstanceIndex == 0) { + hex_index = pc.context.clicked_hex; + region = pc.context.regions[pc.context.clicked_region]; + vertex_index = pc.context.clicked_vertex; + color = vec4(1, 0, 0, 1); + gl_PointSize = 15; + raise = 0.01; + } else { + hex_index = pc.context.hovered_hex; + region = pc.context.regions[pc.context.hovered_region]; + vertex_index = pc.context.hovered_vertex; + color = vec4(0, 1, 0, 1); + gl_PointSize = 10; + raise = 0.015; + } + + vec2 region_qr = vec2(region.q, region.r); + + vec4 region_pos = vec4(0, 0, 0, 0); + region_pos.x = (region_qr.x+region_qr.y/2)*region_width - region_qr.y*x/2; + region_pos.z = 0.75*region_qr.y*region_height + 0.25*region_qr.y*z + 0.5*region_qr.x*z; + + 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 = int(floor(ring/radius)); + } + + vec4 position = vertices[vertex_index] + + (starts[side]*radius) + + (direction[side]*(ring-(radius*side))) + + region_pos; + + position.y = region.hexes[hex_index].heights[vertex_index-1] + region.y + raise; + + gl_Position = pc.context.proj * pc.context.view * position; +} diff --git a/client/src/draw.c b/client/src/draw.c index e82f18d..3fc8bcf 100644 --- a/client/src/draw.c +++ b/client/src/draw.c @@ -32,8 +32,11 @@ void record_hex_draw(VkCommandBuffer command_buffer, HexContext* hex, double tim vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->graphics.pipeline); vkCmdDraw(command_buffer, 18, REGION_HEX_COUNT*MAX_LOADED_REGIONS, 0, 0); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->point_pipeline.pipeline); + vkCmdDraw(command_buffer, 6, 2, 0, 0); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->highlight_pipeline.pipeline); - vkCmdDraw(command_buffer, 18, 2, 0, 0); + vkCmdDraw(command_buffer, 1, 2, 0, 0); #ifdef DRAW_HEX_RAYS vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->ray_pipeline.pipeline); diff --git a/client/src/hex.c b/client/src/hex.c index 3ca9dd3..ebc6dda 100644 --- a/client/src/hex.c +++ b/client/src/hex.c @@ -41,6 +41,166 @@ int hex_indices[] = { 0, 1, 6, }; +VkResult create_point_pipeline( + RenderContext* gpu, + GraphicsPipeline* pipeline) { + VkResult result; + + VkShaderModule vert_shader = load_shader_file("shader/point.vert.spv", gpu->device); + VkShaderModule frag_shader = load_shader_file("shader/point.frag.spv", gpu->device); + + VkPipelineShaderStageCreateInfo stages[] = { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .pName = "main", + .module = vert_shader, + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .pName = "main", + .module = frag_shader, + }, + }; + + VkPushConstantRange push = { + .size = sizeof(HexPushConstant), + .offset = 0, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + }; + + VkPipelineLayoutCreateInfo layout_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pPushConstantRanges = &push, + .pushConstantRangeCount = 1, + }; + + VK_RESULT(vkCreatePipelineLayout(gpu->device, &layout_info, NULL, &pipeline->layout)); + + VkPipelineVertexInputStateCreateInfo vertex_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + }; + + VkPipelineInputAssemblyStateCreateInfo input_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + .primitiveRestartEnable = VK_FALSE, + }; + + VkViewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = (float)(100), + .height = (float)(100), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + VkRect2D scissor = { + .offset = { + .x = 0, + .y = 0, + }, + .extent = { + .width = 100, + .height = 100, + }, + }; + + VkPipelineViewportStateCreateInfo viewport_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor, + }; + + VkPipelineRasterizationStateCreateInfo raster_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .lineWidth = 1.0f, + .cullMode = VK_CULL_MODE_BACK_BIT, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + }; + + VkPipelineColorBlendAttachmentState blend_attachments = { + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + }; + + VkPipelineColorBlendStateCreateInfo blend_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &blend_attachments, + }; + + VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + VkPipelineDynamicStateCreateInfo dynamic_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = sizeof(dynamic_states)/sizeof(VkDynamicState), + .pDynamicStates = dynamic_states, + }; + + VkPipelineMultisampleStateCreateInfo multisample_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .sampleShadingEnable = VK_FALSE, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .minSampleShading = 1.0f, + .pSampleMask = 0, + .alphaToCoverageEnable = VK_FALSE, +.alphaToOneEnable = VK_FALSE, + }; + + VkPipelineDepthStencilStateCreateInfo depth_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS, + }; + + VkGraphicsPipelineCreateInfo graphics_pipeline_info = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .layout = pipeline->layout, + .stageCount = sizeof(stages)/sizeof(VkPipelineShaderStageCreateInfo), + .pStages = stages, + .pVertexInputState = &vertex_info, + .pInputAssemblyState = &input_info, + .pViewportState = &viewport_info, + .pRasterizationState = &raster_info, + .pColorBlendState = &blend_info, + .pDynamicState = &dynamic_info, + .pMultisampleState = &multisample_info, + .pDepthStencilState = &depth_info, + .renderPass = gpu->render_pass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + }; + + VK_RESULT(vkCreateGraphicsPipelines( + gpu->device, + VK_NULL_HANDLE, + 1, &graphics_pipeline_info, + NULL, + &pipeline->pipeline)); + return VK_SUCCESS; +} + VkResult create_ray_pipeline( RenderContext* gpu, GraphicsPipeline* pipeline) { @@ -531,6 +691,7 @@ VkResult create_hex_context( VK_RESULT(create_ray_pipeline(gpu, &context->ray_pipeline)); #endif VK_RESULT(create_hex_highlight_pipeline(gpu, &context->highlight_pipeline)); + VK_RESULT(create_point_pipeline(gpu, &context->point_pipeline)); memset(&context->data, 0, sizeof(GPUHexContext)); glm_perspective( @@ -636,6 +797,7 @@ VkResult allocate_hex_region( bool ray_hex_intersect( float* distance, + uint32_t* vertex, vec3 start, vec3 dir, vec3 region_offset, @@ -685,39 +847,41 @@ bool ray_hex_intersect( glm_vec3_add(vertices[vertex], hex_offset, vertices[vertex]); } - *distance = INFINITY; bool intersect = false; + vec3 t; + glm_vec3_sub(start, vertices[0], t); + 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 vert[2]; + for(int v_i = 0; v_i < 2; v_i++) { + vert[v_i][0] = vertices[hex_indices[triangle*3+v_i + 1]][0]; + vert[v_i][1] = vertices[hex_indices[triangle*3+v_i + 1]][1]; + vert[v_i][2] = vertices[hex_indices[triangle*3+v_i + 1]][2]; } vec3 v0v1; - glm_vec3_sub(vert[1], vert[0], v0v1); + glm_vec3_sub(vert[0], vertices[0], v0v1); vec3 v0v2; - glm_vec3_sub(vert[2], vert[0], v0v2); + glm_vec3_sub(vert[1], vertices[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; + float u = glm_vec3_dot(t, pvec) / det; if(u < 0 || u > 1) continue; vec3 q; glm_vec3_cross(t, v0v1, q); - float v = glm_vec3_dot(dir, q) * det_inv; + float v = glm_vec3_dot(dir, q) / det; if(v < 0 || (u+v) > 1) continue; intersect = true; - *distance = min(*distance, glm_vec3_dot(v0v2, q) * det_inv); + float intersect_distance = glm_vec3_dot(v0v2, q) / det; + if(intersect_distance < *distance) { + *distance = intersect_distance; + *vertex = (u > v) ? ((triangle+1) % 6)+1 : triangle+1; + } } return intersect; @@ -725,26 +889,27 @@ bool ray_hex_intersect( bool ray_region_intersect( float* distance, + uint32_t* vertex, uint32_t* hid, vec3 start, vec3 dir, HexRegion* region) { bool intersect = false; - float temp_distance; - *distance = INFINITY; - + float intersect_distance = INFINITY; + uint32_t intersection_vertex = 0; vec3 region_offset = { ((float)region->data.q + (float)region->data.r/2)*REGION_WIDTH - region->data.r*HEX_X/2, 0, 0.75*region->data.r*REGION_HEIGHT + 0.25*region->data.r*HEX_Z + 0.5*region->data.q*HEX_Z, }; - for(uint32_t temp_hid = 0; temp_hid < REGION_HEX_COUNT; temp_hid++) { - if(ray_hex_intersect(&temp_distance, start, dir, region_offset, temp_hid, region)) { - if(temp_distance < *distance) { + for(uint32_t intersect_hid = 0; intersect_hid < REGION_HEX_COUNT; intersect_hid++) { + if(ray_hex_intersect(&intersect_distance, &intersection_vertex, start, dir, region_offset, intersect_hid, region)) { + if(intersect_distance < *distance) { intersect = true; - *hid = temp_hid; - *distance = temp_distance; + *hid = intersect_hid; + *distance = intersect_distance; + *vertex = intersection_vertex; } } } @@ -754,6 +919,7 @@ bool ray_region_intersect( bool ray_world_intersect( float* distance, + uint32_t* vertex, uint32_t* rid, uint32_t* hid, vec4 ray_start, @@ -777,24 +943,27 @@ bool ray_world_intersect( glm_vec3_divs(dir, mdir, dir); bool intersect = false; - float temp_distance; - uint32_t temp_hid; + float intersect_distance = INFINITY; + uint32_t intersection_vertex = 0; + uint32_t intersect_hid; + *distance = INFINITY; - for(uint32_t temp_rid = 0; temp_rid < MAX_LOADED_REGIONS; temp_rid++) { - HexRegion* region = context->regions[temp_rid]; + for(uint32_t intersect_rid = 0; intersect_rid < MAX_LOADED_REGIONS; intersect_rid++) { + HexRegion* region = context->regions[intersect_rid]; if(region == NULL) { continue; } else if(region->data.map != context->data.current_map) { continue; } - if(ray_region_intersect(&temp_distance, &temp_hid, start, dir, region)) { - if(temp_distance < *distance) { + if(ray_region_intersect(&intersect_distance, &intersection_vertex, &intersect_hid, start, dir, region)) { + if(intersect_distance < *distance) { intersect = true; - *hid = temp_hid; - *rid = temp_rid; - *distance = temp_distance; + *hid = intersect_hid; + *rid = intersect_rid; + *vertex = intersection_vertex; + *distance = intersect_distance; } } } @@ -884,6 +1053,7 @@ VkResult update_hex_hover( float distance; if(ray_world_intersect( &distance, + &hex->data.hovered_vertex, &hex->data.hovered_region, &hex->data.hovered_hex, hex->data.hover_start, @@ -893,7 +1063,7 @@ VkResult update_hex_hover( &hex->data.hovered_region, hex->context, offsetof(GPUHexContext, hovered_region), - sizeof(uint32_t)*2, + sizeof(uint32_t)*3, gpu); } @@ -923,6 +1093,7 @@ VkResult update_hex_click( if(ray_world_intersect( &distance, + &hex->data.clicked_vertex, &hex->data.clicked_region, &hex->data.clicked_hex, hex->data.click_start, @@ -932,7 +1103,7 @@ VkResult update_hex_click( &hex->data.clicked_region, hex->context, offsetof(GPUHexContext, clicked_region), - sizeof(uint32_t)*2, + sizeof(uint32_t)*3, gpu)); }