summaryrefslogtreecommitdiff
path: root/src/vk
diff options
context:
space:
mode:
authorFrederico Linhares <fred@linhares.blue>2023-09-07 15:14:14 -0300
committerFrederico Linhares <fred@linhares.blue>2023-09-15 14:12:15 -0300
commit25bf78bfb4785e2cbed683cc56d3cec4271d8b5a (patch)
tree6fa728091bd662ee0c90a7490bc5ee8e6e23cac4 /src/vk
parente1399befee43ab4549c31ce179e900ad71651edc (diff)
feat Create skeletal mesh
Diffstat (limited to 'src/vk')
-rw-r--r--src/vk/animation.cpp (renamed from src/vk/model_instance.hpp)15
-rw-r--r--src/vk/animation.hpp51
-rw-r--r--src/vk/animation/frame.hpp85
-rw-r--r--src/vk/core.hpp5
-rw-r--r--src/vk/graphics_pipeline_3d.cpp70
-rw-r--r--src/vk/graphics_pipeline_3d.hpp4
-rw-r--r--src/vk/graphics_pipeline_3d_layout.cpp78
-rw-r--r--src/vk/graphics_pipeline_3d_layout.hpp5
-rw-r--r--src/vk/graphics_pipeline_3d_skeletal.cpp657
-rw-r--r--src/vk/graphics_pipeline_3d_skeletal.hpp60
-rw-r--r--src/vk/renderer.cpp10
-rw-r--r--src/vk/renderer.hpp22
-rw-r--r--src/vk/skeletal_mesh.cpp204
-rw-r--r--src/vk/skeletal_mesh.hpp52
-rw-r--r--src/vk/skeletal_mesh_vertex.hpp42
-rw-r--r--src/vk/skeletal_model.cpp256
-rw-r--r--src/vk/skeletal_model.hpp54
-rw-r--r--src/vk/static_mesh.cpp (renamed from src/vk/mesh.cpp)34
-rw-r--r--src/vk/static_mesh.hpp (renamed from src/vk/mesh.hpp)16
-rw-r--r--src/vk/static_mesh_vertex.hpp (renamed from src/vk/vertex_3d.hpp)10
-rw-r--r--src/vk/static_model.cpp (renamed from src/vk/model.cpp)88
-rw-r--r--src/vk/static_model.hpp (renamed from src/vk/model.hpp)29
-rw-r--r--src/vk/uniform_data_object.hpp13
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 = &current_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