15 void DefenderWindow::create_flame_resources() {
17 create_flame_swapchain_resources();
20 void DefenderWindow::cleanup_flame_resources() {
21 cleanup_flame_swapchain_resources();
22 if (flame_vertex_buffer != VK_NULL_HANDLE) {
23 vkDestroyBuffer(
device, flame_vertex_buffer,
nullptr);
24 flame_vertex_buffer = VK_NULL_HANDLE;
26 if (flame_vertex_buffer_memory != VK_NULL_HANDLE) {
27 vkFreeMemory(
device, flame_vertex_buffer_memory,
nullptr);
28 flame_vertex_buffer_memory = VK_NULL_HANDLE;
30 flame_vertex_count = 0;
33 void DefenderWindow::cleanup_flame_swapchain_resources() {
34 if (flame_pipeline != VK_NULL_HANDLE) {
35 vkDestroyPipeline(
device, flame_pipeline,
nullptr);
36 flame_pipeline = VK_NULL_HANDLE;
38 if (flame_pipeline_layout != VK_NULL_HANDLE) {
39 vkDestroyPipelineLayout(
device, flame_pipeline_layout,
nullptr);
40 flame_pipeline_layout = VK_NULL_HANDLE;
44 void DefenderWindow::create_flame_swapchain_resources() {
45 if (flame_vertex_count == 0 ||
device == VK_NULL_HANDLE) {
48 create_flame_pipeline();
51 void DefenderWindow::create_flame_mesh() {
52 constexpr int segments = 40;
53 constexpr float base_z = 0.555f;
54 constexpr float tip_z = 1.18f;
55 constexpr float base_y = 0.040f;
56 constexpr float outer_radius = 0.072f;
57 constexpr float inner_radius = 0.034f;
59 std::vector<space::FlameVertex> vertices{};
60 vertices.reserve(
static_cast<std::size_t
>(segments) * 6U);
62 const glm::vec4 outer_base_color{1.0f, 0.42f, 0.08f, 0.62f};
63 const glm::vec4 outer_tip_color{0.7f, 0.08f, 0.0f, 0.0f};
64 const glm::vec4 inner_base_color{1.0f, 0.94f, 0.52f, 0.86f};
65 const glm::vec4 inner_tip_color{1.0f, 0.32f, 0.04f, 0.0f};
67 auto add_cone = [&](
float radius,
const glm::vec4 &base_color,
const glm::vec4 &tip_color) {
68 const glm::vec3 tip{0.0f, base_y, tip_z};
69 for (
int i = 0; i < segments; ++i) {
70 const float a0 = (
static_cast<float>(i) /
static_cast<float>(segments)) * 2.0f *
space::PI;
71 const float a1 = (
static_cast<float>(i + 1) /
static_cast<float>(segments)) * 2.0f *
space::PI;
72 const glm::vec3 p0{std::cos(a0) * radius, base_y + std::sin(a0) * radius, base_z};
73 const glm::vec3 p1{std::cos(a1) * radius, base_y + std::sin(a1) * radius, base_z};
74 vertices.push_back({p0, base_color});
75 vertices.push_back({p1, base_color});
76 vertices.push_back({tip, tip_color});
80 add_cone(outer_radius, outer_base_color, outer_tip_color);
81 add_cone(inner_radius, inner_base_color, inner_tip_color);
83 flame_vertex_count =
static_cast<uint32_t
>(vertices.size());
84 const VkDeviceSize
buffer_size =
sizeof(space::FlameVertex) *
static_cast<VkDeviceSize
>(vertices.size());
85 create_buffer(buffer_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, flame_vertex_buffer, flame_vertex_buffer_memory);
88 if (vkMapMemory(
device, flame_vertex_buffer_memory, 0, buffer_size, 0, &data) != VK_SUCCESS || data ==
nullptr) {
89 throw mxvk::Exception(
"Failed to map defender flame vertex buffer");
91 std::memcpy(data, vertices.data(),
static_cast<std::size_t
>(buffer_size));
92 vkUnmapMemory(
device, flame_vertex_buffer_memory);
95 void DefenderWindow::create_flame_pipeline() {
96 cleanup_flame_swapchain_resources();
98 const std::vector<char> vert_shader_code =
loadSpv(asset_root +
"/data/flame.vert.spv");
99 const std::vector<char> frag_shader_code =
loadSpv(asset_root +
"/data/flame.frag.spv");
102 VkShaderModule frag_shader_module = VK_NULL_HANDLE;
107 VkPipelineShaderStageCreateInfo vert_stage{};
108 vert_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
109 vert_stage.stage = VK_SHADER_STAGE_VERTEX_BIT;
110 vert_stage.module = vert_shader_module;
111 vert_stage.pName =
"main";
113 VkPipelineShaderStageCreateInfo frag_stage{};
114 frag_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
115 frag_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
116 frag_stage.module = frag_shader_module;
117 frag_stage.pName =
"main";
119 std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages = {vert_stage, frag_stage};
121 VkVertexInputBindingDescription binding_description{};
122 binding_description.binding = 0;
123 binding_description.stride =
sizeof(space::FlameVertex);
124 binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
126 std::array<VkVertexInputAttributeDescription, 2> attributes{};
127 attributes[0].binding = 0;
128 attributes[0].location = 0;
129 attributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
130 attributes[0].offset = offsetof(space::FlameVertex, pos);
131 attributes[1].binding = 0;
132 attributes[1].location = 1;
133 attributes[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
134 attributes[1].offset = offsetof(space::FlameVertex, color);
136 VkPipelineVertexInputStateCreateInfo vertex_input{};
137 vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
138 vertex_input.vertexBindingDescriptionCount = 1;
139 vertex_input.pVertexBindingDescriptions = &binding_description;
140 vertex_input.vertexAttributeDescriptionCount =
static_cast<uint32_t
>(attributes.size());
141 vertex_input.pVertexAttributeDescriptions = attributes.data();
143 VkPipelineInputAssemblyStateCreateInfo input_assembly{};
144 input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
145 input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
146 input_assembly.primitiveRestartEnable = VK_FALSE;
148 VkPipelineViewportStateCreateInfo viewport_state{};
149 viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
150 viewport_state.viewportCount = 1;
151 viewport_state.scissorCount = 1;
153 const std::array<VkDynamicState, 2> dynamic_states = {
154 VK_DYNAMIC_STATE_VIEWPORT,
155 VK_DYNAMIC_STATE_SCISSOR,
157 VkPipelineDynamicStateCreateInfo dynamic_info{};
158 dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
159 dynamic_info.dynamicStateCount =
static_cast<uint32_t
>(dynamic_states.size());
160 dynamic_info.pDynamicStates = dynamic_states.data();
162 VkPipelineRasterizationStateCreateInfo rasterizer{};
163 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
164 rasterizer.depthClampEnable = VK_FALSE;
165 rasterizer.rasterizerDiscardEnable = VK_FALSE;
166 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
167 rasterizer.lineWidth = 1.0f;
168 rasterizer.cullMode = VK_CULL_MODE_NONE;
169 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
170 rasterizer.depthBiasEnable = VK_FALSE;
172 VkPipelineMultisampleStateCreateInfo multisampling{};
173 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
174 multisampling.sampleShadingEnable = VK_FALSE;
175 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
177 VkPipelineDepthStencilStateCreateInfo depth_stencil{};
178 depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
179 depth_stencil.depthTestEnable = VK_TRUE;
180 depth_stencil.depthWriteEnable = VK_FALSE;
181 depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
182 depth_stencil.depthBoundsTestEnable = VK_FALSE;
183 depth_stencil.stencilTestEnable = VK_FALSE;
185 VkPipelineColorBlendAttachmentState color_blend_attachment{};
186 color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
187 color_blend_attachment.blendEnable = VK_TRUE;
188 color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
189 color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
190 color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
191 color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
192 color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
193 color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
195 VkPipelineColorBlendStateCreateInfo color_blending{};
196 color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
197 color_blending.logicOpEnable = VK_FALSE;
198 color_blending.attachmentCount = 1;
199 color_blending.pAttachments = &color_blend_attachment;
201 VkPushConstantRange push_range{};
202 push_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
203 push_range.offset = 0;
204 push_range.size =
sizeof(space::FlamePushConstants);
206 VkPipelineLayoutCreateInfo layout_info{};
207 layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
208 layout_info.pushConstantRangeCount = 1;
209 layout_info.pPushConstantRanges = &push_range;
211 if (vkCreatePipelineLayout(
device, &layout_info,
nullptr, &flame_pipeline_layout) != VK_SUCCESS) {
212 throw mxvk::Exception(
"Failed to create defender flame pipeline layout");
218 VkPipelineRenderingCreateInfo rendering_info{};
219 rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
220 rendering_info.colorAttachmentCount = 1;
221 rendering_info.pColorAttachmentFormats = &color_format;
226 VkGraphicsPipelineCreateInfo pipeline_info{};
227 pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
228 pipeline_info.pNext = &rendering_info;
229 pipeline_info.stageCount =
static_cast<uint32_t
>(shader_stages.size());
230 pipeline_info.pStages = shader_stages.data();
231 pipeline_info.pVertexInputState = &vertex_input;
232 pipeline_info.pInputAssemblyState = &input_assembly;
233 pipeline_info.pViewportState = &viewport_state;
234 pipeline_info.pRasterizationState = &rasterizer;
235 pipeline_info.pMultisampleState = &multisampling;
236 pipeline_info.pDepthStencilState = &depth_stencil;
237 pipeline_info.pColorBlendState = &color_blending;
238 pipeline_info.pDynamicState = &dynamic_info;
239 pipeline_info.layout = flame_pipeline_layout;
240 pipeline_info.renderPass = VK_NULL_HANDLE;
241 pipeline_info.subpass = 0;
243 if (vkCreateGraphicsPipelines(
device, VK_NULL_HANDLE, 1, &pipeline_info,
nullptr, &flame_pipeline) != VK_SUCCESS) {
244 throw mxvk::Exception(
"Failed to create defender flame pipeline");
247 if (frag_shader_module != VK_NULL_HANDLE) {
248 vkDestroyShaderModule(
device, frag_shader_module,
nullptr);
250 vkDestroyShaderModule(
device, vert_shader_module,
nullptr);
251 cleanup_flame_swapchain_resources();
255 vkDestroyShaderModule(
device, frag_shader_module,
nullptr);
256 vkDestroyShaderModule(
device, vert_shader_module,
nullptr);
259 void DefenderWindow::draw_engine_flame(VkCommandBuffer cmd,
const VkExtent2D &extent,
const glm::mat4 &view,
const glm::mat4 &projection) {
260 const bool boost_active = boost_pressed || controller_boost_pressed;
261 const bool propulsion_active = propulsion_pressed || controller_propulsion_pressed || boost_active;
262 if (!propulsion_active || ship.current_speed <= 1.0f || flame_pipeline == VK_NULL_HANDLE || flame_vertex_buffer == VK_NULL_HANDLE || flame_vertex_count == 0) {
266 VkViewport viewport{};
269 viewport.width =
static_cast<float>(extent.width);
270 viewport.height =
static_cast<float>(extent.height);
271 viewport.minDepth = 0.0f;
272 viewport.maxDepth = 1.0f;
273 vkCmdSetViewport(cmd, 0, 1, &viewport);
276 scissor.offset = {0, 0};
277 scissor.extent = extent;
278 vkCmdSetScissor(cmd, 0, 1, &scissor);
280 space::FlamePushConstants pc{};
281 pc.
mvp = projection * view * last_ship_model_matrix;
282 pc.
params = glm::vec4(elapsed_seconds, std::clamp(ship.current_speed / ship.max_speed, 0.0f, 2.0f), boost_active ? 1.0f : 0.0f, 0.0f);
284 vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, flame_pipeline);
285 vkCmdPushConstants(cmd, flame_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(pc), &pc);
287 VkBuffer vertex_buffers[] = {flame_vertex_buffer};
288 VkDeviceSize offsets[] = {0};
289 vkCmdBindVertexBuffers(cmd, 0, 1, vertex_buffers, offsets);
290 vkCmdDraw(cmd, flame_vertex_count, 1, 0, 0);
293 void DefenderWindow::create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &buffer_memory)
const {
294 VkBufferCreateInfo buffer_info{};
295 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
296 buffer_info.size = size;
297 buffer_info.usage = usage;
298 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
300 if (vkCreateBuffer(
device, &buffer_info,
nullptr, &buffer) != VK_SUCCESS) {
301 throw mxvk::Exception(
"Failed to create defender buffer");
304 VkMemoryRequirements mem_requirements{};
305 vkGetBufferMemoryRequirements(
device, buffer, &mem_requirements);
307 VkMemoryAllocateInfo alloc_info{};
308 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
309 alloc_info.allocationSize = mem_requirements.size;
312 alloc_info.memoryTypeIndex = find_memory_type(mem_requirements.memoryTypeBits, properties);
313 if (vkAllocateMemory(
device, &alloc_info,
nullptr, &buffer_memory) != VK_SUCCESS) {
314 throw mxvk::Exception(
"Failed to allocate defender buffer memory");
316 if (vkBindBufferMemory(
device, buffer, buffer_memory, 0) != VK_SUCCESS) {
317 throw mxvk::Exception(
"Failed to bind defender buffer memory");
320 if (buffer_memory != VK_NULL_HANDLE) {
321 vkFreeMemory(
device, buffer_memory,
nullptr);
322 buffer_memory = VK_NULL_HANDLE;
324 if (buffer != VK_NULL_HANDLE) {
325 vkDestroyBuffer(
device, buffer,
nullptr);
326 buffer = VK_NULL_HANDLE;
332 [[nodiscard]] uint32_t DefenderWindow::find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags properties)
const {
333 VkPhysicalDeviceMemoryProperties mem_properties{};
336 for (uint32_t i = 0; i < mem_properties.memoryTypeCount; ++i) {
337 if ((type_filter & (1U << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) {
342 throw mxvk::Exception(
"Failed to find defender memory type");
static VkShaderModule createShaderModule(VkDevice device, const std::vector< char > &spv_bytes)
Create a shader module from SPIR-V bytecode.
VkPhysicalDevice physical_device
VkFormat getDepthFormat() const noexcept
Get the depth format used for dynamic rendering attachments.
static std::vector< char > loadSpv(const std::string &path)
Load a SPIR-V file from disk.
VkFormat getSwapchainFormat() const noexcept
Get the swapchain color format.
constexpr size_t buffer_size
constexpr float PI
Single-precision value of pi.
glm::vec4 params
Shader-specific animation parameters.
glm::mat4 mvp
Model-view-projection matrix.