diff options
author | Frederico Linhares <fred@linhares.blue> | 2023-09-07 15:14:14 -0300 |
---|---|---|
committer | Frederico Linhares <fred@linhares.blue> | 2023-09-15 14:12:15 -0300 |
commit | 25bf78bfb4785e2cbed683cc56d3cec4271d8b5a (patch) | |
tree | 6fa728091bd662ee0c90a7490bc5ee8e6e23cac4 /src/vk | |
parent | e1399befee43ab4549c31ce179e900ad71651edc (diff) |
feat Create skeletal mesh
Diffstat (limited to 'src/vk')
23 files changed, 1701 insertions, 159 deletions
diff --git a/src/vk/model_instance.hpp b/src/vk/animation.cpp index 1383737..fec038e 100644 --- a/src/vk/model_instance.hpp +++ b/src/vk/animation.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. @@ -14,19 +14,14 @@ * limitations under the License. */ -#ifndef CANDY_GEAR_VK_MODEL_INSTANCE_H -#define CANDY_GEAR_VK_MODEL_INSTANCE_H 1 - -#include "core.hpp" +#include "animation.hpp" namespace VK { -struct ModelInstance +Bone::Bone(glm::mat4 offset_matrix): + offset_matrix{offset_matrix} { - glm::vec3 position, rotation; -}; - } -#endif /* CANDY_GEAR_VK_MODEL_INSTANCE_H */ +} diff --git a/src/vk/animation.hpp b/src/vk/animation.hpp new file mode 100644 index 0000000..46dd5bc --- /dev/null +++ b/src/vk/animation.hpp @@ -0,0 +1,51 @@ +/* + * 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_ANIMATION_H +#define CANDY_GEAR_VK_ANIMATION_H 1 + +#include <vector> + +#include "core.hpp" +#include "animation/frame.hpp" + +namespace VK +{ + +struct Bone +{ + glm::mat4x4 offset_matrix; + + Bone(glm::mat4 offset_matrix); +}; + +struct BoneTransform +{ + uint32_t bone_id; + Channel<glm::vec3> positions; + Channel<glm::quat> rotations; + Channel<glm::vec3> scales; +}; + +struct Animation +{ + std::vector<BoneTransform> bone_transforms; + float final_time; +}; + +} + +#endif /* CANDY_GEAR_VK_ANIMATION_H */ diff --git a/src/vk/animation/frame.hpp b/src/vk/animation/frame.hpp new file mode 100644 index 0000000..953a6a6 --- /dev/null +++ b/src/vk/animation/frame.hpp @@ -0,0 +1,85 @@ +/* + * 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_FRAME_H +#define CANDY_GEAR_VK_FRAME_H 1 + +#include <vector> + +#include "../core.hpp" + +namespace VK +{ + +template<typename T> +struct Frame +{ + const T value; + const float timestamp; + + Frame(T value, float timestamp): + value{value}, + timestamp{timestamp} + { + } + +}; + +template<typename T> +struct Channel +{ + int current_index{0}; + std::vector<Frame<T>> key_frames; + + inline glm::mat4 + interpolate( + float animation_time, + glm::mat4 (*single_frame)(T frame), + glm::mat4 (*multiple_frames)(T current_frame, T next_frame, float scale)) + { + if(this->key_frames.size() == 1) + return single_frame(this->key_frames[0].value); + else + { + while(animation_time > this->key_frames[current_index].timestamp) + this->current_index++; + + float scale_factor; + Frame<T> *previous_frame; + Frame<T> *next_frame{&(this->key_frames[this->current_index])}; + if(this->current_index == 0) + { + previous_frame = &(this->key_frames[this->key_frames.size() - 1]); + float midway_length{animation_time - 0}; + float frames_diff{next_frame->timestamp - 0}; + scale_factor = midway_length / frames_diff; + } + else + { + previous_frame = &(this->key_frames[this->current_index - 1]); + float midway_length{animation_time - previous_frame->timestamp}; + float frames_diff{next_frame->timestamp - previous_frame->timestamp}; + scale_factor = midway_length / frames_diff; + } + + return multiple_frames( + previous_frame->value, next_frame->value, scale_factor); + } + }; +}; + +} +#endif /* CANDY_GEAR_VK_FRAME_H */ diff --git a/src/vk/core.hpp b/src/vk/core.hpp index 6d42bde..64d1431 100644 --- a/src/vk/core.hpp +++ b/src/vk/core.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. @@ -23,8 +23,9 @@ #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include <glm/ext/vector_float3.hpp> -#include <glm/glm.hpp> +#include <glm/ext.hpp> #include <glm/gtc/matrix_transform.hpp> +#include <glm/gtx/quaternion.hpp> #include <glm/vec3.hpp> #include <vulkan/vulkan.h> diff --git a/src/vk/graphics_pipeline_3d.cpp b/src/vk/graphics_pipeline_3d.cpp index 4eacc9d..6639d99 100644 --- a/src/vk/graphics_pipeline_3d.cpp +++ b/src/vk/graphics_pipeline_3d.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. @@ -22,7 +22,7 @@ #include "../core.hpp" #include "core.hpp" #include "image.hpp" -#include "vertex_3d.hpp" +#include "static_mesh_vertex.hpp" #include "uniform_data_object.hpp" namespace @@ -330,7 +330,7 @@ load_pipeline(void *obj) VkVertexInputBindingDescription vertex_input_binding{}; vertex_input_binding.binding = 0; - vertex_input_binding.stride = sizeof(VK::Vertex3D); + vertex_input_binding.stride = sizeof(VK::StaticMeshVertex); vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; std::array<VkVertexInputAttributeDescription, 3> vertex_attribute{}; @@ -338,17 +338,17 @@ load_pipeline(void *obj) vertex_attribute[0].location = 0; vertex_attribute[0].binding = 0; vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT; - vertex_attribute[0].offset = offsetof(VK::Vertex3D, position); + vertex_attribute[0].offset = offsetof(VK::StaticMeshVertex, position); // Normal. vertex_attribute[1].location = 1; vertex_attribute[1].binding = 0; vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT; - vertex_attribute[1].offset = offsetof(VK::Vertex3D, normal); + vertex_attribute[1].offset = offsetof(VK::StaticMeshVertex, normal); // Texture coordinate. vertex_attribute[2].location = 2; vertex_attribute[2].binding = 0; vertex_attribute[2].format = VK_FORMAT_R32G32_SFLOAT; - vertex_attribute[2].offset = offsetof(VK::Vertex3D, texture_coord); + vertex_attribute[2].offset = offsetof(VK::StaticMeshVertex, texture_coord); VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; vertex_input_info.sType = @@ -557,49 +557,48 @@ GraphicsPipeline3D::draw( } // Draw models - for(auto& [model, instances]: - cg_core.vk_renderer->models_to_draw[current_frame]) + for(auto& [static_mesh, instances]: + cg_core.vk_renderer->static_models_to_draw[current_frame]) { - std::array<VkDescriptorSet, 3> vk_descriptor_sets{ - this->descriptor_sets_world[image_index], - view->descriptor_sets_3d[image_index], - model->descriptor_sets[image_index]}; - VkBuffer vertex_buffers[]{model->mesh->vertex_buffer->buffer}; + VkBuffer vertex_buffers[]{static_mesh->vertex_buffer->buffer}; VkDeviceSize offsets[]{0}; - 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); vkCmdBindPipeline( draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, this->graphic_pipeline); vkCmdBindVertexBuffers( draw_command_buffer, 0, 1, vertex_buffers, offsets); vkCmdBindIndexBuffer( - draw_command_buffer, model->mesh->index_buffer->buffer, 0, + draw_command_buffer, static_mesh->index_buffer->buffer, 0, VK_INDEX_TYPE_UINT32); for(auto &instance: instances) { // Object matrix. - glm::mat4 instance_matrix{1.0f}; - instance_matrix = glm::translate( - instance_matrix, instance.position); - instance_matrix = glm::rotate( - instance_matrix, instance.rotation.x, glm::vec3{1.0, 0.0, 0.0}); - instance_matrix = glm::rotate( - instance_matrix, instance.rotation.y, glm::vec3{0.0, 1.0, 0.0}); - instance_matrix = glm::rotate( - instance_matrix, instance.rotation.z, glm::vec3{0.0, 0.0, 1.0}); - - ODOModelInstance model_instance{instance_matrix}; - vkCmdPushConstants( - draw_command_buffer, - cg_core.vk_graphics_pipeline_3d_layout->pipeline, - VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(ODOModelInstance), - &model_instance); + glm::mat4 base_matrix{1.0f}; + base_matrix = glm::translate(base_matrix, *instance->position); + base_matrix = glm::rotate( + base_matrix, instance->rotation->x, glm::vec3{1.0, 0.0, 0.0}); + base_matrix = glm::rotate( + base_matrix, instance->rotation->y, glm::vec3{0.0, 1.0, 0.0}); + base_matrix = glm::rotate( + base_matrix, instance->rotation->z, glm::vec3{0.0, 0.0, 1.0}); + + std::array<VkDescriptorSet, 3> vk_descriptor_sets{ + this->descriptor_sets_world[image_index], + view->descriptor_sets_3d[image_index], + instance->descriptor_sets[image_index]}; + + 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); + vkCmdDrawIndexed( - draw_command_buffer, model->mesh->index_count, 1, 0, 0, 0); + draw_command_buffer, static_mesh->index_count, 1, 0, 0, 0); + + VK::ODOStaticModel odo_static_model{}; + odo_static_model.base_matrix = base_matrix; + instance->uniform_buffers[image_index].copy_data(&odo_static_model); } } @@ -627,6 +626,7 @@ GraphicsPipeline3D::draw( view->ub_3d[image_index].copy_data(&ubo_view_3d); } + // TODO: Do not update this for each view. All views use the same data. { // Update world uniform buffer ODOWorld3D_Vert ubo_world_3d_vert{}; ubo_world_3d_vert.ambient_light_color = glm::vec4{0.25, 0.25, 0.25, 1.0}; diff --git a/src/vk/graphics_pipeline_3d.hpp b/src/vk/graphics_pipeline_3d.hpp index 925df77..d174b6e 100644 --- a/src/vk/graphics_pipeline_3d.hpp +++ b/src/vk/graphics_pipeline_3d.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. @@ -23,8 +23,6 @@ #include "../command.hpp" #include "core.hpp" #include "command_pool.hpp" -#include "model.hpp" -#include "model_instance.hpp" #include "uniform_buffer.hpp" #include "view_3d.hpp" diff --git a/src/vk/graphics_pipeline_3d_layout.cpp b/src/vk/graphics_pipeline_3d_layout.cpp index db5d210..cbb73e5 100644 --- a/src/vk/graphics_pipeline_3d_layout.cpp +++ b/src/vk/graphics_pipeline_3d_layout.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. @@ -103,18 +103,64 @@ unload_descriptor_set_view(void *obj) } void -load_descriptor_set_model_instance(void *obj) +load_descriptor_set_skeletal_model(void *obj) { auto self = static_cast<VK::GraphicsPipeline3DLayout*>(obj); - std::array<VkDescriptorSetLayoutBinding, 1> layout_bindings{}; - + std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings; layout_bindings[0].binding = 0; - layout_bindings[0].descriptorType = + layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layout_bindings[0].descriptorCount = 1; + layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layout_bindings[0].pImmutableSamplers = nullptr; + layout_bindings[1].binding = 1; + layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_bindings[1].descriptorCount = 1; + layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layout_bindings[1].pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo layout_info{}; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = nullptr; + layout_info.flags = 0; + layout_info.bindingCount = static_cast<uint32_t>(layout_bindings.size()); + layout_info.pBindings = layout_bindings.data(); + + if(vkCreateDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, + &self->descriptor_set_skeletal_model) != VK_SUCCESS) + throw CommandError{ + "Failed to create Vulkan descriptor set layout for model instance."}; +} + +void +unload_descriptor_set_skeletal_model(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DLayout*>(obj); + + vkDestroyDescriptorSetLayout( + cg_core.vk_device_with_swapchain->device, + self->descriptor_set_skeletal_model, nullptr); +} + +void +load_descriptor_set_static_model(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DLayout*>(obj); + + std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings; + layout_bindings[0].binding = 0; + layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; layout_bindings[0].descriptorCount = 1; - layout_bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; layout_bindings[0].pImmutableSamplers = nullptr; + layout_bindings[1].binding = 1; + layout_bindings[1].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_bindings[1].descriptorCount = 1; + layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layout_bindings[1].pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo layout_info{}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; @@ -125,19 +171,19 @@ load_descriptor_set_model_instance(void *obj) if(vkCreateDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, &layout_info, nullptr, - &self->descriptor_set_model_instance) != VK_SUCCESS) + &self->descriptor_set_static_model) != VK_SUCCESS) throw CommandError{ "Failed to create Vulkan descriptor set layout for model instance."}; } void -unload_descriptor_set_model_instance(void *obj) +unload_descriptor_set_static_model(void *obj) { auto self = static_cast<VK::GraphicsPipeline3DLayout*>(obj); vkDestroyDescriptorSetLayout( cg_core.vk_device_with_swapchain->device, - self->descriptor_set_model_instance, nullptr); + self->descriptor_set_static_model, nullptr); } void @@ -148,19 +194,14 @@ load_pipeline(void *obj) std::array<VkDescriptorSetLayout, 3> set_layouts{ self->descriptor_set_world, self->descriptor_set_view, - self->descriptor_set_model_instance}; - - std::array<VkPushConstantRange, 1> push_constants; - push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constants[0].offset = 0; - push_constants[0].size = sizeof(VK::ODOModelInstance); + self->descriptor_set_skeletal_model}; VkPipelineLayoutCreateInfo pipeline_layout_info{}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = set_layouts.size(); pipeline_layout_info.pSetLayouts = set_layouts.data(); - pipeline_layout_info.pushConstantRangeCount = push_constants.size(); - pipeline_layout_info.pPushConstantRanges = push_constants.data(); + pipeline_layout_info.pushConstantRangeCount = 0; + pipeline_layout_info.pPushConstantRanges = nullptr; if(vkCreatePipelineLayout( cg_core.vk_device_with_swapchain->device, @@ -266,7 +307,8 @@ unload_render_pass(void *obj) const CommandChain loader{ {&load_descriptor_set_world, &unload_descriptor_set_world}, {&load_descriptor_set_view, &unload_descriptor_set_view}, - {&load_descriptor_set_model_instance, &unload_descriptor_set_model_instance}, + {&load_descriptor_set_skeletal_model, &unload_descriptor_set_skeletal_model}, + {&load_descriptor_set_static_model, &unload_descriptor_set_static_model}, {&load_pipeline, &unload_pipeline}, {&load_render_pass, &unload_render_pass} }; diff --git a/src/vk/graphics_pipeline_3d_layout.hpp b/src/vk/graphics_pipeline_3d_layout.hpp index 10c8bd9..cb89b95 100644 --- a/src/vk/graphics_pipeline_3d_layout.hpp +++ b/src/vk/graphics_pipeline_3d_layout.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. @@ -26,7 +26,8 @@ struct GraphicsPipeline3DLayout { VkDescriptorSetLayout descriptor_set_world; VkDescriptorSetLayout descriptor_set_view; - VkDescriptorSetLayout descriptor_set_model_instance; + VkDescriptorSetLayout descriptor_set_skeletal_model; + VkDescriptorSetLayout descriptor_set_static_model; VkPipelineLayout pipeline; VkRenderPass render_pass; diff --git a/src/vk/graphics_pipeline_3d_skeletal.cpp b/src/vk/graphics_pipeline_3d_skeletal.cpp new file mode 100644 index 0000000..3e1db66 --- /dev/null +++ b/src/vk/graphics_pipeline_3d_skeletal.cpp @@ -0,0 +1,657 @@ +/* + * 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_3d.hpp" + +#include <array> +#include <stdexcept> + +#include "../core.hpp" +#include "core.hpp" +#include "image.hpp" +#include "skeletal_mesh_vertex.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +void +load_world_vert_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + try + { + self->ub_world_vert.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->ub_world_vert.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::ODOWorld3D_Vert)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_world_vert_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + self->ub_world_vert.clear(); +} + +void +load_world_frag_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + try + { + self->ub_world_frag.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->ub_world_frag.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::ODOWorld3D_Frag)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_world_frag_uniform_buffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + self->ub_world_frag.clear(); +} + +void +load_descriptor_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + uint32_t uniform_buffers_count = + self->ub_world_vert.size() + self->ub_world_vert.size(); + + VkDescriptorPoolSize descriptor_pool_size{}; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_size.descriptorCount = uniform_buffers_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 = uniform_buffers_count; + pool_info.poolSizeCount = 1; + pool_info.pPoolSizes = &descriptor_pool_size; + + if(vkCreateDescriptorPool( + cg_core.vk_device_with_swapchain->device, &pool_info, nullptr, + &self->descriptor_pool) != VK_SUCCESS) + throw CommandError{"Failed to create a Vulkan descriptor pool."}; +} + +void +unload_descriptor_pool(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + vkDestroyDescriptorPool( + cg_core.vk_device_with_swapchain->device, self->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets_world(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + std::vector<VkDescriptorSetLayout> layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_world); + + 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_world.resize(layouts.size()); + if(vkAllocateDescriptorSets( + cg_core.vk_device_with_swapchain->device, &alloc_info, + self->descriptor_sets_world.data()) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan world descriptor set."}; +} + +void +load_resources_to_descriptor_sets(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + { + VkDescriptorBufferInfo world_vert_info{}; + world_vert_info.buffer = self->ub_world_vert[i].buffer; + world_vert_info.offset = 0; + world_vert_info.range = sizeof(VK::ODOWorld3D_Vert); + + VkDescriptorBufferInfo world_frag_info{}; + world_frag_info.buffer = self->ub_world_frag[i].buffer; + world_frag_info.offset = 0; + world_frag_info.range = sizeof(VK::ODOWorld3D_Frag); + + std::array<VkWriteDescriptorSet, 2> write_descriptors{}; + write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_descriptors[0].dstSet = self->descriptor_sets_world[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 = &world_vert_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_world[i]; + write_descriptors[1].dstBinding = 1; + write_descriptors[1].dstArrayElement = 0; + write_descriptors[1].descriptorCount = 1; + write_descriptors[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_descriptors[1].pBufferInfo = &world_frag_info; + write_descriptors[1].pImageInfo = nullptr; + write_descriptors[1].pTexelBufferView = nullptr; + + vkUpdateDescriptorSets( + cg_core.vk_device_with_swapchain->device, write_descriptors.size(), + write_descriptors.data(), 0, nullptr); + } +} + +void +load_depth_image(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + VkExtent3D extent3d{}; + extent3d.width = cg_core.display_width; + extent3d.height = cg_core.display_height; + extent3d.depth = 1; + + try + { + VK::Image::create( + cg_core.vk_device_with_swapchain, + &self->depth_image, + &self->depth_image_memory, + VK_FORMAT_D32_SFLOAT, + extent3d, + 1, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + ); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + vkDestroyImage( + cg_core.vk_device_with_swapchain->device, self->depth_image, + nullptr); + vkFreeMemory( + cg_core.vk_device_with_swapchain->device, + self->depth_image_memory, nullptr); +} + +void +load_depth_image_view(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + try + { + VK::Image::create_view( + cg_core.vk_device_with_swapchain, &self->depth_image_view, + self->depth_image, + VK_FORMAT_D32_SFLOAT, VK_IMAGE_ASPECT_DEPTH_BIT); + } + catch(VK::Image::Error error) + { + std::string error_message{"Failed to create depth image view → "}; + error_message += error.what(); + throw CommandError{error_message}; + } +} + +void +unload_depth_image_view(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); +} + +void +load_framebuffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + self->swapchain_framebuffers.resize(cg_core.vk_swapchain->images_count); + for (auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + { + std::array<VkImageView, 2> attachments = { + cg_core.vk_swapchain->image_views[i], + self->depth_image_view + }; + + VkFramebufferCreateInfo framebuffer_info{}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = + cg_core.vk_graphics_pipeline_3d_layout->render_pass; + framebuffer_info.attachmentCount = attachments.size(); + framebuffer_info.pAttachments = attachments.data(); + framebuffer_info.width = cg_core.display_width; + framebuffer_info.height = cg_core.display_height; + + framebuffer_info.layers = 1; + + if(vkCreateFramebuffer( + cg_core.vk_device_with_swapchain->device, &framebuffer_info, nullptr, + &self->swapchain_framebuffers[i]) != VK_SUCCESS) + throw CommandError{"Failed to create Vulkan Framebuffer."}; + } +} + +void +unload_framebuffer(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + for(auto framebuffer: self->swapchain_framebuffers) + vkDestroyFramebuffer( + cg_core.vk_device_with_swapchain->device, framebuffer, nullptr); +} + +void +load_pipeline(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(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->vert3d_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->frag3d_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(VK::SkeletalMeshVertex); + vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::array<VkVertexInputAttributeDescription, 5> vertex_attribute{}; + // Position. + vertex_attribute[0].location = 0; + vertex_attribute[0].binding = 0; + vertex_attribute[0].format = VK_FORMAT_R32G32B32_SFLOAT; + vertex_attribute[0].offset = offsetof(VK::SkeletalMeshVertex, position); + // Normal. + vertex_attribute[1].location = 1; + vertex_attribute[1].binding = 0; + vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT; + vertex_attribute[1].offset = offsetof(VK::SkeletalMeshVertex, normal); + // Texture coordinate. + vertex_attribute[2].location = 2; + vertex_attribute[2].binding = 0; + vertex_attribute[2].format = VK_FORMAT_R32G32_SFLOAT; + vertex_attribute[2].offset = offsetof(VK::SkeletalMeshVertex, texture_coord); + // Bones ids. + vertex_attribute[3].location = 3; + vertex_attribute[3].binding = 0; + vertex_attribute[3].format = VK_FORMAT_R32G32B32A32_SINT; + vertex_attribute[3].offset = offsetof(VK::SkeletalMeshVertex, bone_ids); + // Bones weights. + vertex_attribute[4].location = 4; + vertex_attribute[4].binding = 0; + vertex_attribute[4].format = VK_FORMAT_R32G32B32A32_SFLOAT; + vertex_attribute[4].offset = offsetof(VK::SkeletalMeshVertex, bone_weights); + + 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<uint32_t>(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_LIST; + 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_FALSE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + 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; + + VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_LINE_WIDTH + }; + + VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; + dynamic_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_info.dynamicStateCount = 2; + dynamic_state_info.pDynamicStates = dynamic_states; + + 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_graphics_pipeline_3d_layout->render_pass; + 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."}; +} + +void +unload_pipeline(void *obj) +{ + auto self = static_cast<VK::GraphicsPipeline3DSkeletal*>(obj); + + vkDestroyPipeline( + cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr); +} + +const CommandChain loader{ + {&load_world_vert_uniform_buffer, &unload_world_vert_uniform_buffer}, + {&load_world_frag_uniform_buffer, &unload_world_frag_uniform_buffer}, + {&load_descriptor_pool, &unload_descriptor_pool}, + // By destroying the pool the sets are also destroyed. + {&load_descriptor_sets_world, nullptr}, + {&load_resources_to_descriptor_sets, nullptr}, + {&load_depth_image, &unload_depth_image}, + {&load_depth_image_view, &unload_depth_image_view}, + {&load_framebuffer, &unload_framebuffer}, + {&load_pipeline, &unload_pipeline} +}; + +} + +namespace VK +{ + +GraphicsPipeline3DSkeletal::GraphicsPipeline3DSkeletal() +{ + loader.execute(this); +} + +GraphicsPipeline3DSkeletal::~GraphicsPipeline3DSkeletal() +{ + loader.revert(this); +} + +void +GraphicsPipeline3DSkeletal::draw( + std::shared_ptr<View3D> view, const VkCommandBuffer draw_command_buffer, + const size_t current_frame, const uint32_t image_index) +{ + { // 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<int32_t>(view->region.x); + vk_scissor.offset.y = static_cast<int32_t>(view->region.y); + vk_scissor.extent.width = static_cast<uint32_t>(view->region.z); + vk_scissor.extent.height = static_cast<uint32_t>(view->region.w); + vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); + } + + // Draw models + for(auto& [skeletal_mesh, instances]: + cg_core.vk_renderer->skeletal_models_to_draw[current_frame]) + { + VkBuffer vertex_buffers[]{skeletal_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( + draw_command_buffer, skeletal_mesh->index_buffer->buffer, 0, + VK_INDEX_TYPE_UINT32); + + for(auto &instance: instances) + { // Object matrix. + glm::mat4 base_matrix{1.0f}; + base_matrix = glm::translate(base_matrix, *instance->position); + base_matrix = glm::rotate( + base_matrix, instance->rotation->x, glm::vec3{1.0, 0.0, 0.0}); + base_matrix = glm::rotate( + base_matrix, instance->rotation->y, glm::vec3{0.0, 1.0, 0.0}); + base_matrix = glm::rotate( + base_matrix, instance->rotation->z, glm::vec3{0.0, 0.0, 1.0}); + + std::array<VkDescriptorSet, 3> vk_descriptor_sets{ + this->descriptor_sets_world[image_index], + view->descriptor_sets_3d[image_index], + instance->descriptor_sets[image_index]}; + + 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); + + vkCmdDrawIndexed( + draw_command_buffer, skeletal_mesh->index_count, 1, 0, 0, 0); + + VK::ODOSkeletalModel odo_skeletal_model{}; + instance->tick(cg_core.delta_time); + odo_skeletal_model.base_matrix = base_matrix; + std::copy(instance->bone_transforms.begin(), + instance->bone_transforms.end(), + odo_skeletal_model.bone_matrices); + instance->uniform_buffers[image_index].copy_data(&odo_skeletal_model); + } + } + + { // Update view uniform buffers + VK::ODOView3D ubo_view_3d{}; + + // View matrix. + ubo_view_3d.view = glm::mat4{1.0f}; + ubo_view_3d.view = glm::translate( + ubo_view_3d.view, *view->camera_position); + ubo_view_3d.view = glm::rotate( + ubo_view_3d.view, view->camera_rotation->y, glm::vec3{0.0, 1.0, 0.0}); + ubo_view_3d.view = glm::rotate( + ubo_view_3d.view, view->camera_rotation->x, glm::vec3{1.0, 0.0, 0.0}); + ubo_view_3d.view = glm::rotate( + ubo_view_3d.view, view->camera_rotation->z, glm::vec3{0.0, 0.0, 1.0}); + ubo_view_3d.view = glm::inverse(ubo_view_3d.view); + + // Projection matrix. + ubo_view_3d.proj = glm::perspective( + glm::radians(45.0f), + view->region.z / view->region.w, + 0.1f, 100.0f); + + view->ub_3d[image_index].copy_data(&ubo_view_3d); + } + + // TODO: Do not update this for each view. All views use the same data. + { // Update world uniform buffer + ODOWorld3D_Vert ubo_world_3d_vert{}; + ubo_world_3d_vert.ambient_light_color = glm::vec4{0.25, 0.25, 0.25, 1.0}; + this->ub_world_vert[image_index].copy_data(&ubo_world_3d_vert); + + ODOWorld3D_Frag ubo_world_3d_frag{}; + ubo_world_3d_frag.directional_light_direction = + glm::vec3{-0.57735, 0.57735, -0.57735}; + ubo_world_3d_frag.directional_light_color = glm::vec4{0.8, 0.8, 0.8, 1.0}; + this->ub_world_frag[image_index].copy_data(&ubo_world_3d_frag); + } +} + +} diff --git a/src/vk/graphics_pipeline_3d_skeletal.hpp b/src/vk/graphics_pipeline_3d_skeletal.hpp new file mode 100644 index 0000000..9563e14 --- /dev/null +++ b/src/vk/graphics_pipeline_3d_skeletal.hpp @@ -0,0 +1,60 @@ +/* + * 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_3D_SKELETAL_H +#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_3D_SKELETAL_H 1 + +#include <memory> +#include <unordered_map> + +#include "../command.hpp" +#include "core.hpp" +#include "command_pool.hpp" +#include "skeletal_model.hpp" +#include "uniform_buffer.hpp" +#include "view_3d.hpp" + +namespace VK +{ + +struct GraphicsPipeline3DSkeletal +{ + // Depth image. + VkImage depth_image; + VkDeviceMemory depth_image_memory; + VkImageView depth_image_view; + + // FIXME: if this vector get resized, it will cause a segmentation fault! + std::vector<UniformBuffer> ub_world_vert; + std::vector<UniformBuffer> ub_world_frag; + + VkDescriptorPool descriptor_pool; + std::vector<VkDescriptorSet> descriptor_sets_world; + + std::vector<VkFramebuffer> swapchain_framebuffers; + VkPipeline graphic_pipeline; + + GraphicsPipeline3DSkeletal(); + ~GraphicsPipeline3DSkeletal(); + + void + draw(std::shared_ptr<View3D> view, const VkCommandBuffer draw_command_buffer, + const size_t current_frame, const uint32_t image_index); +}; + +} + +#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_SKELETAL_3D_H */ diff --git a/src/vk/renderer.cpp b/src/vk/renderer.cpp index a7e5f35..b240cb9 100644 --- a/src/vk/renderer.cpp +++ b/src/vk/renderer.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. @@ -143,7 +143,8 @@ namespace VK Renderer::Renderer(std::vector<std::shared_ptr<View2D>> views_2d, std::vector<std::shared_ptr<View3D>> views_3d): - models_to_draw{cg_core.vk_swapchain->images_count}, + skeletal_models_to_draw{cg_core.vk_swapchain->images_count}, + static_models_to_draw{cg_core.vk_swapchain->images_count}, views_2d{views_2d}, views_3d{views_3d} { @@ -347,14 +348,15 @@ Renderer::draw() // Prepare for the next frame. { - this->models_to_draw[next_frame].clear(); + this->skeletal_models_to_draw[next_frame].clear(); + this->static_models_to_draw[next_frame].clear(); cg_core.vk_swapchain->current_frame = next_frame; } } else { // Clear images for the current frame because we are skipping this frame. - this->models_to_draw[cg_core.vk_swapchain->current_frame].clear(); + this->skeletal_models_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 551e4e5..7c7d111 100644 --- a/src/vk/renderer.hpp +++ b/src/vk/renderer.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. @@ -22,8 +22,10 @@ #include <vector> #include "core.hpp" -#include "model.hpp" -#include "model_instance.hpp" +#include "skeletal_mesh.hpp" +#include "skeletal_model.hpp" +#include "static_mesh.hpp" +#include "static_model.hpp" #include "queue_family.hpp" #include "view_2d.hpp" #include "view_3d.hpp" @@ -33,9 +35,17 @@ namespace VK struct Renderer { - std::vector<std::unordered_map< - std::shared_ptr<Model>, std::vector<ModelInstance>>> - models_to_draw; + std::vector< + std::unordered_map< + std::shared_ptr<SkeletalMesh>, + std::vector<std::shared_ptr<SkeletalModel>>>> + skeletal_models_to_draw; + + std::vector< + std::unordered_map< + std::shared_ptr<StaticMesh>, + std::vector<std::shared_ptr<StaticModel>>>> + static_models_to_draw; VkDescriptorPool descriptor_pool; std::vector<std::shared_ptr<View2D>> views_2d; diff --git a/src/vk/skeletal_mesh.cpp b/src/vk/skeletal_mesh.cpp new file mode 100644 index 0000000..b65d27d --- /dev/null +++ b/src/vk/skeletal_mesh.cpp @@ -0,0 +1,204 @@ +/* + * 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 "skeletal_mesh.hpp" + +#include "../binary_reader.hpp" +#include "../command.hpp" +#include "../core.hpp" +#include "skeletal_mesh_vertex.hpp" + +namespace +{ + +// Data that is only needed for the command chain but not for the SkeletalMesh +// goes here. +struct MeshBuilder +{ + std::string mesh_path; + VK::SkeletalMesh *mesh; + + MeshBuilder(VK::SkeletalMesh *m, std::string mp); + MeshBuilder(VK::SkeletalMesh *m, const char* mp); +}; + +MeshBuilder::MeshBuilder(VK::SkeletalMesh *m, std::string mp): + mesh{m}, + mesh_path{mp} +{ +} + +MeshBuilder::MeshBuilder(VK::SkeletalMesh *m, const char *mp): + MeshBuilder{m, std::string(mp)} +{ +} + +void +load_mesh(void *obj) +{ + auto self = static_cast<MeshBuilder*>(obj); + + BinaryReader input{self->mesh_path}; + + self->mesh->queue_family = + cg_core.vk_device_with_swapchain->get_queue_family_with_graphics(); + + { // Load vertexes. + auto vertex_count{input.read_ui32()}; + std::vector<VK::SkeletalMeshVertex> vertexes{vertex_count}; + + for(auto i{0}; i < vertex_count; i++) + { + vertexes[i].position = input.read_vec3(); + vertexes[i].normal = input.read_vec3(); + vertexes[i].texture_coord = input.read_vec2(); + + for(auto ii{0}; ii < VK::SKELETAL_MESH_MAX_NUM_OF_INFLUENCING_BONES; + ii++) + vertexes[i].bone_ids[ii] = input.read_ui32(); + + for(auto ii{0}; ii < VK::SKELETAL_MESH_MAX_NUM_OF_INFLUENCING_BONES; + ii++) + vertexes[i].bone_weights[ii] = input.read_float(); + } + + void *vertexes_data{vertexes.data()}; + size_t vertexes_size = sizeof(vertexes[0]) * vertexes.size(); + self->mesh->source_vertex_buffer = new VK::SourceBuffer{ + self->mesh->queue_family->device, vertexes_data, vertexes_size}; + self->mesh->vertex_buffer = new VK::DestinationBuffer{ + self->mesh->queue_family, self->mesh->source_vertex_buffer, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT}; + } + + { // Load indexes. + self->mesh->index_count = input.read_ui32(); + std::vector<uint32_t> indexes(self->mesh->index_count); + + for(auto i{0}; i < self->mesh->index_count; i++) + indexes[i] = input.read_ui32(); + + void *indexes_data{indexes.data()}; + size_t indexes_size{sizeof(indexes[0]) * indexes.size()}; + VK::SourceBuffer source_index_buffer{ + self->mesh->queue_family->device, indexes_data, indexes_size}; + self->mesh->index_buffer = new VK::DestinationBuffer{ + self->mesh->queue_family, &source_index_buffer, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT}; + } + + { // Load bones + auto bone_count{input.read_ui32()}; + self->mesh->bones.reserve(bone_count); + for(int i{0}; i < bone_count; i++) + self->mesh->bones.emplace_back(input.read_mat4()); + } + + { // Load animations + auto num_animations{input.read_ui32()}; + self->mesh->animations.resize(num_animations); + for(uint32_t i{0}; i < num_animations; i++) + { + auto duration{input.read_double()}; + self->mesh->animations[i].final_time = (float)duration; + + auto ticks_per_second{input.read_double()}; + + auto num_bone_transforms{input.read_ui32()}; + std::vector<VK::BoneTransform> *bone_transforms = + &(self->mesh->animations[i].bone_transforms); + bone_transforms->resize(num_bone_transforms); + for(uint32_t bone_transform_index{0}; + bone_transform_index < num_bone_transforms; bone_transform_index++) + { + auto bone_id{input.read_ui32()}; + + auto num_positions{input.read_ui32()}; + VK::Channel<glm::vec3> *positions = + &((*bone_transforms)[bone_transform_index].positions); + for(auto position_key_index{0}; position_key_index < num_positions; + position_key_index++) + { + auto vec3{input.read_vec3()}; + auto timestamp{input.read_double()}; + positions->key_frames.emplace_back( + vec3, static_cast<float>(timestamp)); + } + + auto num_rotations{input.read_ui32()}; + VK::Channel<glm::quat> *rotations = + &((*bone_transforms)[bone_transform_index].rotations); + for(auto rotation_key_index{0}; rotation_key_index < num_rotations; + rotation_key_index++) + { + auto quat{input.read_quat()}; + auto timestamp{input.read_double()}; + rotations->key_frames.emplace_back( + quat, static_cast<float>(timestamp)); + } + + auto num_scales{input.read_ui32()}; + VK::Channel<glm::vec3> *scales = + &((*bone_transforms)[bone_transform_index].scales); + for(auto scaling_key_index{0}; scaling_key_index < num_scales; + scaling_key_index++) + { + auto vec3{input.read_vec3()}; + auto timestamp{input.read_double()}; + scales->key_frames.emplace_back(vec3, static_cast<float>(timestamp)); + } + } + } + } +} + +void +unload_mesh(void *obj) +{ + auto self = static_cast<MeshBuilder*>(obj); + + delete self->mesh->index_buffer; + delete self->mesh->vertex_buffer; + delete self->mesh->source_vertex_buffer; +} + +static const CommandChain loader{ + {&load_mesh, &unload_mesh} +}; + +} + +namespace VK +{ + +SkeletalMesh::SkeletalMesh(std::string mesh_path) +{ + MeshBuilder mesh_builder(this, mesh_path); + loader.execute(&mesh_builder); +} + +SkeletalMesh::SkeletalMesh(const char* mesh_path): + SkeletalMesh{std::string(mesh_path)} +{ +} + +SkeletalMesh::~SkeletalMesh() +{ + MeshBuilder mesh_builder(this, ""); + loader.revert(&mesh_builder); +} + +} diff --git a/src/vk/skeletal_mesh.hpp b/src/vk/skeletal_mesh.hpp new file mode 100644 index 0000000..7fc8334 --- /dev/null +++ b/src/vk/skeletal_mesh.hpp @@ -0,0 +1,52 @@ +/* + * 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_SKELETAL_MESH_H +#define CANDY_GEAR_VK_SKELETAL_MESH_H 1 + +#include <string> +#include <vector> + +#include "animation.hpp" +#include "core.hpp" +#include "destination_buffer.hpp" +#include "queue_family.hpp" +#include "uniform_buffer.hpp" +#include "texture.hpp" + +namespace VK +{ + +struct SkeletalMesh +{ + QueueFamily *queue_family; + + uint32_t index_count; + SourceBuffer *source_vertex_buffer; + DestinationBuffer *index_buffer; + DestinationBuffer *vertex_buffer; + + std::vector<Bone> bones; + std::vector<Animation> animations; + + SkeletalMesh(std::string mesh_path); + SkeletalMesh(const char* mesh_path); + ~SkeletalMesh(); +}; + +} + +#endif /* CANDY_GEAR_VK_SKELETAL_MESH_H */ diff --git a/src/vk/skeletal_mesh_vertex.hpp b/src/vk/skeletal_mesh_vertex.hpp new file mode 100644 index 0000000..b5bba3d --- /dev/null +++ b/src/vk/skeletal_mesh_vertex.hpp @@ -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. + */ + +#ifndef CANDY_GEAR_VK_SKELETAL_MESH_VERTEX_H +#define CANDY_GEAR_VK_SKELETAL_MESH_VERTEX_H 1 + +#include "core.hpp" + +namespace VK +{ + +// This variable define the maximum ammount of bones that can influence a +// vertex. +const int SKELETAL_MESH_MAX_NUM_OF_INFLUENCING_BONES{4}; +const int SKELETAL_MESH_MAX_NUM_OF_BONES{50}; + +struct SkeletalMeshVertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texture_coord; + + uint32_t bone_ids[SKELETAL_MESH_MAX_NUM_OF_INFLUENCING_BONES]; + float bone_weights[SKELETAL_MESH_MAX_NUM_OF_INFLUENCING_BONES]; +}; + +} + +#endif /* CANDY_GEAR_VK_SKELETAL_MESH_VERTEX_H */ diff --git a/src/vk/skeletal_model.cpp b/src/vk/skeletal_model.cpp new file mode 100644 index 0000000..8528535 --- /dev/null +++ b/src/vk/skeletal_model.cpp @@ -0,0 +1,256 @@ +/* + * 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 "skeletal_model.hpp" + +#include "../core.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +void +load_uniform_buffers(void *obj) +{ + auto self = static_cast<VK::SkeletalModel*>(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::ODOSkeletalModel)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_uniform_buffers(void *obj) +{ + auto self = static_cast<VK::SkeletalModel*>(obj); + + self->uniform_buffers.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast<VK::SkeletalModel*>(obj); + + std::array<VkDescriptorPoolSize, 2> 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->skeletal_mesh->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<VK::SkeletalModel*>(obj); + + vkDestroyDescriptorPool( + self->skeletal_mesh->queue_family->device->device, self->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast<VK::SkeletalModel*>(obj); + + std::vector<VkDescriptorSetLayout> layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_skeletal_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->skeletal_mesh->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<VK::SkeletalModel*>(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::ODOSkeletalModel); + + VkDescriptorImageInfo image_info{}; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = self->texture->view; + image_info.sampler = self->texture->sampler; + + std::array<VkWriteDescriptorSet, 2> 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 +{ + +SkeletalModel::SkeletalModel( + std::shared_ptr<SkeletalMesh> skeletal_mesh, + std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position, + std::shared_ptr<glm::vec3> rotation): + skeletal_mesh{skeletal_mesh}, + texture{texture}, + position{position}, + rotation{rotation}, + animation_index{0}, + animation_time{0.0f}, + bone_transforms(SKELETAL_MESH_MAX_NUM_OF_BONES) +{ + loader.execute(this); + + for(int i{0}; i < skeletal_mesh->bones.size(); i++) + this->bone_transforms[i] = skeletal_mesh->bones[i].offset_matrix; +} + +SkeletalModel::~SkeletalModel() +{ + loader.revert(this); +} + +void +SkeletalModel::tick(float delta) +{ + VK::Animation *current_animation = + &this->skeletal_mesh->animations[this->animation_index]; + + { // update time + this->animation_time += delta; + if(this->animation_time > current_animation->final_time) + { + this->animation_time -= current_animation->final_time; + for(VK::BoneTransform &bone_transform: + current_animation->bone_transforms) + { + bone_transform.positions.current_index = 0; + bone_transform.rotations.current_index = 0; + bone_transform.scales.current_index = 0; + } + } + } + + for(int i{0}; i < current_animation->bone_transforms.size(); i++) + { + VK::BoneTransform *bone_transform = ¤t_animation->bone_transforms[i]; + + auto position{bone_transform->positions.interpolate( + this->animation_time, + [](glm::vec3 frame) + { + return glm::translate(glm::mat4(1.0f), frame); + }, + [](glm::vec3 previous_frame, glm::vec3 next_frame, float scale_factor) + { + glm::vec3 final_position{glm::mix( + previous_frame, next_frame, scale_factor)}; + return glm::translate(glm::mat4(1.0f), final_position); + })}; + + auto rotation{bone_transform->rotations.interpolate( + this->animation_time, + [](glm::quat frame) + { + return glm::toMat4(glm::normalize(frame)); + }, + [](glm::quat previous_frame, glm::quat next_frame, float scale_factor) + { + return glm::toMat4(glm::slerp( + previous_frame, next_frame, scale_factor)); + })}; + + auto scale{bone_transform->scales.interpolate( + this->animation_time, + [](glm::vec3 frame) + { + return glm::scale(glm::mat4(1.0f), frame); + }, + [](glm::vec3 previous_frame, glm::vec3 next_frame, float scale_factor) + { + glm::vec3 scale{glm::mix( + previous_frame, next_frame, scale_factor)}; + return glm::scale(glm::mat4(1.0f), scale); + })}; + + this->bone_transforms[i] = position * rotation * scale; + } +} + +} diff --git a/src/vk/skeletal_model.hpp b/src/vk/skeletal_model.hpp new file mode 100644 index 0000000..1043e0f --- /dev/null +++ b/src/vk/skeletal_model.hpp @@ -0,0 +1,54 @@ +/* + * 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_SKELETAL_MODEL_H +#define CANDY_GEAR_VK_SKELETAL_MODEL_H 1 + +#include <memory> +#include <vector> + +#include "core.hpp" +#include "skeletal_mesh.hpp" + +namespace VK +{ + +struct SkeletalModel +{ + std::shared_ptr<SkeletalMesh> skeletal_mesh; + std::shared_ptr<Texture> texture; + std::vector<UniformBuffer> uniform_buffers; + std::shared_ptr<glm::vec3> position, rotation; + int animation_index; + float animation_time; + std::vector<glm::mat4> bone_transforms; + + VkDescriptorPool descriptor_pool; + std::vector<VkDescriptorSet> descriptor_sets; + + SkeletalModel( + std::shared_ptr<SkeletalMesh> skeletal_mesh, + std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position, + std::shared_ptr<glm::vec3> rotation); + ~SkeletalModel(); + + void + tick(float delta); +}; + +} + +#endif /* CANDY_GEAR_VK_SKELETAL_MODEL_H */ diff --git a/src/vk/mesh.cpp b/src/vk/static_mesh.cpp index 9afeac3..29034df 100644 --- a/src/vk/mesh.cpp +++ b/src/vk/static_mesh.cpp @@ -14,34 +14,34 @@ * limitations under the License. */ -#include "mesh.hpp" +#include "static_mesh.hpp" #include "../binary_reader.hpp" #include "../command.hpp" #include "../core.hpp" -#include "vertex_3d.hpp" +#include "static_mesh_vertex.hpp" namespace { -// Data that is only needed for the command chain but not for the Mesh goes -// here. +// Data that is only needed for the command chain but not for the StaticMesh +// goes here. struct MeshBuilder { std::string mesh_path; - VK::Mesh *mesh; + VK::StaticMesh *mesh; - MeshBuilder(VK::Mesh *m, std::string mp); - MeshBuilder(VK::Mesh *m, const char* mp); + MeshBuilder(VK::StaticMesh *m, std::string mp); + MeshBuilder(VK::StaticMesh *m, const char* mp); }; -MeshBuilder::MeshBuilder(VK::Mesh *m, std::string mp): +MeshBuilder::MeshBuilder(VK::StaticMesh *m, std::string mp): mesh{m}, mesh_path{mp} { } -MeshBuilder::MeshBuilder(VK::Mesh *m, const char *mp): +MeshBuilder::MeshBuilder(VK::StaticMesh *m, const char *mp): MeshBuilder{m, std::string(mp)} { } @@ -56,10 +56,9 @@ load_mesh(void *obj) self->mesh->queue_family = cg_core.vk_device_with_swapchain->get_queue_family_with_graphics(); - // Load vertexes. - { + { // Load vertexes. auto vertex_count{input.read_ui32()}; - std::vector<VK::Vertex3D> vertexes{vertex_count}; + std::vector<VK::StaticMeshVertex> vertexes{vertex_count}; for(auto i{0}; i < vertex_count; i++) { @@ -77,8 +76,7 @@ load_mesh(void *obj) VK_BUFFER_USAGE_VERTEX_BUFFER_BIT}; } - // Load indexes. - { + { // Load indexes self->mesh->index_count = input.read_ui32(); std::vector<uint32_t> indexes(self->mesh->index_count); @@ -114,18 +112,18 @@ static const CommandChain loader{ namespace VK { -Mesh::Mesh(std::string mesh_path) +StaticMesh::StaticMesh(std::string mesh_path) { MeshBuilder mesh_builder(this, mesh_path); loader.execute(&mesh_builder); } -Mesh::Mesh(const char* mesh_path): - Mesh{std::string(mesh_path)} +StaticMesh::StaticMesh(const char* mesh_path): + StaticMesh{std::string(mesh_path)} { } -Mesh::~Mesh() +StaticMesh::~StaticMesh() { MeshBuilder mesh_builder(this, ""); loader.revert(&mesh_builder); diff --git a/src/vk/mesh.hpp b/src/vk/static_mesh.hpp index 97b9d8a..0ab38b2 100644 --- a/src/vk/mesh.hpp +++ b/src/vk/static_mesh.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. @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef CANDY_GEAR_VK_MESH_H -#define CANDY_GEAR_VK_MESH_H 1 +#ifndef CANDY_GEAR_VK_STATIC_MESH_H +#define CANDY_GEAR_VK_STATIC_MESH_H 1 #include <string> #include <vector> @@ -29,7 +29,7 @@ namespace VK { -struct Mesh +struct StaticMesh { QueueFamily *queue_family; @@ -38,11 +38,11 @@ struct Mesh DestinationBuffer *index_buffer; DestinationBuffer *vertex_buffer; - Mesh(std::string mesh_path); - Mesh(const char* mesh_path); - ~Mesh(); + StaticMesh(std::string mesh_path); + StaticMesh(const char* mesh_path); + ~StaticMesh(); }; } -#endif /* CANDY_GEAR_VK_MESH_H */ +#endif /* CANDY_GEAR_VK_STATIC_MESH_H */ diff --git a/src/vk/vertex_3d.hpp b/src/vk/static_mesh_vertex.hpp index 8bb8675..7a91fe4 100644 --- a/src/vk/vertex_3d.hpp +++ b/src/vk/static_mesh_vertex.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. @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef CANDY_GEAR_VK_VERTEX_3D_H -#define CANDY_GEAR_VK_VERTEX_3D_H 1 +#ifndef CANDY_GEAR_VK_STATIC_MESH_VERTEX_H +#define CANDY_GEAR_VK_STATIC_MESH_VERTEX_H 1 #include "core.hpp" namespace VK { -struct Vertex3D +struct StaticMeshVertex { glm::vec3 position; glm::vec3 normal; @@ -31,4 +31,4 @@ struct Vertex3D } -#endif /* CANDY_GEAR_VK_VERTEX_3D_H */ +#endif /* CANDY_GEAR_VK_STATIC_MESH_VERTEX_H */ diff --git a/src/vk/model.cpp b/src/vk/static_model.cpp index 4f63ddb..bf1f0d3 100644 --- a/src/vk/model.cpp +++ b/src/vk/static_model.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. @@ -14,9 +14,7 @@ * limitations under the License. */ -#include "model.hpp" - -#include <array> +#include "static_model.hpp" #include "../core.hpp" #include "uniform_data_object.hpp" @@ -27,45 +25,52 @@ namespace void load_uniform_buffers(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); - self->ub_model_instance.reserve(cg_core.vk_swapchain->images_count); - for(int i{0}; i < cg_core.vk_swapchain->images_count; i++) - self->ub_model_instance.emplace_back( - cg_core.vk_device_with_swapchain, sizeof(VK::ODOModelInstance)); + 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::ODOStaticModel)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } } void unload_uniform_buffers(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); - self->ub_model_instance.clear(); + self->uniform_buffers.clear(); } void load_descriptor_set_pool(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); std::array<VkDescriptorPoolSize, 2> descriptor_pool_sizes{}; descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptor_pool_sizes[0].descriptorCount = - self->ub_model_instance.size(); + self->uniform_buffers.size(); descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptor_pool_sizes[1].descriptorCount = - self->ub_model_instance.size(); + 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->ub_model_instance.size(); + pool_info.maxSets = self->uniform_buffers.size(); pool_info.poolSizeCount = descriptor_pool_sizes.size(); pool_info.pPoolSizes = descriptor_pool_sizes.data(); if(vkCreateDescriptorPool( - self->mesh->queue_family->device->device, &pool_info, nullptr, + self->static_mesh->queue_family->device->device, &pool_info, nullptr, &self->descriptor_pool) != VK_SUCCESS) throw CommandError{"Failed to create a Vulkan descriptor pool."}; } @@ -73,20 +78,21 @@ load_descriptor_set_pool(void *obj) void unload_descriptor_set_pool(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); vkDestroyDescriptorPool( - self->mesh->queue_family->device->device, self->descriptor_pool, nullptr); + self->static_mesh->queue_family->device->device, self->descriptor_pool, + nullptr); } void load_descriptor_sets(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); std::vector<VkDescriptorSetLayout> layouts( cg_core.vk_swapchain->images_count, - cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_model_instance); + cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_static_model); VkDescriptorSetAllocateInfo alloc_info{}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; @@ -96,7 +102,7 @@ load_descriptor_sets(void *obj) self->descriptor_sets.resize(layouts.size()); if(vkAllocateDescriptorSets( - self->mesh->queue_family->device->device, &alloc_info, + self->static_mesh->queue_family->device->device, &alloc_info, self->descriptor_sets.data()) != VK_SUCCESS) CommandError{"Failed to create Vulkan descriptor set."}; } @@ -104,31 +110,40 @@ load_descriptor_sets(void *obj) void load_buffers_to_descriptor_sets(void *obj) { - auto self = static_cast<VK::Model*>(obj); + auto self = static_cast<VK::StaticModel*>(obj); - for(auto i{0}; i < self->ub_model_instance.size(); i++) + for(auto i{0}; i < self->uniform_buffers.size(); i++) { VkDescriptorBufferInfo buffer_info{}; - buffer_info.buffer = self->ub_model_instance[i].buffer; + buffer_info.buffer = self->uniform_buffers[i].buffer; buffer_info.offset = 0; - buffer_info.range = sizeof(VK::ODOModelInstance); + buffer_info.range = sizeof(VK::ODOStaticModel); VkDescriptorImageInfo image_info{}; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.imageView = self->texture->view; image_info.sampler = self->texture->sampler; - std::array<VkWriteDescriptorSet, 1> write_descriptors{}; + std::array<VkWriteDescriptorSet, 2> 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_COMBINED_IMAGE_SAMPLER; - write_descriptors[0].pBufferInfo = nullptr; - write_descriptors[0].pImageInfo = &image_info; + 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(), @@ -148,14 +163,19 @@ static const CommandChain loader{ namespace VK { -Model::Model(std::shared_ptr<Mesh> mesh, std::shared_ptr<Texture> texture): - mesh{mesh}, - texture{texture} +StaticModel::StaticModel( + std::shared_ptr<StaticMesh> static_mesh, + std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position, + std::shared_ptr<glm::vec3> rotation): + static_mesh{static_mesh}, + texture{texture}, + position{position}, + rotation{rotation} { loader.execute(this); } -Model::~Model() +StaticModel::~StaticModel() { loader.revert(this); } diff --git a/src/vk/model.hpp b/src/vk/static_model.hpp index 72682b2..08c68c8 100644 --- a/src/vk/model.hpp +++ b/src/vk/static_model.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. @@ -14,28 +14,35 @@ * limitations under the License. */ -#ifndef CANDY_GEAR_VK_MODEL_H -#define CANDY_GEAR_VK_MODEL_H 1 +#ifndef CANDY_GEAR_VK_STATIC_MODEL_H +#define CANDY_GEAR_VK_STATIC_MODEL_H 1 -#include "mesh.hpp" -#include "texture.hpp" +#include <memory> +#include <vector> + +#include "core.hpp" +#include "static_mesh.hpp" namespace VK { -struct Model +struct StaticModel { - std::shared_ptr<Mesh> mesh; + std::shared_ptr<StaticMesh> static_mesh; std::shared_ptr<Texture> texture; + std::vector<UniformBuffer> uniform_buffers; + std::shared_ptr<glm::vec3> position, rotation; - std::vector<UniformBuffer> ub_model_instance; VkDescriptorPool descriptor_pool; std::vector<VkDescriptorSet> descriptor_sets; - Model(std::shared_ptr<Mesh> mesh, std::shared_ptr<Texture> texture); - ~Model(); + StaticModel( + std::shared_ptr<StaticMesh> static_mesh, + std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position, + std::shared_ptr<glm::vec3> rotation); + ~StaticModel(); }; } -#endif /* CANDY_GEAR_VK_MODEL_H */ +#endif /* CANDY_GEAR_VK_STATIC_MODEL_H */ diff --git a/src/vk/uniform_data_object.hpp b/src/vk/uniform_data_object.hpp index 102b82d..4ee4e73 100644 --- a/src/vk/uniform_data_object.hpp +++ b/src/vk/uniform_data_object.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. @@ -18,6 +18,7 @@ #define CANDY_GEAR_VK_UNIFORM_DATA_OBJECT_H 1 #include "core.hpp" +#include "skeletal_mesh_vertex.hpp" namespace VK { @@ -44,9 +45,15 @@ struct ODOWorld3D_Frag glm::vec4 directional_light_color; }; -struct ODOModelInstance +struct ODOStaticModel { - glm::mat4 matrix; + glm::mat4 base_matrix; +}; + +struct ODOSkeletalModel +{ + glm::mat4 base_matrix; + glm::mat4 bone_matrices[SKELETAL_MESH_MAX_NUM_OF_BONES]; }; struct ODOVector4D |