#include "hex.h" #include "gpu.h" #include "vulkan/vulkan_core.h" #include #include // 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}, }; HexCoord hex_starts_qr[] = { { 0, -1}, {-1, 0}, {-1, 1}, { 0, 1}, { 1, 0}, { 1, -1}, }; HexCoord hex_directions_qr[] = { {-1, 1}, { 0, 1}, { 1, 0}, { 1, -1}, { 0, -1}, {-1, 0}, }; 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, }; 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, }; VkPipelineRenderingCreateInfo rendering_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &gpu->swapchain_format.format, .depthAttachmentFormat = gpu->depth_format, }; 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, .pNext = &rendering_info, .renderPass = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VK_RESULT(vkCreateGraphicsPipelines( gpu->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, NULL, &pipeline->pipeline)); vkDestroyShaderModule(gpu->device, vert_shader, NULL); vkDestroyShaderModule(gpu->device, frag_shader, NULL); return VK_SUCCESS; } VkResult create_ray_pipeline( RenderContext* gpu, GraphicsPipeline* pipeline) { VkResult result; VkShaderModule vert_shader = load_shader_file("shader/ray.vert.spv", gpu->device); VkShaderModule frag_shader = load_shader_file("shader/ray.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_LINE_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, }; VkPipelineRenderingCreateInfo rendering_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &gpu->swapchain_format.format, .depthAttachmentFormat = gpu->depth_format, }; 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, .pNext = &rendering_info, .renderPass = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VK_RESULT(vkCreateGraphicsPipelines( gpu->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, NULL, &pipeline->pipeline)); vkDestroyShaderModule(gpu->device, vert_shader, NULL); vkDestroyShaderModule(gpu->device, frag_shader, NULL); return VK_SUCCESS; } VkResult create_hex_highlight_pipeline( RenderContext* gpu, GraphicsPipeline* pipeline) { VkResult result; VkShaderModule vert_shader = load_shader_file("shader/hex_highlight.vert.spv", gpu->device); VkShaderModule frag_shader = load_shader_file("shader/hex_highlight.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_TRIANGLE_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, }; VkPipelineRenderingCreateInfo rendering_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &gpu->swapchain_format.format, .depthAttachmentFormat = gpu->depth_format, }; 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, .pNext = &rendering_info, .renderPass = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VK_RESULT(vkCreateGraphicsPipelines( gpu->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, NULL, &pipeline->pipeline)); vkDestroyShaderModule(gpu->device, vert_shader, NULL); vkDestroyShaderModule(gpu->device, frag_shader, NULL); return VK_SUCCESS; } VkResult create_hex_pipeline( RenderContext* gpu, GraphicsPipeline* pipeline) { VkResult result; VkShaderModule vert_shader = load_shader_file("shader/hex.vert.spv", gpu->device); VkShaderModule frag_shader = load_shader_file("shader/hex.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_TRIANGLE_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, }; VkPipelineRenderingCreateInfo rendering_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &gpu->swapchain_format.format, .depthAttachmentFormat = gpu->depth_format, }; 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, .pNext = &rendering_info, .renderPass = VK_NULL_HANDLE, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; VK_RESULT(vkCreateGraphicsPipelines( gpu->device, VK_NULL_HANDLE, 1, &graphics_pipeline_info, NULL, &pipeline->pipeline)); vkDestroyShaderModule(gpu->device, vert_shader, NULL); vkDestroyShaderModule(gpu->device, frag_shader, NULL); return VK_SUCCESS; } VkResult create_hex_context( RenderContext* gpu, HexContext* context) { VkResult result; VK_RESULT(create_hex_pipeline(gpu, &context->graphics)); VK_RESULT(create_ray_pipeline(gpu, &context->ray_pipeline)); 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)); for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPUHexContext), &context->context[i], &context->context_memory[i])); context->address[i] = buffer_address(gpu->device, context->context[i]); VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPURay)*MAX_RAYS, &context->rays[i], &context->rays_memory[i])); VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPUPoint)*MAX_POINTS, &context->points[i], &context->points_memory[i])); VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPUHighlight)*MAX_HIGHLIGHTS, &context->highlights[i], &context->highlights_memory[i])); } for(uint32_t i = 0; i < MAX_HIGHLIGHTS; i++) { uint32_t temp = 0xFFFFFFFF; VK_RESULT(add_transfers( &temp, context->highlights, sizeof(GPUHighlight)*i + offsetof(GPUHighlight, hex), sizeof(uint32_t), gpu)); } for(uint32_t i = 0; i < MAX_POINTS; i++) { uint32_t temp = 0xFFFFFFFF; VK_RESULT(add_transfers( &temp, context->points, sizeof(GPUPoint)*i + offsetof(GPUPoint, hex), sizeof(uint32_t), gpu)); } VK_RESULT(add_transfers( &context->data, context->context, 0, sizeof(GPUHexContext), gpu)); for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VkDeviceAddress rays, points, highlights; rays = buffer_address(gpu->device, context->rays[i]); points = buffer_address(gpu->device, context->points[i]); highlights = buffer_address(gpu->device, context->highlights[i]); VK_RESULT(add_transfer( &rays, context->context[i], offsetof(GPUHexContext, rays), sizeof(VkDeviceAddress), i, gpu)); VK_RESULT(add_transfer( &points, context->context[i], offsetof(GPUHexContext, points), sizeof(VkDeviceAddress), i, gpu)); VK_RESULT(add_transfer( &highlights, context->context[i], offsetof(GPUHexContext, highlights), sizeof(VkDeviceAddress), i, gpu)); } return VK_SUCCESS; } void destroy_hex_context(RenderContext* gpu, HexContext* context) { vkDestroyPipeline(gpu->device, context->graphics.pipeline, NULL); vkDestroyPipelineLayout(gpu->device, context->graphics.layout, NULL); vkDestroyPipeline(gpu->device, context->highlight_pipeline.pipeline, NULL); vkDestroyPipelineLayout(gpu->device, context->highlight_pipeline.layout, NULL); vkDestroyPipeline(gpu->device, context->point_pipeline.pipeline, NULL); vkDestroyPipelineLayout(gpu->device, context->point_pipeline.layout, NULL); vkDestroyPipeline(gpu->device, context->ray_pipeline.pipeline, NULL); vkDestroyPipelineLayout(gpu->device, context->ray_pipeline.layout, NULL); // Direct teardown rather than free_hex_region: its GPU-address-nulling // transfer is pointless work when the context's own buffers are about to // be destroyed right below, unread. for(uint32_t i = 0; i < MAX_LOADED_REGIONS; i++) { if(context->regions[i] == NULL) continue; vmaDestroyBuffer(gpu->allocator, context->regions[i]->region, context->regions[i]->region_memory); free(context->regions[i]); context->regions[i] = NULL; } for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vmaDestroyBuffer(gpu->allocator, context->context[i], context->context_memory[i]); vmaDestroyBuffer(gpu->allocator, context->rays[i], context->rays_memory[i]); vmaDestroyBuffer(gpu->allocator, context->points[i], context->points_memory[i]); vmaDestroyBuffer(gpu->allocator, context->highlights[i], context->highlights_memory[i]); } } VkResult set_hex_region(HexRegion* region, HexContext* hex, RenderContext* gpu) { uint32_t index = UINT32_MAX; for(uint32_t i = 0; i < MAX_LOADED_REGIONS; i++) { if(hex->regions[i] == region) { index = i; break; } } if(index == UINT32_MAX) { return VK_ERROR_UNKNOWN; } return add_transfer( ®ion->data.hexes, region->region, offsetof(GPUHexRegion, hexes), sizeof(GPUHex)*REGION_HEX_COUNT, gpu->current_frame, gpu); } VkResult free_hex_region( uint32_t region_index, HexContext* hex, RenderContext* gpu) { if(hex->regions[region_index] == NULL) return VK_ERROR_VALIDATION_FAILED_EXT; free(hex->regions[region_index]); hex->regions[region_index] = NULL; VkDeviceAddress null_address = 0x00; return add_transfers( &null_address, hex->context, offsetof(GPUHexContext, regions) + sizeof(VkDeviceAddress)*region_index, sizeof(VkDeviceAddress), gpu); } VkResult allocate_hex_region( int32_t q, int32_t r, int32_t y, uint32_t map, HexRegion** region, HexContext* hex, RenderContext* gpu) { VkResult result; uint32_t i = 0; for(; i < MAX_LOADED_REGIONS; i++) { if(hex->regions[i] == NULL) { hex->regions[i] = malloc(sizeof(HexRegion)); *region = hex->regions[i]; break; } } if(*region == NULL) { return VK_ERROR_OUT_OF_HOST_MEMORY; } (*region)->data.position.q = q; (*region)->data.position.r = r; (*region)->data.y = y; (*region)->data.map = map; VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPUHexRegion), &(*region)->region, &(*region)->region_memory)); VK_RESULT(add_transfer( &(*region)->data.position, (*region)->region, offsetof(GPUHexRegion, position), sizeof(int32_t)*3 + sizeof(uint32_t), gpu->current_frame, gpu)); (*region)->address = buffer_address(gpu->device, (*region)->region); return add_transfers( &(*region)->address, hex->context, offsetof(GPUHexContext, regions) + sizeof(VkDeviceAddress)*i, sizeof(VkDeviceAddress), gpu); } bool ray_hex_intersect( float* distance, uint32_t* vertex, vec3 start, vec3 dir, vec3 region_offset, uint32_t hex_index, bool edge_only, HexRegion* region) { GPUHex* hex = ®ion->data.hexes[hex_index]; 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]); } bool intersect = false; vec3 t; glm_vec3_sub(start, vertices[0], t); for(uint32_t triangle = 0; triangle < 6; triangle++) { 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[0], vertices[0], v0v1); vec3 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 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; if(v < 0 || (u+v) > 1) continue; intersect = true; float intersect_distance = glm_vec3_dot(v0v2, q) / det; if(intersect_distance < *distance) { *distance = intersect_distance; if(edge_only) { // Never report the hex center (vertex 0) - always snap to whichever // of the triangle's two edge vertices is closer, so vertex-mode // picking always lands on a real, editable vertex. if(u >= v) { *vertex = ((triangle + 1) % 6) + 1; } else { *vertex = triangle + 1; } } else { float w = 1 - u - v; if(u >= v && u >= w) { *vertex = ((triangle + 1) % 6) + 1; } else if(v >= u && v >= w) { *vertex = triangle + 1; } else { *vertex = 0; } } } } return intersect; } bool ray_region_intersect( float* distance, uint32_t* vertex, uint32_t* hid, vec3 start, vec3 dir, bool edge_only, HexRegion* region) { bool intersect = false; float intersect_distance = INFINITY; uint32_t intersection_vertex = 0; vec3 region_offset = { ((float)region->data.position.q + (float)region->data.position.r/2)*REGION_WIDTH - region->data.position.r*HEX_X/2, 0, 0.75*region->data.position.r*REGION_HEIGHT + 0.25*region->data.position.r*HEX_Z + 0.5*region->data.position.q*HEX_Z, }; 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, edge_only, region)) { if(intersect_distance < *distance) { intersect = true; *hid = intersect_hid; *distance = intersect_distance; *vertex = intersection_vertex; } } } return intersect; } bool ray_world_intersect( float* distance, uint32_t* vertex, uint32_t* rid, uint32_t* hid, vec4 ray_start, vec4 ray_end, bool edge_only, HexContext* context) { 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 mdir = glm_vec3_norm(dir); glm_vec3_divs(dir, mdir, dir); bool intersect = false; float intersect_distance = INFINITY; uint32_t intersection_vertex = 0; uint32_t intersect_hid; *distance = INFINITY; 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(&intersect_distance, &intersection_vertex, &intersect_hid, start, dir, edge_only, region)) { if(intersect_distance < *distance) { intersect = true; *hid = intersect_hid; *rid = intersect_rid; *vertex = intersection_vertex; *distance = intersect_distance; } } } return intersect; } void update_hex_picking_inverse(Camera* camera, HexContext* hex) { mat4 regular; glm_mat4_mul(camera->proj, camera->view, regular); glm_mat4_inv(regular, hex->inverse); } void cursor_to_world_ray(RenderContext* gpu, mat4 inverse, double cursor[2], vec4 start, vec4 end) { double cursor_scaled[2] = { 2*(cursor[0]*gpu->window_scale[0]/gpu->swapchain_extent.width - 0.5), 2*(cursor[1]*gpu->window_scale[1]/gpu->swapchain_extent.height - 0.5), }; vec4 transformed_start = { cursor_scaled[0], cursor_scaled[1], PERSPECTIVE_NEARZ, 1.0, }; vec4 transformed_end = { PERSPECTIVE_FARZ*cursor_scaled[0], PERSPECTIVE_FARZ*cursor_scaled[1], PERSPECTIVE_FARZ, PERSPECTIVE_FARZ, }; glm_mat4_mulv(inverse, transformed_start, start); glm_mat4_mulv(inverse, transformed_end, end); } void hex_qr(uint32_t hex, HexCoord* world) { float radius = 0; float ring = 0; int side = 0; if(hex != 0) { radius = floor(0.5 + sqrt(12*hex-3)/6); ring = hex - (3*radius*radius - 3*radius + 1); side = floor(ring/radius); } world->q = hex_starts_qr[side].q*radius + hex_directions_qr[side].q*(ring-(radius*side)); world->r = hex_starts_qr[side].r*radius + hex_directions_qr[side].r*(ring-(radius*side)); } void region_qr(HexCoord region, HexCoord* world) { world->q = region.q * REGION_DIAMETER + region.r * (REGION_DIAMETER - 1)/2; world->r = -region.r * REGION_DIAMETER - region.q * (REGION_DIAMETER + 1)/2; } void hex_add(HexCoord from, HexCoord* to) { to->q += from.q; to->r += from.r; } unsigned int hex_distance(HexCoord a, HexCoord b) { return (abs(a.q - b.q) + abs(a.r - b.r) + abs(a.q + a.r - b.q - b.r))/2; } unsigned int region_radius_1(HexCoord hex, HexCoord region) { return region.q*REGION_DIAMETER + region.r*(REGION_SIZE-1) - hex.q; } unsigned int region_radius_2(HexCoord hex, HexCoord region) { return -region.r*REGION_DIAMETER - region.q*REGION_SIZE - hex.r; } unsigned int region_radius_3(HexCoord hex, HexCoord region) { return region.q*(REGION_SIZE-1) - region.r*REGION_SIZE - hex.q - hex.r; } void region_1(HexCoord hex, HexCoord* region) { region->q = ceil((hex.q*REGION_DIAMETER + hex.r*(REGION_SIZE-1))/(double)REGION_HEX_COUNT); region->r = floor((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER)/(double)REGION_HEX_COUNT); } void region_2(HexCoord hex, HexCoord* region) { region->q = ceil((hex.q*REGION_DIAMETER + hex.r*(REGION_SIZE - 1))/(double)REGION_HEX_COUNT); region->r = floor((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER + REGION_SIZE*REGION_SIZE - 2*REGION_SIZE + 1)/(double)REGION_HEX_COUNT); } void region_3(HexCoord hex, HexCoord* region) { region->q = ceil((hex.q*REGION_DIAMETER + hex.r*(REGION_SIZE-1) - REGION_SIZE*REGION_SIZE + 2*REGION_SIZE - 1)/(double)REGION_HEX_COUNT); region->r = ceil((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER)/(double)REGION_HEX_COUNT); } void region_4(HexCoord hex, HexCoord* region) { region->q = ceil((hex.q*REGION_DIAMETER + hex.r*(REGION_SIZE-1) - REGION_SIZE*REGION_SIZE + REGION_SIZE)/(double)REGION_HEX_COUNT); region->r = floor((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER)/(double)REGION_HEX_COUNT); } void region_5(HexCoord hex, HexCoord* region) { region->q = ceil((hex.r*(REGION_SIZE-1) + hex.q*REGION_DIAMETER - 2*REGION_SIZE*REGION_SIZE + 2*REGION_SIZE)/(double)REGION_HEX_COUNT); region->r = floor((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER + REGION_SIZE*REGION_SIZE - REGION_SIZE)/(double)REGION_HEX_COUNT); } void region_6(HexCoord hex, HexCoord* region) { region->q = floor((hex.q*REGION_DIAMETER + hex.r*(REGION_SIZE-1))/(double)REGION_HEX_COUNT); region->r = ceil((-hex.q*REGION_SIZE - hex.r*REGION_DIAMETER)/(double)REGION_HEX_COUNT); } void hex_index(HexCoord world, HexCoord* region, uint32_t* hex) { int R1, R2, R3; unsigned int side, radius; HexCoord side_start, region_center; region_1(world, region); R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (R1 + R2 + R3)/2; if(R1 >= 0 && R2 > 0 && radius <= REGION_SIZE - 1) { side = 0; goto side_found; } region_2(world, region); R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (R1 - R2 + R3)/2; if(R2 <= 0 && R3 > 0 && radius <= REGION_SIZE - 1) { side = 1; goto side_found; } region_3(world, region); R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (R1 - R2 - R3)/2; if(R1 > 0 && R3 <= 0 && radius <= REGION_SIZE - 1) { side = 2; goto side_found; } region_4(world, region); R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (-R1 + R2 + R3)/2; if(R1 < 0 && R3 >= 0 && radius <= REGION_SIZE - 1) { side = 5; goto side_found; } region_5(world, region); R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (-R1 + R2 - R3)/2; if(R2 >= 0 && R3 < 0 && radius <= REGION_SIZE - 1) { side = 4; goto side_found; } region_6(world, region); side = 3; R1 = region_radius_1(world, *region); R2 = region_radius_2(world, *region); R3 = region_radius_3(world, *region); radius = (-R1 - R2 -R3)/2; side_found: side_start.q = hex_starts_qr[side].q * radius; side_start.r = hex_starts_qr[side].r * radius; region_qr(*region, ®ion_center); hex_add(region_center, &side_start); unsigned int side_distance = hex_distance(world, side_start); if(radius == 0) { *hex = 0; } else { *hex = (3*radius*(radius-1) + 1) + radius*side + side_distance; } } void hex_vertex_neighbors( uint32_t vertex, HexCoord hex, uint32_t n_vertex[2], HexCoord n_region[2], uint32_t n_hex[2]) { HexCoord n1 = hex_starts_qr[(vertex+3) % 6]; HexCoord n2 = hex_starts_qr[(vertex+4) % 6]; hex_add(hex, &n1); hex_add(hex, &n2); hex_index(n1, &n_region[0], &n_hex[0]); hex_index(n2, &n_region[1], &n_hex[1]); n_vertex[0] = ((vertex + 1) % 6) + 1; n_vertex[1] = ((vertex + 3) % 6) + 1; } void first_matching_region(HexCoord coord, int y, uint32_t* region, HexContext* context) { for(*region = 0; *region < MAX_LOADED_REGIONS; (*region)++) { if(context->regions[*region] != NULL && context->regions[*region]->data.position.q == coord.q && context->regions[*region]->data.position.r == coord.r && context->regions[*region]->data.y == y) { return; } } }