summaryrefslogtreecommitdiff
path: root/src
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
parente1399befee43ab4549c31ce179e900ad71651edc (diff)
feat Create skeletal mesh
Diffstat (limited to 'src')
-rw-r--r--src/binary_reader.cpp61
-rw-r--r--src/binary_reader.hpp15
-rw-r--r--src/core.cpp64
-rw-r--r--src/core.hpp4
-rw-r--r--src/main.cpp10
-rw-r--r--src/mesh.cpp62
-rw-r--r--src/model.cpp87
-rw-r--r--src/skeletal_mesh.cpp67
-rw-r--r--src/skeletal_mesh.hpp27
-rw-r--r--src/skeletal_model.cpp134
-rw-r--r--src/skeletal_model.hpp (renamed from src/mesh.hpp)12
-rw-r--r--src/static_mesh.cpp67
-rw-r--r--src/static_mesh.hpp27
-rw-r--r--src/static_model.cpp119
-rw-r--r--src/static_model.hpp (renamed from src/model.hpp)10
-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
38 files changed, 2287 insertions, 339 deletions
diff --git a/src/binary_reader.cpp b/src/binary_reader.cpp
index 5847328..04633a2 100644
--- a/src/binary_reader.cpp
+++ b/src/binary_reader.cpp
@@ -26,6 +26,11 @@ union IntAndFloat32bit{
float f;
};
+union IntAndFloat64bit{
+ uint64_t i;
+ double f;
+};
+
}
BinaryReader::BinaryReader(std::string file_path):
@@ -65,6 +70,37 @@ BinaryReader::read_ui32()
return b1 << 24 | b2 << 16 | b3 << 8 | b4;
}
+uint64_t
+BinaryReader::read_ui64()
+{
+ uint8_t b1{this->data[_pointer++]}, b2{this->data[_pointer++]},
+ b3{this->data[_pointer++]}, b4{this->data[_pointer++]},
+ b5{this->data[_pointer++]}, b6{this->data[_pointer++]},
+ b7{this->data[_pointer++]}, b8{this->data[_pointer++]};
+
+ return (uint64_t)b1 << 56 | (uint64_t)b2 << 48 | (uint64_t)b3 << 40 |
+ (uint64_t)b4 << 32 | (uint64_t)b5 << 24 | (uint64_t)b6 << 16 |
+ (uint64_t)b7 << 8 | (uint64_t)b8;
+}
+
+float
+BinaryReader::read_float()
+{
+ IntAndFloat32bit num;
+ num.i = read_ui32();
+
+ return num.f;
+}
+
+double
+BinaryReader::read_double()
+{
+ IntAndFloat64bit num;
+ num.i = read_ui64();
+
+ return num.f;
+}
+
glm::vec2
BinaryReader::read_vec2()
{
@@ -81,6 +117,31 @@ BinaryReader::read_vec3()
return glm::vec3{x.f, y.f, z.f};
}
+glm::quat
+BinaryReader::read_quat()
+{
+ IntAndFloat32bit w{read_ui32()}, x{read_ui32()}, y{read_ui32()},
+ z{read_ui32()};
+
+ return glm::quat{w.f, x.f, y.f, z.f};
+}
+
+glm::mat4
+BinaryReader::read_mat4()
+{
+ glm::mat4 matrix;
+ float *offset_matrix_data{glm::value_ptr(matrix)};
+
+ for(int i{0}; i < 16; i++)
+ {
+ IntAndFloat32bit num;
+ num.i = read_ui32();
+ offset_matrix_data[i] = num.f;
+ }
+
+ return matrix;
+}
+
void
BinaryReader::read_chars(char *str, int size)
{
diff --git a/src/binary_reader.hpp b/src/binary_reader.hpp
index b2e4de5..5dbff54 100644
--- a/src/binary_reader.hpp
+++ b/src/binary_reader.hpp
@@ -43,12 +43,27 @@ public:
uint32_t
read_ui32();
+ uint64_t
+ read_ui64();
+
+ float
+ read_float();
+
+ double
+ read_double();
+
glm::vec2
read_vec2();
glm::vec3
read_vec3();
+ glm::quat
+ read_quat();
+
+ glm::mat4
+ read_mat4();
+
void
read_chars(char *str, int size);
};
diff --git a/src/core.cpp b/src/core.cpp
index 5039ffe..986ecc1 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -20,9 +20,11 @@
#include "font.hpp"
#include "graphic.hpp"
#include "key.hpp"
-#include "mesh.hpp"
-#include "model.hpp"
#include "rotation_3d.hpp"
+#include "skeletal_model.hpp"
+#include "skeletal_mesh.hpp"
+#include "static_model.hpp"
+#include "static_mesh.hpp"
#include "sound.hpp"
#include "sprite.hpp"
#include "texture.hpp"
@@ -51,14 +53,22 @@ vk_debug_callback(
{
// Set level.
Log::Level log_level;
- if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
+ switch(message_severity)
+ {
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
log_level = Log::Level::Trace;
- else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
log_level = Log::Level::Information;
- else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
log_level = Log::Level::Warning;
- else if(message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
+ break;
+ case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+ default:
log_level = Log::Level::Error;
+ break;
+ }
// Log message.
cg_core.log.message(log_level, callback_data->pMessage);
@@ -144,7 +154,9 @@ load_game(void *obj)
cg_graphic_finish_config(cg_core.mrb);
cg_core.max_frame_duration =
- duration<long long, std::milli>(1000 / cg_core.fps);
+ duration<long long, std::milli>(1000 / cg_core.fps);
+ // FIXME: actually calculates the real delta time.
+ cg_core.delta_time = 1.0f / cg_core.fps;
}
void
@@ -593,6 +605,26 @@ unload_vk_graphics_pipeline_3d(void *obj)
}
void
+load_vk_graphics_pipeline_3d_skeletal(void *obj)
+{
+ try
+ {
+ cg_core.vk_graphics_pipeline_3d_skeletal =
+ std::make_unique<VK::GraphicsPipeline3DSkeletal>();
+ }
+ catch(const CommandError &e)
+ {
+ throw CommandError{"Failed to create 3d skeletal graphics pipeline."};
+ }
+}
+
+void
+unload_vk_graphics_pipeline_3d_skeletal(void *obj)
+{
+ cg_core.vk_graphics_pipeline_3d_skeletal = nullptr;
+}
+
+void
load_vk_graphics_pipeline_2d_solid(void *obj)
{
try
@@ -660,14 +692,14 @@ unload_vk_renderer(void *obj)
void
load_mruby_interface(void *obj)
{
- mrb_value main_obj = mrb_obj_iv_inspect(cg_core.mrb, cg_core.mrb->top_self);
-
cg_candy_gear_init(cg_core.mrb);
cg_font_init(cg_core.mrb);
cg_key_init(cg_core.mrb);
- cg_mesh_init(cg_core.mrb);
- cg_model_init(cg_core.mrb);
cg_rotation_3d_init(cg_core.mrb);
+ cg_skeletal_model_init(cg_core.mrb);
+ cg_skeletal_mesh_init(cg_core.mrb);
+ cg_static_model_init(cg_core.mrb);
+ cg_static_mesh_init(cg_core.mrb);
cg_sound_init(cg_core.mrb);
cg_sprite_init(cg_core.mrb);
cg_texture_init(cg_core.mrb);
@@ -675,13 +707,6 @@ load_mruby_interface(void *obj)
cg_vector_4d_init(cg_core.mrb);
cg_view_2d_init(cg_core.mrb);
cg_view_3d_init(cg_core.mrb);
-
- mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_init, 0);
- if (cg_core.mrb->exc)
- {
- mrb_print_error(cg_core.mrb);
- throw CommandError{"Error initializing game."};
- }
}
}
@@ -709,6 +734,9 @@ const CommandChain cg_sCore::loader{
&unload_vk_graphics_pipeline_2d_solid_layout},
{&load_vk_graphics_pipeline_2d_wired_layout,
&unload_vk_graphics_pipeline_2d_wired_layout},
+ // TODO: finish skeletal mesh animation
+ // {&load_vk_graphics_pipeline_3d_skeletal,
+ // &unload_vk_graphics_pipeline_3d_skeletal},
{&load_vk_graphics_pipeline_3d, &unload_vk_graphics_pipeline_3d},
{&load_vk_graphics_pipeline_2d_solid, &unload_vk_graphics_pipeline_2d_solid},
{&load_vk_graphics_pipeline_2d_wired, &unload_vk_graphics_pipeline_2d_wired},
diff --git a/src/core.hpp b/src/core.hpp
index ad5d136..d9e3c9e 100644
--- a/src/core.hpp
+++ b/src/core.hpp
@@ -54,6 +54,7 @@
#include "vk/graphics_pipeline_2d_wired.hpp"
#include "vk/graphics_pipeline_3d_layout.hpp"
#include "vk/graphics_pipeline_3d.hpp"
+#include "vk/graphics_pipeline_3d_skeletal.hpp"
#include "vk/renderer.hpp"
#include "vk/swapchain.hpp"
@@ -98,6 +99,7 @@ struct cg_sCore
Uint32 fps;
std::chrono::duration<long long, std::milli> max_frame_duration;
+ float delta_time;
SDL_Window *window;
@@ -119,6 +121,8 @@ struct cg_sCore
VK::GraphicsPipeline2DSolidLayout *vk_graphics_pipeline_2d_solid_layout;
VK::GraphicsPipeline2DWiredLayout *vk_graphics_pipeline_2d_wired_layout;
std::unique_ptr<VK::GraphicsPipeline3D> vk_graphics_pipeline_3d;
+ std::unique_ptr<VK::GraphicsPipeline3DSkeletal>
+ vk_graphics_pipeline_3d_skeletal;
std::unique_ptr<VK::GraphicsPipeline2DSolid> vk_graphics_pipeline_2d_solid;
std::unique_ptr<VK::GraphicsPipeline2DWired> vk_graphics_pipeline_2d_wired;
diff --git a/src/main.cpp b/src/main.cpp
index 7f02f49..75b7894 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -41,7 +41,15 @@ int main(int argc, char *argv[])
return 1;
}
- mrb_value main_obj = mrb_obj_iv_inspect(cg_core.mrb, cg_core.mrb->top_self);
+ mrb_value main_obj{mrb_obj_iv_inspect(cg_core.mrb, cg_core.mrb->top_self)};
+
+ mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_init, 0);
+ if (cg_core.mrb->exc)
+ {
+ mrb_print_error(cg_core.mrb);
+ cg_sCore::loader.revert(nullptr);
+ return 1;
+ }
auto frame_start = steady_clock::now();
diff --git a/src/mesh.cpp b/src/mesh.cpp
deleted file mode 100644
index 409d45e..0000000
--- a/src/mesh.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2022 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 "mesh.hpp"
-
-#include "rotation_3d.hpp"
-#include "vector_3d.hpp"
-
-void
-cg_free_mesh(mrb_state *mrb, void* obj)
-{
- auto ptr = static_cast<std::shared_ptr<VK::Mesh>*>(obj);
-
- ptr->~shared_ptr();
- mrb_free(mrb, ptr);
-}
-
-const struct mrb_data_type cg_mesh_type = { "CG_Mesh", cg_free_mesh };
-
-static mrb_value
-cg_cMesh_initialize(mrb_state *mrb, mrb_value self)
-{
- const char *file_path;
-
- std::shared_ptr<VK::Mesh> *ptr;
-
- mrb_get_args(mrb, "z", &file_path);
- ptr = (std::shared_ptr<VK::Mesh>*)DATA_PTR(self);
- if(ptr) mrb_free(mrb, ptr);
- ptr = (std::shared_ptr<VK::Mesh>*)mrb_malloc(
- mrb, sizeof(std::shared_ptr<VK::Mesh>));
-
- new(ptr)std::shared_ptr<VK::Mesh>(std::make_shared<VK::Mesh>(file_path));
-
- mrb_data_init(self, ptr, &cg_mesh_type);
- return self;
-}
-
-void
-cg_mesh_init(mrb_state *mrb)
-{
- struct RClass *cg_m, *cg_cMesh;
-
- cg_m = mrb_module_get(mrb, "CandyGear");
- cg_cMesh = mrb_define_class_under(mrb, cg_m, "Mesh", mrb->object_class);
- MRB_SET_INSTANCE_TT(cg_cMesh, MRB_TT_DATA);
- mrb_define_method(
- mrb, cg_cMesh, "initialize", cg_cMesh_initialize, MRB_ARGS_REQ(1));
-}
diff --git a/src/model.cpp b/src/model.cpp
deleted file mode 100644
index b5c4185..0000000
--- a/src/model.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2022 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 "model.hpp"
-
-#include "mesh.hpp"
-#include "rotation_3d.hpp"
-#include "texture.hpp"
-#include "vector_3d.hpp"
-#include "vk/model_instance.hpp"
-
-void
-cg_free_model(mrb_state *mrb, void *obj)
-{
- auto ptr = static_cast<std::shared_ptr<VK::Model>*>(obj);
-
- ptr->~shared_ptr();
- mrb_free(mrb, ptr);
-}
-
-const struct mrb_data_type cg_model_type = { "CG_Model", cg_free_model };
-
-static mrb_value
-cg_cModel_initialize(mrb_state *mrb, mrb_value self)
-{
- std::shared_ptr<VK::Mesh> *mesh;
- std::shared_ptr<VK::Texture> *texture;
- std::shared_ptr<VK::Model> *ptr;
-
- mrb_get_args(mrb, "dd", &mesh, &cg_mesh_type, &texture, &cg_texture_type);
- ptr = (std::shared_ptr<VK::Model>*)DATA_PTR(self);
- if(ptr) mrb_free(mrb, ptr);
- ptr = (std::shared_ptr<VK::Model>*)mrb_malloc(
- mrb, sizeof(std::shared_ptr<VK::Model>));
-
- new(ptr)std::shared_ptr<VK::Model>(
- std::make_shared<VK::Model>(*mesh, *texture));
-
- mrb_data_init(self, ptr, &cg_mesh_type);
- return self;
-}
-
-static mrb_value
-cg_cModel_draw(mrb_state *mrb, mrb_value self)
-{
- auto ptr = (std::shared_ptr<VK::Model>*)DATA_PTR(self);
- VK::ModelInstance instance;
- std::shared_ptr<glm::vec3> *position;
- std::shared_ptr<glm::vec3> *rotation;
-
- mrb_get_args(
- mrb, "dd", &position, &cg_vector_3d_type, &rotation, &cg_rotation_3d_type);
- instance.position = **position;
- instance.rotation = **rotation;
-
- auto &instances = cg_core.vk_renderer->models_to_draw[
- cg_core.vk_swapchain->current_frame][*ptr];
- instances.push_back(instance);
-
- return self;
-}
-
-void
-cg_model_init(mrb_state *mrb)
-{
- struct RClass *cg_m, *cg_cModel;
-
- cg_m = mrb_module_get(mrb, "CandyGear");
- cg_cModel = mrb_define_class_under(mrb, cg_m, "Model", mrb->object_class);
- MRB_SET_INSTANCE_TT(cg_cModel, MRB_TT_DATA);
- mrb_define_method(
- mrb, cg_cModel, "initialize", cg_cModel_initialize, MRB_ARGS_REQ(2));
- mrb_define_method(mrb, cg_cModel, "draw", cg_cModel_draw, MRB_ARGS_REQ(2));
-}
diff --git a/src/skeletal_mesh.cpp b/src/skeletal_mesh.cpp
new file mode 100644
index 0000000..767ef5a
--- /dev/null
+++ b/src/skeletal_mesh.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "rotation_3d.hpp"
+#include "vector_3d.hpp"
+#include "vk/skeletal_mesh.hpp"
+
+void
+cg_free_skeletal_mesh(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::SkeletalMesh>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_skeletal_mesh_type = {
+ "CG_SkeletalMesh", cg_free_skeletal_mesh };
+
+static mrb_value
+cg_cSkeletalMesh_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ std::shared_ptr<VK::SkeletalMesh> *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (std::shared_ptr<VK::SkeletalMesh>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::SkeletalMesh>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::SkeletalMesh>));
+
+ new(ptr)std::shared_ptr<VK::SkeletalMesh>(
+ std::make_shared<VK::SkeletalMesh>(file_path));
+
+ mrb_data_init(self, ptr, &cg_skeletal_mesh_type);
+ return self;
+}
+
+void
+cg_skeletal_mesh_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSkeletalMesh;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSkeletalMesh = mrb_define_class_under(
+ mrb, cg_m, "SkeletalMesh", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSkeletalMesh, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSkeletalMesh, "initialize", cg_cSkeletalMesh_initialize,
+ MRB_ARGS_REQ(1));
+}
diff --git a/src/skeletal_mesh.hpp b/src/skeletal_mesh.hpp
new file mode 100644
index 0000000..476aa9b
--- /dev/null
+++ b/src/skeletal_mesh.hpp
@@ -0,0 +1,27 @@
+/*
+ * 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_SKELETAL_MESH_H
+#define CANDY_GEAR_SKELETAL_MESH_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_skeletal_mesh_type;
+
+void
+cg_skeletal_mesh_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SKELETAL_MESH_H */
diff --git a/src/skeletal_model.cpp b/src/skeletal_model.cpp
new file mode 100644
index 0000000..0165e91
--- /dev/null
+++ b/src/skeletal_model.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "vector_3d.hpp"
+#include "rotation_3d.hpp"
+#include "skeletal_mesh.hpp"
+#include "texture.hpp"
+#include "vk/skeletal_model.hpp"
+
+void
+cg_free_skeletal_model(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::SkeletalModel>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_skeletal_model_type = {
+ "CG_SkeletalModel", cg_free_skeletal_model };
+
+static mrb_value
+cg_cSkeletalModel_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<VK::SkeletalMesh> *skeletal_mesh;
+ std::shared_ptr<VK::Texture> *texture;
+ std::shared_ptr<glm::vec3> *position;
+ std::shared_ptr<glm::vec3> *rotation;
+ std::shared_ptr<VK::SkeletalModel> *ptr;
+
+ mrb_get_args(
+ mrb, "dddd", &skeletal_mesh, &cg_skeletal_mesh_type, &texture,
+ &cg_texture_type, &position, &cg_vector_3d_type, &rotation,
+ &cg_rotation_3d_type);
+ ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::SkeletalModel>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::SkeletalModel>));
+
+ new(ptr)std::shared_ptr<VK::SkeletalModel>(
+ std::make_shared<VK::SkeletalModel>(
+ *skeletal_mesh, *texture, *position, *rotation));
+
+ mrb_data_init(self, ptr, &cg_skeletal_model_type);
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_rotation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *rotation;
+
+ mrb_get_args(mrb, "d", &rotation, &cg_rotation_3d_type);
+ (*ptr)->rotation = *rotation;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_position(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *position;
+
+ mrb_get_args(mrb, "d", &position, &cg_vector_3d_type);
+ (*ptr)->position = *position;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_animation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
+ mrb_int animation_index;
+
+ mrb_get_args(mrb, "i", &animation_index);
+ (*ptr)->animation_index = animation_index;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
+
+ auto &instances = cg_core.vk_renderer->skeletal_models_to_draw[
+ cg_core.vk_swapchain->current_frame][(*ptr)->skeletal_mesh];
+ instances.push_back(*ptr);
+
+ return self;
+}
+
+void
+cg_skeletal_model_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSkeletalModel;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSkeletalModel = mrb_define_class_under(
+ mrb, cg_m, "SkeletalModel", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSkeletalModel, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "initialize", cg_cSkeletalModel_initialize,
+ MRB_ARGS_REQ(4));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "position=", cg_cSkeletalModel_set_position,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "rotation=", cg_cSkeletalModel_set_rotation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "animation=", cg_cSkeletalModel_set_animation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "draw", cg_cSkeletalModel_draw, MRB_ARGS_NONE());
+}
diff --git a/src/mesh.hpp b/src/skeletal_model.hpp
index a68e224..05f9c2e 100644
--- a/src/mesh.hpp
+++ b/src/skeletal_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,14 +14,12 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_MESH_H
-#define CANDY_GEAR_MESH_H 1
+#ifndef CANDY_GEAR_SKELETAL_MODEL_H
+#define CANDY_GEAR_SKELETAL_MODEL_H 1
#include "core.hpp"
-extern const struct mrb_data_type cg_mesh_type;
-
void
-cg_mesh_init(mrb_state *mrb);
+cg_skeletal_model_init(mrb_state *mrb);
-#endif /* CANDY_GEAR_MESH_H */
+#endif /* CANDY_GEAR_SKELETAL_MODEL_H */
diff --git a/src/static_mesh.cpp b/src/static_mesh.cpp
new file mode 100644
index 0000000..e1e8776
--- /dev/null
+++ b/src/static_mesh.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "static_mesh.hpp"
+
+#include "rotation_3d.hpp"
+#include "vector_3d.hpp"
+#include "vk/static_mesh.hpp"
+
+void
+cg_free_static_mesh(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::StaticMesh>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_static_mesh_type = {
+ "CG_StaticMesh", cg_free_static_mesh };
+
+static mrb_value
+cg_cStaticMesh_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ std::shared_ptr<VK::StaticMesh> *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (std::shared_ptr<VK::StaticMesh>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::StaticMesh>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::StaticMesh>));
+
+ new(ptr)std::shared_ptr<VK::StaticMesh>(
+ std::make_shared<VK::StaticMesh>(file_path));
+
+ mrb_data_init(self, ptr, &cg_static_mesh_type);
+ return self;
+}
+
+void
+cg_static_mesh_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cStaticMesh;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cStaticMesh = mrb_define_class_under(
+ mrb, cg_m, "StaticMesh", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cStaticMesh, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cStaticMesh, "initialize", cg_cStaticMesh_initialize,
+ MRB_ARGS_REQ(1));
+}
diff --git a/src/static_mesh.hpp b/src/static_mesh.hpp
new file mode 100644
index 0000000..8f3b2d6
--- /dev/null
+++ b/src/static_mesh.hpp
@@ -0,0 +1,27 @@
+/*
+ * 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_STATIC_MESH_H
+#define CANDY_GEAR_STATIC_MESH_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_static_mesh_type;
+
+void
+cg_static_mesh_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_STATIC_MESH_H */
diff --git a/src/static_model.cpp b/src/static_model.cpp
new file mode 100644
index 0000000..25a08c7
--- /dev/null
+++ b/src/static_model.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "static_model.hpp"
+
+#include "vector_3d.hpp"
+#include "rotation_3d.hpp"
+#include "static_mesh.hpp"
+#include "texture.hpp"
+#include "vk/static_model.hpp"
+
+void
+cg_free_static_model(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::StaticModel>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_static_model_type = {
+ "CG_StaticModel", cg_free_static_model };
+
+static mrb_value
+cg_cStaticModel_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<VK::StaticMesh> *static_mesh;
+ std::shared_ptr<VK::Texture> *texture;
+ std::shared_ptr<glm::vec3> *position;
+ std::shared_ptr<glm::vec3> *rotation;
+ std::shared_ptr<VK::StaticModel> *ptr;
+
+ mrb_get_args(
+ mrb, "dddd", &static_mesh, &cg_static_mesh_type, &texture,
+ &cg_texture_type, &position, &cg_vector_3d_type, &rotation,
+ &cg_rotation_3d_type);
+ ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::StaticModel>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::StaticModel>));
+
+ new(ptr)std::shared_ptr<VK::StaticModel>(
+ std::make_shared<VK::StaticModel>(
+ *static_mesh, *texture, *position, *rotation));
+
+ mrb_data_init(self, ptr, &cg_static_model_type);
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_set_rotation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *rotation;
+
+ mrb_get_args(mrb, "d", &rotation, &cg_rotation_3d_type);
+ (*ptr)->rotation = *rotation;
+
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_set_position(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *position;
+
+ mrb_get_args(mrb, "d", &position, &cg_vector_3d_type);
+ (*ptr)->position = *position;
+
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
+
+ auto &instances = cg_core.vk_renderer->static_models_to_draw[
+ cg_core.vk_swapchain->current_frame][(*ptr)->static_mesh];
+ instances.push_back(*ptr);
+
+ return self;
+}
+
+void
+cg_static_model_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cStaticModel;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cStaticModel = mrb_define_class_under(
+ mrb, cg_m, "StaticModel", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cStaticModel, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cStaticModel, "initialize", cg_cStaticModel_initialize,
+ MRB_ARGS_REQ(4));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "position=", cg_cStaticModel_set_position,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "rotation=", cg_cStaticModel_set_rotation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "draw", cg_cStaticModel_draw, MRB_ARGS_NONE());
+}
diff --git a/src/model.hpp b/src/static_model.hpp
index 50e49a1..dfb7054 100644
--- a/src/model.hpp
+++ b/src/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,12 +14,12 @@
* limitations under the License.
*/
-#ifndef CANDY_GEAR_MODEL_H
-#define CANDY_GEAR_MODEL_H 1
+#ifndef CANDY_GEAR_STATIC_MODEL_H
+#define CANDY_GEAR_STATIC_MODEL_H 1
#include "core.hpp"
void
-cg_model_init(mrb_state *mrb);
+cg_static_model_init(mrb_state *mrb);
-#endif /* CANDY_GEAR_MODEL_H */
+#endif /* CANDY_GEAR_STATIC_MODEL_H */
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