From ba447583c74d44c6b07c5409918207157f921efe Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Mon, 9 Oct 2023 14:46:09 -0300 Subject: feat Render sprite in a 3D position --- glsl/shader_sprite_3d.frag.glsl | 42 +++++ glsl/shader_sprite_3d.vert.glsl | 73 ++++++++ src/core.cpp | 24 +++ src/core.hpp | 2 + src/sprite_3d.cpp | 82 ++++++++ src/sprite_3d.hpp | 25 +++ src/vk/device.cpp | 10 +- src/vk/device.hpp | 4 +- src/vk/graphics_pipeline_2d_solid.cpp | 4 +- src/vk/graphics_pipeline_3d.cpp | 7 +- src/vk/graphics_pipeline_sprite_3d.cpp | 333 +++++++++++++++++++++++++++++++++ src/vk/graphics_pipeline_sprite_3d.hpp | 44 +++++ src/vk/renderer.cpp | 20 +- src/vk/renderer.hpp | 3 + src/vk/sprite_3d.cpp | 181 ++++++++++++++++++ src/vk/sprite_3d.hpp | 46 +++++ src/vk/uniform_data_object.hpp | 7 + test/src/mode/demo.rb | 5 + 18 files changed, 899 insertions(+), 13 deletions(-) create mode 100644 glsl/shader_sprite_3d.frag.glsl create mode 100644 glsl/shader_sprite_3d.vert.glsl create mode 100644 src/sprite_3d.cpp create mode 100644 src/sprite_3d.hpp create mode 100644 src/vk/graphics_pipeline_sprite_3d.cpp create mode 100644 src/vk/graphics_pipeline_sprite_3d.hpp create mode 100644 src/vk/sprite_3d.cpp create mode 100644 src/vk/sprite_3d.hpp diff --git a/glsl/shader_sprite_3d.frag.glsl b/glsl/shader_sprite_3d.frag.glsl new file mode 100644 index 0000000..fe823f8 --- /dev/null +++ b/glsl/shader_sprite_3d.frag.glsl @@ -0,0 +1,42 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +struct DataTransferObject +{ + vec4 frag_color; + vec2 frag_texture_coord; +}; + +layout(location = 0) in DataTransferObject in_dto; + +layout(location = 0) out vec4 out_color; + +layout(set = 0, binding = 1) uniform UBOWorld +{ + vec3 directional_light_direction; + vec4 directional_light_color; +} ubo_world; + +layout(set = 2, binding = 1) uniform sampler2D texture_sampler; + +void +main() +{ + out_color = texture(texture_sampler, in_dto.frag_texture_coord); +} diff --git a/glsl/shader_sprite_3d.vert.glsl b/glsl/shader_sprite_3d.vert.glsl new file mode 100644 index 0000000..f7c6cf6 --- /dev/null +++ b/glsl/shader_sprite_3d.vert.glsl @@ -0,0 +1,73 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec2 in_texture_coord; + +layout(location = 0) out DataTransferObject +{ + vec4 frag_color; + vec2 frag_texture_coord; +} out_dto; + +layout(set = 0, binding = 0) uniform UBOWorld +{ + vec4 ambient_light_color; +} ubo_world; + +layout(set = 1, binding = 0) uniform UBOView +{ + mat4 view; + mat4 proj; +} ubo_view; + +layout(set = 2, binding = 0) uniform UBOSprite3D +{ + vec3 position; + vec2 size; +} ubo_position; + +void +main() +{ + vec4 position = vec4(ubo_position.position, 1.0); + position = ubo_view.view * position; + switch(gl_VertexIndex) + { + case 0: + position.x -= ubo_position.size.x/2; + position.y -= ubo_position.size.y/2; + break; + case 1: + position.x -= ubo_position.size.x/2; + position.y += ubo_position.size.y/2; + break; + case 2: + position.x += ubo_position.size.x/2; + position.y -= ubo_position.size.y/2; + break; + case 3: + position.x += ubo_position.size.x/2; + position.y += ubo_position.size.y/2; + break; + } + gl_Position = ubo_view.proj * position; + + out_dto.frag_color = ubo_world.ambient_light_color; + out_dto.frag_texture_coord = in_texture_coord; +} diff --git a/src/core.cpp b/src/core.cpp index 591eaba..e40370e 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -27,6 +27,7 @@ #include "static_mesh.hpp" #include "sound.hpp" #include "sprite.hpp" +#include "sprite_3d.hpp" #include "texture.hpp" #include "vector_3d.hpp" #include "vector_4d.hpp" @@ -700,6 +701,26 @@ unload_vk_graphics_pipeline_3d_skeletal(void *obj) cg_core.vk_graphics_pipeline_3d_skeletal = nullptr; } +void +load_vk_graphics_pipeline_sprite_3d(void *obj) +{ + try + { + cg_core.vk_graphics_pipeline_sprite_3d = + std::make_unique(); + } + catch(const CommandError &e) + { + throw CommandError{"Failed to create sprite 3d graphics pipeline."}; + } +} + +void +unload_vk_graphics_pipeline_sprite_3d(void *obj) +{ + cg_core.vk_graphics_pipeline_sprite_3d = nullptr; +} + void load_vk_graphics_pipeline_2d_solid(void *obj) { @@ -778,6 +799,7 @@ load_mruby_interface(void *obj) cg_static_mesh_init(cg_core.mrb); cg_sound_init(cg_core.mrb); cg_sprite_init(cg_core.mrb); + cg_sprite_3d_init(cg_core.mrb); cg_texture_init(cg_core.mrb); cg_vector_3d_init(cg_core.mrb); cg_vector_4d_init(cg_core.mrb); @@ -818,6 +840,8 @@ const CommandChain cg_sCore::loader{ // {&load_vk_graphics_pipeline_3d_skeletal, // &unload_vk_graphics_pipeline_3d_skeletal}, {&load_vk_graphics_pipeline_3d, &unload_vk_graphics_pipeline_3d}, + {&load_vk_graphics_pipeline_sprite_3d, + &unload_vk_graphics_pipeline_sprite_3d}, {&load_vk_graphics_pipeline_2d_solid, &unload_vk_graphics_pipeline_2d_solid}, {&load_vk_graphics_pipeline_2d_wired, &unload_vk_graphics_pipeline_2d_wired}, diff --git a/src/core.hpp b/src/core.hpp index 9566a52..7393b33 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -59,6 +59,7 @@ #include "vk/graphics_pipeline_3d_layout.hpp" #include "vk/graphics_pipeline_3d.hpp" #include "vk/graphics_pipeline_3d_skeletal.hpp" +#include "vk/graphics_pipeline_sprite_3d.hpp" #include "vk/renderer.hpp" #include "vk/swapchain.hpp" @@ -131,6 +132,7 @@ struct cg_sCore std::unique_ptr vk_graphics_pipeline_3d; std::unique_ptr vk_graphics_pipeline_3d_skeletal; + std::unique_ptr vk_graphics_pipeline_sprite_3d; std::unique_ptr vk_graphics_pipeline_2d_solid; std::unique_ptr vk_graphics_pipeline_2d_wired; diff --git a/src/sprite_3d.cpp b/src/sprite_3d.cpp new file mode 100644 index 0000000..3e4e29d --- /dev/null +++ b/src/sprite_3d.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sprite_3d.hpp" + +#include "sprite.hpp" +#include "vector_3d.hpp" + +void +cg_free_sprite_3d(mrb_state *mrb, void *obj) +{ + auto ptr = static_cast*>(obj); + + ptr->~shared_ptr(); + mrb_free(mrb, ptr); +} + +const struct mrb_data_type cg_sprite_3d_type = { + "CG_Sprite3D", cg_free_sprite_3d }; + +static mrb_value +cg_cSprite3D_initialize(mrb_state *mrb, mrb_value self) +{ + std::shared_ptr *sprite; + std::shared_ptr *position; + mrb_float w, h; + std::shared_ptr *ptr; + + mrb_get_args(mrb, "ddff", &sprite, &cg_sprite_type, + &position, &cg_vector_3d_type, &w, &h); + ptr = (std::shared_ptr*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + glm::vec3 size{w, h, 0.0}; + new(ptr)std::shared_ptr( + std::make_shared(*sprite, *position, size)); + + mrb_data_init(self, ptr, &cg_sprite_3d_type); + return self; +} + +static mrb_value +cg_cSprite3D_draw(mrb_state *mrb, mrb_value self) +{ + auto ptr = (std::shared_ptr*)DATA_PTR(self); + + auto &sprites_3d_to_draw = cg_core.vk_renderer->sprites_3d_to_draw[ + cg_core.vk_swapchain->current_frame]; + sprites_3d_to_draw.emplace_back(*ptr); + + return self; +} + +void +cg_sprite_3d_init(mrb_state *mrb) +{ + struct RClass *cg_m, *cg_cSprite3D; + + cg_m = mrb_module_get(mrb, "CandyGear"); + cg_cSprite3D = mrb_define_class_under( + mrb, cg_m, "Sprite3D", mrb->object_class); + MRB_SET_INSTANCE_TT(cg_cSprite3D, MRB_TT_DATA); + mrb_define_method( + mrb, cg_cSprite3D, "initialize", cg_cSprite3D_initialize, MRB_ARGS_REQ(3)); + mrb_define_method( + mrb, cg_cSprite3D, "draw", cg_cSprite3D_draw, MRB_ARGS_NONE()); +} diff --git a/src/sprite_3d.hpp b/src/sprite_3d.hpp new file mode 100644 index 0000000..526c23a --- /dev/null +++ b/src/sprite_3d.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_SPRITE_3D_H +#define CANDY_GEAR_SPRITE_3D_H 1 + +#include "core.hpp" + +void +cg_sprite_3d_init(mrb_state *mrb); + +#endif /* CANDY_GEAR_SPRITE_3D_H */ diff --git a/src/vk/device.cpp b/src/vk/device.cpp index ea943c4..83bdf38 100644 --- a/src/vk/device.cpp +++ b/src/vk/device.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 Frederico de Oliveira Linhares + * Copyright 2022-2023 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -158,6 +158,10 @@ Device::Device(VkPhysicalDevice vk_physical_device, bool with_swapchain) // Load Shaders { + this->vert_sprite_3d_shader_module = create_shader_module( + this->device, DATA_DIR "/glsl/shader_sprite_3d.vert.spv"); + this->frag_sprite_3d_shader_module = create_shader_module( + this->device, DATA_DIR "/glsl/shader_sprite_3d.frag.spv"); this->vert3d_shader_module = create_shader_module( this->device, DATA_DIR "/glsl/shader_3d.vert.spv"); this->frag3d_shader_module = create_shader_module( @@ -203,6 +207,10 @@ Device::~Device() std::free(this->queue_families); // Destroy shaders + vkDestroyShaderModule( + this->device, this->vert_sprite_3d_shader_module, nullptr); + vkDestroyShaderModule( + this->device, this->frag_sprite_3d_shader_module, nullptr); vkDestroyShaderModule(this->device, this->vert3d_shader_module, nullptr); vkDestroyShaderModule(this->device, this->frag3d_shader_module, nullptr); vkDestroyShaderModule( diff --git a/src/vk/device.hpp b/src/vk/device.hpp index 9fcec20..36fc08d 100644 --- a/src/vk/device.hpp +++ b/src/vk/device.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 Frederico de Oliveira Linhares + * Copyright 2022-2023 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,8 @@ public: VkDevice device; VkPhysicalDevice physical_device; + VkShaderModule vert_sprite_3d_shader_module; + VkShaderModule frag_sprite_3d_shader_module; VkShaderModule vert3d_shader_module; VkShaderModule frag3d_shader_module; VkShaderModule vert2d_solid_shader_module; diff --git a/src/vk/graphics_pipeline_2d_solid.cpp b/src/vk/graphics_pipeline_2d_solid.cpp index d0c1768..00bc26b 100644 --- a/src/vk/graphics_pipeline_2d_solid.cpp +++ b/src/vk/graphics_pipeline_2d_solid.cpp @@ -238,8 +238,8 @@ GraphicsPipeline2DSolid::draw( const size_t current_frame, const size_t next_frame, const uint32_t image_index) { - // Set viewport - { + // TODO set viewport just once per view, not once per pipeline. + { // Set viewport VkViewport vk_viewport{}; vk_viewport.x = view->region.x; vk_viewport.y = view->region.y; diff --git a/src/vk/graphics_pipeline_3d.cpp b/src/vk/graphics_pipeline_3d.cpp index 43ab734..d98a27d 100644 --- a/src/vk/graphics_pipeline_3d.cpp +++ b/src/vk/graphics_pipeline_3d.cpp @@ -277,6 +277,10 @@ GraphicsPipeline3D::draw( vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); } + vkCmdBindPipeline( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->graphic_pipeline); + // Draw models for(auto& [static_mesh, instances]: cg_core.vk_renderer->static_models_to_draw[current_frame]) @@ -284,9 +288,6 @@ GraphicsPipeline3D::draw( VkBuffer vertex_buffers[]{static_mesh->vertex_buffer->buffer}; VkDeviceSize offsets[]{0}; - vkCmdBindPipeline( - draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - this->graphic_pipeline); vkCmdBindVertexBuffers( draw_command_buffer, 0, 1, vertex_buffers, offsets); vkCmdBindIndexBuffer( diff --git a/src/vk/graphics_pipeline_sprite_3d.cpp b/src/vk/graphics_pipeline_sprite_3d.cpp new file mode 100644 index 0000000..d14cb48 --- /dev/null +++ b/src/vk/graphics_pipeline_sprite_3d.cpp @@ -0,0 +1,333 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "graphics_pipeline_sprite_3d.hpp" + +#include + +#include "../core.hpp" +#include "core.hpp" +#include "sprite.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +struct Sprite3DOrder +{ + std::shared_ptr sprite_3d; + float distance; +}; + +bool +operator<(const Sprite3DOrder &a, const Sprite3DOrder &b) +{ + return a.distance < b.distance; +} + +bool +operator>(const Sprite3DOrder &a, const Sprite3DOrder &b) +{ + return a.distance > b.distance; +} + +void +load_pipeline(void *obj) +{ + auto self = static_cast(obj); + + VkPipelineShaderStageCreateInfo vert_shader_stage_info{}; + vert_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_shader_stage_info.pNext = nullptr; + vert_shader_stage_info.flags = 0; + vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_shader_stage_info.module = + cg_core.vk_device_with_swapchain->vert_sprite_3d_shader_module; + vert_shader_stage_info.pName = "main"; + vert_shader_stage_info.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo frag_shader_stage_info{}; + frag_shader_stage_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_shader_stage_info.pNext = nullptr; + frag_shader_stage_info.flags = 0; + frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_shader_stage_info.module = + cg_core.vk_device_with_swapchain->frag_sprite_3d_shader_module; + frag_shader_stage_info.pName = "main"; + frag_shader_stage_info.pSpecializationInfo = nullptr; + + VkPipelineShaderStageCreateInfo shader_stages[] = { + vert_shader_stage_info, + frag_shader_stage_info + }; + + VkVertexInputBindingDescription vertex_input_binding{}; + vertex_input_binding.binding = 0; + vertex_input_binding.stride = sizeof(glm::vec2); + vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::array vertex_attribute{}; + // Texture coordinate. + vertex_attribute[0].location = 0; + vertex_attribute[0].binding = 0; + vertex_attribute[0].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute[0].offset = 0; + + VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; + vertex_input_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_info.pNext = nullptr; + vertex_input_info.flags = 0; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &vertex_input_binding; + vertex_input_info.vertexAttributeDescriptionCount = + static_cast(vertex_attribute.size()); + vertex_input_info.pVertexAttributeDescriptions = vertex_attribute.data(); + + VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; + input_assembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.pNext = nullptr; + input_assembly.flags = 0; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0; + viewport.y = 0; + viewport.width = cg_core.display_width; + viewport.height = cg_core.display_height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = {0, 0}; + scissor.extent = {cg_core.display_width, cg_core.display_height}; + + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.pNext = nullptr; + viewport_state.flags = 0; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.pNext = nullptr; + rasterizer.flags = 0; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + rasterizer.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + + VkPipelineDepthStencilStateCreateInfo depth_stencil = {}; + depth_stencil.sType = + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil.depthTestEnable = VK_TRUE; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.minDepthBounds = 0.0f; + depth_stencil.maxDepthBounds = 1.0f; + depth_stencil.stencilTestEnable = VK_FALSE; + depth_stencil.front = {}; + depth_stencil.back = {}; + + VkPipelineColorBlendAttachmentState color_blend_attachment = {}; + color_blend_attachment.blendEnable = VK_TRUE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment.dstColorBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo color_blending = {}; + color_blending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending.pNext = nullptr; + color_blending.flags = 0; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &color_blend_attachment; + color_blending.blendConstants[0] = 0.0f; + color_blending.blendConstants[1] = 0.0f; + color_blending.blendConstants[2] = 0.0f; + color_blending.blendConstants[3] = 0.0f; + + std::array dynamic_states{ + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_LINE_WIDTH, + VK_DYNAMIC_STATE_BLEND_CONSTANTS + }; + + VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; + dynamic_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_info.dynamicStateCount = dynamic_states.size(); + dynamic_state_info.pDynamicStates = dynamic_states.data(); + + VkGraphicsPipelineCreateInfo pipeline_info{}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.pNext = nullptr; + pipeline_info.flags = 0; + pipeline_info.stageCount = 2; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pTessellationState = nullptr; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pDepthStencilState = &depth_stencil; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.pDynamicState = &dynamic_state_info; + pipeline_info.layout = cg_core.vk_graphics_pipeline_3d_layout->pipeline; + pipeline_info.renderPass = cg_core.vk_render_pass->pipeline_3d; + pipeline_info.subpass = 0; + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_info.basePipelineIndex = -1; + + if(vkCreateGraphicsPipelines( + cg_core.vk_device_with_swapchain->device, VK_NULL_HANDLE, 1, + &pipeline_info, nullptr, &self->graphic_pipeline) + != VK_SUCCESS) + throw CommandError{"Failed to create graphics pipeline sprite 3d."}; +} + +void +unload_pipeline(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyPipeline( + cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); +} + +const CommandChain loader{ + {&load_pipeline, &unload_pipeline} +}; + +} + +namespace VK +{ + +GraphicsPipelineSprite3D::GraphicsPipelineSprite3D() +{ + loader.execute(this); +} + +GraphicsPipelineSprite3D::~GraphicsPipelineSprite3D() +{ + loader.revert(this); +} + +void +GraphicsPipelineSprite3D::draw( + std::shared_ptr view, const VkCommandBuffer draw_command_buffer, + const size_t current_frame, const uint32_t image_index) +{ + // TODO set viewport just once per view, not once per pipeline. + { // Set viewport + VkViewport vk_viewport{}; + vk_viewport.x = view->region.x; + vk_viewport.y = view->region.y; + vk_viewport.width = view->region.z; + vk_viewport.height = view->region.w; + vk_viewport.minDepth = 0.0f; + vk_viewport.maxDepth = 1.0f; + vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport); + + VkRect2D vk_scissor{}; + vk_scissor.offset.x = static_cast(view->region.x); + vk_scissor.offset.y = static_cast(view->region.y); + vk_scissor.extent.width = static_cast(view->region.z); + vk_scissor.extent.height = static_cast(view->region.w); + vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); + } + + vkCmdBindPipeline( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + this->graphic_pipeline); + + std::vector sprite_3d_order; + { // Sort sprites 3D + sprite_3d_order.reserve( + cg_core.vk_renderer->sprites_3d_to_draw[current_frame].size()); + + for(std::shared_ptr sprite: + cg_core.vk_renderer->sprites_3d_to_draw[current_frame]) + sprite_3d_order.emplace_back( + sprite, glm::distance(*view->camera_position, *sprite->position)); + + std::sort(sprite_3d_order.rbegin(), sprite_3d_order.rend()); + } + + // Draw sprites + for(auto& sprite: sprite_3d_order) + { + std::array vk_descriptor_sets{ + cg_core.vk_light->descriptor_sets_world[image_index], + view->descriptor_sets_3d[image_index], + sprite.sprite_3d->descriptor_sets[image_index]}; + VkDeviceSize offsets[]{0}; + + vkCmdBindVertexBuffers( + draw_command_buffer, 0, 1, + &sprite.sprite_3d->sprite->vertex_buffer->buffer, offsets); + vkCmdBindDescriptorSets( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + cg_core.vk_graphics_pipeline_3d_layout->pipeline, 0, + vk_descriptor_sets.size(), vk_descriptor_sets.data(), 0, nullptr); + + UDOSprite3D ubo_sprite_3d{}; + ubo_sprite_3d.position = *sprite.sprite_3d->position; + ubo_sprite_3d.size = sprite.sprite_3d->size; + sprite.sprite_3d->uniform_buffers[image_index].copy_data(&ubo_sprite_3d); + + vkCmdDraw(draw_command_buffer, Sprite::vertex_count, 1, 0, 0); + } +} + +} diff --git a/src/vk/graphics_pipeline_sprite_3d.hpp b/src/vk/graphics_pipeline_sprite_3d.hpp new file mode 100644 index 0000000..eb7aa34 --- /dev/null +++ b/src/vk/graphics_pipeline_sprite_3d.hpp @@ -0,0 +1,44 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H +#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H 1 + +#include + +#include "core.hpp" +#include "command_pool.hpp" +#include "sprite_3d.hpp" +#include "view_3d.hpp" + +namespace VK +{ + +struct GraphicsPipelineSprite3D +{ + VkPipeline graphic_pipeline; + + GraphicsPipelineSprite3D(); + ~GraphicsPipelineSprite3D(); + + void + draw(std::shared_ptr view, const VkCommandBuffer draw_command_buffer, + const size_t current_frame, const uint32_t image_index); +}; + +} + +#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H */ diff --git a/src/vk/renderer.cpp b/src/vk/renderer.cpp index 766690f..d4e25ba 100644 --- a/src/vk/renderer.cpp +++ b/src/vk/renderer.cpp @@ -146,6 +146,7 @@ Renderer::Renderer(std::vector> views_2d, std::vector> views_3d): skeletal_models_to_draw{cg_core.vk_swapchain->images_count}, static_models_to_draw{cg_core.vk_swapchain->images_count}, + sprites_3d_to_draw{cg_core.vk_swapchain->images_count}, views_2d{views_2d}, views_3d{views_3d} { @@ -239,14 +240,19 @@ Renderer::draw() vkCmdBeginRenderPass( draw_command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); + } - for(auto &view: this->views_3d) - cg_core.vk_graphics_pipeline_3d->draw( - view, draw_command_buffer, cg_core.vk_swapchain->current_frame, - image_index); + for(auto &view: this->views_3d) + cg_core.vk_graphics_pipeline_3d->draw( + view, draw_command_buffer, cg_core.vk_swapchain->current_frame, + image_index); - vkCmdEndRenderPass(draw_command_buffer); - } + for(auto &view: this->views_3d) + cg_core.vk_graphics_pipeline_sprite_3d->draw( + view, draw_command_buffer, cg_core.vk_swapchain->current_frame, + image_index); + + vkCmdEndRenderPass(draw_command_buffer); { // 2D render pass VkRenderPassBeginInfo render_pass_begin{}; @@ -345,6 +351,7 @@ Renderer::draw() { this->skeletal_models_to_draw[next_frame].clear(); this->static_models_to_draw[next_frame].clear(); + this->sprites_3d_to_draw[next_frame].clear(); cg_core.vk_swapchain->current_frame = next_frame; } } @@ -353,6 +360,7 @@ Renderer::draw() // Clear images for the current frame because we are skipping this frame. this->skeletal_models_to_draw[cg_core.vk_swapchain->current_frame].clear(); this->static_models_to_draw[cg_core.vk_swapchain->current_frame].clear(); + this->sprites_3d_to_draw[cg_core.vk_swapchain->current_frame].clear(); for(auto &view: this->views_2d) view->sprites_to_draw[cg_core.vk_swapchain->current_frame].clear(); for(auto &view: this->views_3d) diff --git a/src/vk/renderer.hpp b/src/vk/renderer.hpp index 7c7d111..96d25ba 100644 --- a/src/vk/renderer.hpp +++ b/src/vk/renderer.hpp @@ -24,6 +24,7 @@ #include "core.hpp" #include "skeletal_mesh.hpp" #include "skeletal_model.hpp" +#include "sprite_3d.hpp" #include "static_mesh.hpp" #include "static_model.hpp" #include "queue_family.hpp" @@ -47,6 +48,8 @@ struct Renderer std::vector>>> static_models_to_draw; + std::vector>> sprites_3d_to_draw; + VkDescriptorPool descriptor_pool; std::vector> views_2d; std::vector> views_3d; diff --git a/src/vk/sprite_3d.cpp b/src/vk/sprite_3d.cpp new file mode 100644 index 0000000..9794285 --- /dev/null +++ b/src/vk/sprite_3d.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sprite_3d.hpp" + +#include "../core.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +void +load_uniform_buffers(void *obj) +{ + auto self = static_cast(obj); + + try + { + self->uniform_buffers.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->uniform_buffers.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::UDOSprite3D)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_uniform_buffers(void *obj) +{ + auto self = static_cast(obj); + + self->uniform_buffers.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast(obj); + + std::array descriptor_pool_sizes{}; + descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_sizes[0].descriptorCount = + self->uniform_buffers.size(); + descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_pool_sizes[1].descriptorCount = + cg_core.vk_swapchain->images_count; + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = self->uniform_buffers.size(); + pool_info.poolSizeCount = descriptor_pool_sizes.size(); + pool_info.pPoolSizes = descriptor_pool_sizes.data(); + + if(vkCreateDescriptorPool( + self->queue_family->device->device, &pool_info, nullptr, + &self->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; +} + +void +unload_descriptor_set_pool(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyDescriptorPool( + self->queue_family->device->device, self->descriptor_pool, nullptr); +} +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + std::vector layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_descriptor_set_layout->model); + + VkDescriptorSetAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = self->descriptor_pool; + alloc_info.descriptorSetCount = layouts.size(); + alloc_info.pSetLayouts = layouts.data(); + + self->descriptor_sets.resize(layouts.size()); + if(vkAllocateDescriptorSets( + self->queue_family->device->device, &alloc_info, + self->descriptor_sets.data()) != VK_SUCCESS) + CommandError{"Failed to create Vulkan descriptor set."}; +} + +void +load_buffers_to_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + for(auto i{0}; i < self->uniform_buffers.size(); i++) + { + VkDescriptorBufferInfo buffer_info{}; + buffer_info.buffer = self->uniform_buffers[i].buffer; + buffer_info.offset = 0; + buffer_info.range = sizeof(VK::UDOSprite3D); + + VkDescriptorImageInfo image_info{}; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = self->sprite->texture->view; + image_info.sampler = self->sprite->texture->sampler; + + std::array write_descriptors{}; + write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[0].dstSet = self->descriptor_sets[i]; + write_descriptors[0].dstBinding = 0; + write_descriptors[0].dstArrayElement = 0; + write_descriptors[0].descriptorCount = 1; + write_descriptors[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptors[0].pBufferInfo = &buffer_info; + write_descriptors[0].pImageInfo = nullptr; + write_descriptors[0].pTexelBufferView = nullptr; + write_descriptors[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[1].dstSet = self->descriptor_sets[i]; + write_descriptors[1].dstBinding = 1; + write_descriptors[1].dstArrayElement = 0; + write_descriptors[1].descriptorCount = 1; + write_descriptors[1].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_descriptors[1].pBufferInfo = nullptr; + write_descriptors[1].pImageInfo = &image_info; + write_descriptors[1].pTexelBufferView = nullptr; + + vkUpdateDescriptorSets( + cg_core.vk_device_with_swapchain->device, write_descriptors.size(), + write_descriptors.data(), 0, nullptr); + } +} + +static const CommandChain loader{ + {&load_uniform_buffers, &unload_uniform_buffers}, + {&load_descriptor_set_pool, &unload_descriptor_set_pool}, + {&load_descriptor_sets, nullptr}, + {&load_buffers_to_descriptor_sets, nullptr} +}; + +} + +namespace VK +{ + +Sprite3D::Sprite3D( + std::shared_ptr sprite, std::shared_ptr position, + glm::vec2 size): + sprite{sprite}, + position{position}, + size{size} +{ + this->queue_family = + cg_core.vk_device_with_swapchain->get_queue_family_with_graphics(); + loader.execute(this); +} + +Sprite3D::~Sprite3D() +{ + loader.revert(this); +} + +} diff --git a/src/vk/sprite_3d.hpp b/src/vk/sprite_3d.hpp new file mode 100644 index 0000000..2059606 --- /dev/null +++ b/src/vk/sprite_3d.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_SPRITE_3D_H +#define CANDY_GEAR_VK_SPRITE_3D_H 1 + +#include "core.hpp" +#include "sprite.hpp" + +namespace VK +{ + +struct Sprite3D +{ + std::shared_ptr sprite; + std::shared_ptr position; + glm::vec2 size; + + QueueFamily *queue_family; + + std::vector uniform_buffers; + VkDescriptorPool descriptor_pool; + std::vector descriptor_sets; + + Sprite3D( + std::shared_ptr sprite, std::shared_ptr position, + glm::vec2 size); + ~Sprite3D(); +}; + +} + +#endif /* CANDY_GEAR_VK_SPRITE_3D_H */ diff --git a/src/vk/uniform_data_object.hpp b/src/vk/uniform_data_object.hpp index de69655..5cc22cb 100644 --- a/src/vk/uniform_data_object.hpp +++ b/src/vk/uniform_data_object.hpp @@ -68,6 +68,13 @@ struct UDOVector3D glm::vec3 vectors; }; +struct UDOSprite3D +{ + glm::vec3 position; + uint32_t padding; + glm::vec2 size; +}; + } #endif /* CANDY_GEAR_VK_UNIFORM_DATA_OBJECT_H */ diff --git a/test/src/mode/demo.rb b/test/src/mode/demo.rb index a9781b6..67fa805 100644 --- a/test/src/mode/demo.rb +++ b/test/src/mode/demo.rb @@ -66,6 +66,10 @@ module Mode mesh, texture, instance_positions[5], @instances_rotation) ] + sprite_3d_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0); + @sprite_3d = CandyGear::Sprite3D.new( + @sprite, sprite_3d_position, 1.0, 1.0); + @camera_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0); @camera_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0); @@ -125,6 +129,7 @@ module Mode @instances_rotation.rotate(0.0, BOX_ROTATION_SPEED); @rectangle.draw_rectangle(@view1, @color); @instances.each {_1.draw()}; + @sprite_3d.draw(); end end end -- cgit v1.2.3