From 25bf78bfb4785e2cbed683cc56d3cec4271d8b5a Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Thu, 7 Sep 2023 15:14:14 -0300 Subject: feat Create skeletal mesh --- Rakefile | 2 +- glsl/shader_3d.frag.glsl | 4 +- glsl/shader_3d.vert.glsl | 13 +- glsl/shader_3d_skeletal.vert.glsl | 74 ++++ src/binary_reader.cpp | 61 +++ src/binary_reader.hpp | 15 + src/core.cpp | 64 ++- src/core.hpp | 4 + src/main.cpp | 10 +- src/mesh.cpp | 62 --- src/mesh.hpp | 27 -- src/model.cpp | 87 ---- src/model.hpp | 25 -- src/skeletal_mesh.cpp | 67 ++++ src/skeletal_mesh.hpp | 27 ++ src/skeletal_model.cpp | 134 +++++++ src/skeletal_model.hpp | 25 ++ src/static_mesh.cpp | 67 ++++ src/static_mesh.hpp | 27 ++ src/static_model.cpp | 119 ++++++ src/static_model.hpp | 25 ++ src/vk/animation.cpp | 27 ++ src/vk/animation.hpp | 51 +++ src/vk/animation/frame.hpp | 85 ++++ src/vk/core.hpp | 5 +- src/vk/graphics_pipeline_3d.cpp | 70 ++-- src/vk/graphics_pipeline_3d.hpp | 4 +- src/vk/graphics_pipeline_3d_layout.cpp | 78 +++- src/vk/graphics_pipeline_3d_layout.hpp | 5 +- src/vk/graphics_pipeline_3d_skeletal.cpp | 657 +++++++++++++++++++++++++++++++ src/vk/graphics_pipeline_3d_skeletal.hpp | 60 +++ src/vk/mesh.cpp | 134 ------- src/vk/mesh.hpp | 48 --- src/vk/model.cpp | 163 -------- src/vk/model.hpp | 41 -- src/vk/model_instance.hpp | 32 -- src/vk/renderer.cpp | 10 +- src/vk/renderer.hpp | 22 +- src/vk/skeletal_mesh.cpp | 204 ++++++++++ src/vk/skeletal_mesh.hpp | 52 +++ src/vk/skeletal_mesh_vertex.hpp | 42 ++ src/vk/skeletal_model.cpp | 256 ++++++++++++ src/vk/skeletal_model.hpp | 54 +++ src/vk/static_mesh.cpp | 132 +++++++ src/vk/static_mesh.hpp | 48 +++ src/vk/static_mesh_vertex.hpp | 34 ++ src/vk/static_model.cpp | 183 +++++++++ src/vk/static_model.hpp | 48 +++ src/vk/uniform_data_object.hpp | 13 +- src/vk/vertex_3d.hpp | 34 -- test/src/mode/demo.rb | 24 +- 51 files changed, 2794 insertions(+), 761 deletions(-) create mode 100644 glsl/shader_3d_skeletal.vert.glsl delete mode 100644 src/mesh.cpp delete mode 100644 src/mesh.hpp delete mode 100644 src/model.cpp delete mode 100644 src/model.hpp create mode 100644 src/skeletal_mesh.cpp create mode 100644 src/skeletal_mesh.hpp create mode 100644 src/skeletal_model.cpp create mode 100644 src/skeletal_model.hpp create mode 100644 src/static_mesh.cpp create mode 100644 src/static_mesh.hpp create mode 100644 src/static_model.cpp create mode 100644 src/static_model.hpp create mode 100644 src/vk/animation.cpp create mode 100644 src/vk/animation.hpp create mode 100644 src/vk/animation/frame.hpp create mode 100644 src/vk/graphics_pipeline_3d_skeletal.cpp create mode 100644 src/vk/graphics_pipeline_3d_skeletal.hpp delete mode 100644 src/vk/mesh.cpp delete mode 100644 src/vk/mesh.hpp delete mode 100644 src/vk/model.cpp delete mode 100644 src/vk/model.hpp delete mode 100644 src/vk/model_instance.hpp create mode 100644 src/vk/skeletal_mesh.cpp create mode 100644 src/vk/skeletal_mesh.hpp create mode 100644 src/vk/skeletal_mesh_vertex.hpp create mode 100644 src/vk/skeletal_model.cpp create mode 100644 src/vk/skeletal_model.hpp create mode 100644 src/vk/static_mesh.cpp create mode 100644 src/vk/static_mesh.hpp create mode 100644 src/vk/static_mesh_vertex.hpp create mode 100644 src/vk/static_model.cpp create mode 100644 src/vk/static_model.hpp delete mode 100644 src/vk/vertex_3d.hpp diff --git a/Rakefile b/Rakefile index 838f3ce..c483907 100644 --- a/Rakefile +++ b/Rakefile @@ -107,7 +107,7 @@ task :doc do end rule '.o' => ['.cpp', "mruby-#{MRUBY_VERSION}"] do |t| - system("g++ #{t.source} -o #{t.name} -g -D DEBUG=1 -c -std=c++20 "\ + system("g++ #{t.source} -o #{t.name} -g -D DEBUG=1 -c -std=c++23 "\ "-Imruby-#{MRUBY_VERSION}/include/ #{PKG_VALUES}") end diff --git a/glsl/shader_3d.frag.glsl b/glsl/shader_3d.frag.glsl index 916f26f..2405305 100644 --- a/glsl/shader_3d.frag.glsl +++ b/glsl/shader_3d.frag.glsl @@ -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. @@ -34,7 +34,7 @@ layout(set = 0, binding = 1) uniform UBOWorld vec4 directional_light_color; } ubo_world; -layout(set = 2, binding = 0) uniform sampler2D texture_sampler; +layout(set = 2, binding = 1) uniform sampler2D texture_sampler; void main() diff --git a/glsl/shader_3d.vert.glsl b/glsl/shader_3d.vert.glsl index 8fd9167..fe78dc7 100644 --- a/glsl/shader_3d.vert.glsl +++ b/glsl/shader_3d.vert.glsl @@ -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. @@ -21,7 +21,6 @@ layout(location = 0) in vec3 in_position; layout(location = 1) in vec3 in_normal; layout(location = 2) in vec2 in_texture_coord; - layout(location = 0) out DataTransferObject { vec4 frag_color; @@ -40,18 +39,18 @@ layout(set = 1, binding = 0) uniform UBOView mat4 proj; } ubo_view; -layout(push_constant) uniform UBOModelInstance +layout(set = 2, binding = 0) uniform UBOStaticModel { - mat4 matrix; -} ubo_model_instance; + mat4 base_matrix; +} ubo_static_model; void main() { gl_Position = ubo_view.proj * ubo_view.view * - ubo_model_instance.matrix * vec4(in_position, 1.0); + ubo_static_model.base_matrix * vec4(in_position, 1.0); out_dto.frag_color = ubo_world.ambient_light_color; out_dto.frag_texture_coord = in_texture_coord; - out_dto.normal = mat3(ubo_model_instance.matrix) * in_normal; + out_dto.normal = mat3(ubo_static_model.base_matrix) * in_normal; } diff --git a/glsl/shader_3d_skeletal.vert.glsl b/glsl/shader_3d_skeletal.vert.glsl new file mode 100644 index 0000000..169e1af --- /dev/null +++ b/glsl/shader_3d_skeletal.vert.glsl @@ -0,0 +1,74 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +#define MAX_NUM_OF_INFLUENCING_BONES 4 +#define MAX_NUM_OF_BONES 50 + +layout(location = 0) in vec3 in_position; +layout(location = 1) in vec3 in_normal; +layout(location = 2) in vec2 in_texture_coord; +layout(location = 3) in ivec4 in_bone_ids; +layout(location = 4) in vec4 in_bone_weights; + +layout(location = 0) out DataTransferObject +{ + vec4 frag_color; + vec2 frag_texture_coord; + vec3 normal; +} out_dto; + +layout(set = 0, binding = 0) uniform UBOWorld +{ + vec4 ambient_light_color; +} ubo_world; + +layout(set = 1, binding = 0) uniform UBOView +{ + mat4 view; + mat4 proj; +} ubo_view; + +layout(set = 2, binding = 0) uniform UBOSkeletalModel +{ + mat4 base_matrix; + mat4 bone_matrices[MAX_NUM_OF_BONES]; +} ubo_skeletal_model; + +void +main() +{ + vec4 position = vec4(0.0f); + vec3 normal = in_normal; + for(int i = 0; i < MAX_NUM_OF_INFLUENCING_BONES; i++) + { + if(in_bone_weights[i] == 0.0) + break; + + vec4 local_position = ubo_skeletal_model.bone_matrices[in_bone_ids[i]] * + vec4(in_position, 1.0f); + position += local_position * in_bone_weights[i]; + normal = mat3(ubo_skeletal_model.bone_matrices[in_bone_ids[i]]) * normal; + } + + mat4 view_model = ubo_view.view * ubo_skeletal_model.base_matrix; + gl_Position = ubo_view.proj * view_model * position; + out_dto.frag_color = ubo_world.ambient_light_color; + out_dto.frag_texture_coord = in_texture_coord; + out_dto.normal = normal; +} 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(1000 / cg_core.fps); + duration(1000 / cg_core.fps); + // FIXME: actually calculates the real delta time. + cg_core.delta_time = 1.0f / cg_core.fps; } void @@ -592,6 +604,26 @@ unload_vk_graphics_pipeline_3d(void *obj) cg_core.vk_graphics_pipeline_3d = nullptr; } +void +load_vk_graphics_pipeline_3d_skeletal(void *obj) +{ + try + { + cg_core.vk_graphics_pipeline_3d_skeletal = + std::make_unique(); + } + 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) { @@ -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 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_graphics_pipeline_3d; + std::unique_ptr + vk_graphics_pipeline_3d_skeletal; std::unique_ptr vk_graphics_pipeline_2d_solid; std::unique_ptr 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*>(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 *ptr; - - mrb_get_args(mrb, "z", &file_path); - ptr = (std::shared_ptr*)DATA_PTR(self); - if(ptr) mrb_free(mrb, ptr); - ptr = (std::shared_ptr*)mrb_malloc( - mrb, sizeof(std::shared_ptr)); - - new(ptr)std::shared_ptr(std::make_shared(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/mesh.hpp b/src/mesh.hpp deleted file mode 100644 index a68e224..0000000 --- a/src/mesh.hpp +++ /dev/null @@ -1,27 +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. - */ - -#ifndef CANDY_GEAR_MESH_H -#define CANDY_GEAR_MESH_H 1 - -#include "core.hpp" - -extern const struct mrb_data_type cg_mesh_type; - -void -cg_mesh_init(mrb_state *mrb); - -#endif /* CANDY_GEAR_MESH_H */ 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*>(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 *mesh; - std::shared_ptr *texture; - std::shared_ptr *ptr; - - mrb_get_args(mrb, "dd", &mesh, &cg_mesh_type, &texture, &cg_texture_type); - ptr = (std::shared_ptr*)DATA_PTR(self); - if(ptr) mrb_free(mrb, ptr); - ptr = (std::shared_ptr*)mrb_malloc( - mrb, sizeof(std::shared_ptr)); - - new(ptr)std::shared_ptr( - std::make_shared(*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*)DATA_PTR(self); - VK::ModelInstance instance; - std::shared_ptr *position; - std::shared_ptr *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/model.hpp b/src/model.hpp deleted file mode 100644 index 50e49a1..0000000 --- a/src/model.hpp +++ /dev/null @@ -1,25 +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. - */ - -#ifndef CANDY_GEAR_MODEL_H -#define CANDY_GEAR_MODEL_H 1 - -#include "core.hpp" - -void -cg_model_init(mrb_state *mrb); - -#endif /* CANDY_GEAR_MODEL_H */ 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*>(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 *ptr; + + mrb_get_args(mrb, "z", &file_path); + ptr = (std::shared_ptr*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + new(ptr)std::shared_ptr( + std::make_shared(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*>(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 *skeletal_mesh; + std::shared_ptr *texture; + std::shared_ptr *position; + std::shared_ptr *rotation; + std::shared_ptr *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*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + new(ptr)std::shared_ptr( + std::make_shared( + *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*)DATA_PTR(self); + std::shared_ptr *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*)DATA_PTR(self); + std::shared_ptr *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*)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*)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/skeletal_model.hpp b/src/skeletal_model.hpp new file mode 100644 index 0000000..05f9c2e --- /dev/null +++ b/src/skeletal_model.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_SKELETAL_MODEL_H +#define CANDY_GEAR_SKELETAL_MODEL_H 1 + +#include "core.hpp" + +void +cg_skeletal_model_init(mrb_state *mrb); + +#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*>(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 *ptr; + + mrb_get_args(mrb, "z", &file_path); + ptr = (std::shared_ptr*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + new(ptr)std::shared_ptr( + std::make_shared(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*>(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 *static_mesh; + std::shared_ptr *texture; + std::shared_ptr *position; + std::shared_ptr *rotation; + std::shared_ptr *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*)DATA_PTR(self); + if(ptr) mrb_free(mrb, ptr); + ptr = (std::shared_ptr*)mrb_malloc( + mrb, sizeof(std::shared_ptr)); + + new(ptr)std::shared_ptr( + std::make_shared( + *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*)DATA_PTR(self); + std::shared_ptr *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*)DATA_PTR(self); + std::shared_ptr *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*)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/static_model.hpp b/src/static_model.hpp new file mode 100644 index 0000000..dfb7054 --- /dev/null +++ b/src/static_model.hpp @@ -0,0 +1,25 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_STATIC_MODEL_H +#define CANDY_GEAR_STATIC_MODEL_H 1 + +#include "core.hpp" + +void +cg_static_model_init(mrb_state *mrb); + +#endif /* CANDY_GEAR_STATIC_MODEL_H */ diff --git a/src/vk/animation.cpp b/src/vk/animation.cpp new file mode 100644 index 0000000..fec038e --- /dev/null +++ b/src/vk/animation.cpp @@ -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. + */ + +#include "animation.hpp" + +namespace VK +{ + +Bone::Bone(glm::mat4 offset_matrix): + offset_matrix{offset_matrix} +{ +} + +} 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 + +#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 positions; + Channel rotations; + Channel scales; +}; + +struct Animation +{ + std::vector 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 + +#include "../core.hpp" + +namespace VK +{ + +template +struct Frame +{ + const T value; + const float timestamp; + + Frame(T value, float timestamp): + value{value}, + timestamp{timestamp} + { + } + +}; + +template +struct Channel +{ + int current_index{0}; + std::vector> 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 *previous_frame; + Frame *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 -#include +#include #include +#include #include #include 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 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 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 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(obj); - std::array layout_bindings{}; - + std::array 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(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(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(obj); + + std::array 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(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 set_layouts{ self->descriptor_set_world, self->descriptor_set_view, - self->descriptor_set_model_instance}; - - std::array 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 +#include + +#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(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(obj); + + self->ub_world_vert.clear(); +} + +void +load_world_frag_uniform_buffer(void *obj) +{ + auto self = static_cast(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(obj); + + self->ub_world_frag.clear(); +} + +void +load_descriptor_pool(void *obj) +{ + auto self = static_cast(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(obj); + + vkDestroyDescriptorPool( + cg_core.vk_device_with_swapchain->device, self->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets_world(void *obj) +{ + auto self = static_cast(obj); + + std::vector 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(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 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(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(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(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(obj); + + vkDestroyImageView( + cg_core.vk_device_with_swapchain->device, self->depth_image_view, nullptr); +} + +void +load_framebuffer(void *obj) +{ + auto self = static_cast(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 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(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(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 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(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(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 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(view->region.x); + vk_scissor.offset.y = static_cast(view->region.y); + vk_scissor.extent.width = static_cast(view->region.z); + vk_scissor.extent.height = static_cast(view->region.w); + vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor); + } + + // 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 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 +#include + +#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 ub_world_vert; + std::vector ub_world_frag; + + VkDescriptorPool descriptor_pool; + std::vector descriptor_sets_world; + + std::vector swapchain_framebuffers; + VkPipeline graphic_pipeline; + + GraphicsPipeline3DSkeletal(); + ~GraphicsPipeline3DSkeletal(); + + void + draw(std::shared_ptr view, const VkCommandBuffer draw_command_buffer, + const size_t current_frame, const uint32_t image_index); +}; + +} + +#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_SKELETAL_3D_H */ diff --git a/src/vk/mesh.cpp b/src/vk/mesh.cpp deleted file mode 100644 index 9afeac3..0000000 --- a/src/vk/mesh.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 "mesh.hpp" - -#include "../binary_reader.hpp" -#include "../command.hpp" -#include "../core.hpp" -#include "vertex_3d.hpp" - -namespace -{ - -// Data that is only needed for the command chain but not for the Mesh goes -// here. -struct MeshBuilder -{ - std::string mesh_path; - VK::Mesh *mesh; - - MeshBuilder(VK::Mesh *m, std::string mp); - MeshBuilder(VK::Mesh *m, const char* mp); -}; - -MeshBuilder::MeshBuilder(VK::Mesh *m, std::string mp): - mesh{m}, - mesh_path{mp} -{ -} - -MeshBuilder::MeshBuilder(VK::Mesh *m, const char *mp): - MeshBuilder{m, std::string(mp)} -{ -} - -void -load_mesh(void *obj) -{ - auto self = static_cast(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 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(); - } - - 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 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}; - } -} - -void -unload_mesh(void *obj) -{ - auto self = static_cast(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 -{ - -Mesh::Mesh(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)} -{ -} - -Mesh::~Mesh() -{ - MeshBuilder mesh_builder(this, ""); - loader.revert(&mesh_builder); -} - -} diff --git a/src/vk/mesh.hpp b/src/vk/mesh.hpp deleted file mode 100644 index 97b9d8a..0000000 --- a/src/vk/mesh.hpp +++ /dev/null @@ -1,48 +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. - */ - -#ifndef CANDY_GEAR_VK_MESH_H -#define CANDY_GEAR_VK_MESH_H 1 - -#include -#include - -#include "core.hpp" -#include "destination_buffer.hpp" -#include "queue_family.hpp" -#include "uniform_buffer.hpp" -#include "texture.hpp" - -namespace VK -{ - -struct Mesh -{ - QueueFamily *queue_family; - - uint32_t index_count; - SourceBuffer *source_vertex_buffer; - DestinationBuffer *index_buffer; - DestinationBuffer *vertex_buffer; - - Mesh(std::string mesh_path); - Mesh(const char* mesh_path); - ~Mesh(); -}; - -} - -#endif /* CANDY_GEAR_VK_MESH_H */ diff --git a/src/vk/model.cpp b/src/vk/model.cpp deleted file mode 100644 index 4f63ddb..0000000 --- a/src/vk/model.cpp +++ /dev/null @@ -1,163 +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 - -#include "../core.hpp" -#include "uniform_data_object.hpp" - -namespace -{ - -void -load_uniform_buffers(void *obj) -{ - auto self = static_cast(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)); -} - -void -unload_uniform_buffers(void *obj) -{ - auto self = static_cast(obj); - - self->ub_model_instance.clear(); -} - -void -load_descriptor_set_pool(void *obj) -{ - auto self = static_cast(obj); - - std::array descriptor_pool_sizes{}; - descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptor_pool_sizes[0].descriptorCount = - self->ub_model_instance.size(); - descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptor_pool_sizes[1].descriptorCount = - self->ub_model_instance.size(); - - 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.poolSizeCount = descriptor_pool_sizes.size(); - pool_info.pPoolSizes = descriptor_pool_sizes.data(); - - if(vkCreateDescriptorPool( - self->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(obj); - - vkDestroyDescriptorPool( - self->mesh->queue_family->device->device, self->descriptor_pool, nullptr); -} - -void -load_descriptor_sets(void *obj) -{ - auto self = static_cast(obj); - - std::vector layouts( - cg_core.vk_swapchain->images_count, - cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_model_instance); - - 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->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(obj); - - for(auto i{0}; i < self->ub_model_instance.size(); i++) - { - VkDescriptorBufferInfo buffer_info{}; - buffer_info.buffer = self->ub_model_instance[i].buffer; - buffer_info.offset = 0; - buffer_info.range = sizeof(VK::ODOModelInstance); - - 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 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].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 -{ - -Model::Model(std::shared_ptr mesh, std::shared_ptr texture): - mesh{mesh}, - texture{texture} -{ - loader.execute(this); -} - -Model::~Model() -{ - loader.revert(this); -} - -} diff --git a/src/vk/model.hpp b/src/vk/model.hpp deleted file mode 100644 index 72682b2..0000000 --- a/src/vk/model.hpp +++ /dev/null @@ -1,41 +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. - */ - -#ifndef CANDY_GEAR_VK_MODEL_H -#define CANDY_GEAR_VK_MODEL_H 1 - -#include "mesh.hpp" -#include "texture.hpp" - -namespace VK -{ - -struct Model -{ - std::shared_ptr mesh; - std::shared_ptr texture; - - std::vector ub_model_instance; - VkDescriptorPool descriptor_pool; - std::vector descriptor_sets; - - Model(std::shared_ptr mesh, std::shared_ptr texture); - ~Model(); -}; - -} - -#endif /* CANDY_GEAR_VK_MODEL_H */ diff --git a/src/vk/model_instance.hpp b/src/vk/model_instance.hpp deleted file mode 100644 index 1383737..0000000 --- a/src/vk/model_instance.hpp +++ /dev/null @@ -1,32 +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. - */ - -#ifndef CANDY_GEAR_VK_MODEL_INSTANCE_H -#define CANDY_GEAR_VK_MODEL_INSTANCE_H 1 - -#include "core.hpp" - -namespace VK -{ - -struct ModelInstance -{ - glm::vec3 position, rotation; -}; - -} - -#endif /* CANDY_GEAR_VK_MODEL_INSTANCE_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> views_2d, std::vector> 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 #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::vector>> - models_to_draw; + std::vector< + std::unordered_map< + std::shared_ptr, + std::vector>>> + skeletal_models_to_draw; + + std::vector< + std::unordered_map< + std::shared_ptr, + std::vector>>> + static_models_to_draw; VkDescriptorPool descriptor_pool; std::vector> 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(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 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 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 *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 *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(timestamp)); + } + + auto num_rotations{input.read_ui32()}; + VK::Channel *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(timestamp)); + } + + auto num_scales{input.read_ui32()}; + VK::Channel *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(timestamp)); + } + } + } + } +} + +void +unload_mesh(void *obj) +{ + auto self = static_cast(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 +#include + +#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 bones; + std::vector 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(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(obj); + + self->uniform_buffers.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast(obj); + + std::array descriptor_pool_sizes{}; + descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_sizes[0].descriptorCount = + self->uniform_buffers.size(); + descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_pool_sizes[1].descriptorCount = + cg_core.vk_swapchain->images_count; + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = self->uniform_buffers.size(); + pool_info.poolSizeCount = descriptor_pool_sizes.size(); + pool_info.pPoolSizes = descriptor_pool_sizes.data(); + + if(vkCreateDescriptorPool( + self->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(obj); + + vkDestroyDescriptorPool( + self->skeletal_mesh->queue_family->device->device, self->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + std::vector layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_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(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 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 skeletal_mesh, + std::shared_ptr texture, std::shared_ptr position, + std::shared_ptr rotation): + skeletal_mesh{skeletal_mesh}, + texture{texture}, + position{position}, + rotation{rotation}, + animation_index{0}, + animation_time{0.0f}, + bone_transforms(SKELETAL_MESH_MAX_NUM_OF_BONES) +{ + loader.execute(this); + + for(int i{0}; i < skeletal_mesh->bones.size(); i++) + this->bone_transforms[i] = skeletal_mesh->bones[i].offset_matrix; +} + +SkeletalModel::~SkeletalModel() +{ + loader.revert(this); +} + +void +SkeletalModel::tick(float delta) +{ + VK::Animation *current_animation = + &this->skeletal_mesh->animations[this->animation_index]; + + { // update time + this->animation_time += delta; + if(this->animation_time > current_animation->final_time) + { + this->animation_time -= current_animation->final_time; + for(VK::BoneTransform &bone_transform: + current_animation->bone_transforms) + { + bone_transform.positions.current_index = 0; + bone_transform.rotations.current_index = 0; + bone_transform.scales.current_index = 0; + } + } + } + + for(int i{0}; i < current_animation->bone_transforms.size(); i++) + { + VK::BoneTransform *bone_transform = ¤t_animation->bone_transforms[i]; + + auto position{bone_transform->positions.interpolate( + this->animation_time, + [](glm::vec3 frame) + { + return glm::translate(glm::mat4(1.0f), frame); + }, + [](glm::vec3 previous_frame, glm::vec3 next_frame, float scale_factor) + { + glm::vec3 final_position{glm::mix( + previous_frame, next_frame, scale_factor)}; + return glm::translate(glm::mat4(1.0f), final_position); + })}; + + auto rotation{bone_transform->rotations.interpolate( + this->animation_time, + [](glm::quat frame) + { + return glm::toMat4(glm::normalize(frame)); + }, + [](glm::quat previous_frame, glm::quat next_frame, float scale_factor) + { + return glm::toMat4(glm::slerp( + previous_frame, next_frame, scale_factor)); + })}; + + auto scale{bone_transform->scales.interpolate( + this->animation_time, + [](glm::vec3 frame) + { + return glm::scale(glm::mat4(1.0f), frame); + }, + [](glm::vec3 previous_frame, glm::vec3 next_frame, float scale_factor) + { + glm::vec3 scale{glm::mix( + previous_frame, next_frame, scale_factor)}; + return glm::scale(glm::mat4(1.0f), scale); + })}; + + this->bone_transforms[i] = position * rotation * scale; + } +} + +} diff --git a/src/vk/skeletal_model.hpp b/src/vk/skeletal_model.hpp new file mode 100644 index 0000000..1043e0f --- /dev/null +++ b/src/vk/skeletal_model.hpp @@ -0,0 +1,54 @@ +/* + * Copyright 2022-2023 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANDY_GEAR_VK_SKELETAL_MODEL_H +#define CANDY_GEAR_VK_SKELETAL_MODEL_H 1 + +#include +#include + +#include "core.hpp" +#include "skeletal_mesh.hpp" + +namespace VK +{ + +struct SkeletalModel +{ + std::shared_ptr skeletal_mesh; + std::shared_ptr texture; + std::vector uniform_buffers; + std::shared_ptr position, rotation; + int animation_index; + float animation_time; + std::vector bone_transforms; + + VkDescriptorPool descriptor_pool; + std::vector descriptor_sets; + + SkeletalModel( + std::shared_ptr skeletal_mesh, + std::shared_ptr texture, std::shared_ptr position, + std::shared_ptr rotation); + ~SkeletalModel(); + + void + tick(float delta); +}; + +} + +#endif /* CANDY_GEAR_VK_SKELETAL_MODEL_H */ diff --git a/src/vk/static_mesh.cpp b/src/vk/static_mesh.cpp new file mode 100644 index 0000000..29034df --- /dev/null +++ b/src/vk/static_mesh.cpp @@ -0,0 +1,132 @@ +/* + * 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 "../binary_reader.hpp" +#include "../command.hpp" +#include "../core.hpp" +#include "static_mesh_vertex.hpp" + +namespace +{ + +// Data that is only needed for the command chain but not for the StaticMesh +// goes here. +struct MeshBuilder +{ + std::string mesh_path; + VK::StaticMesh *mesh; + + MeshBuilder(VK::StaticMesh *m, std::string mp); + MeshBuilder(VK::StaticMesh *m, const char* mp); +}; + +MeshBuilder::MeshBuilder(VK::StaticMesh *m, std::string mp): + mesh{m}, + mesh_path{mp} +{ +} + +MeshBuilder::MeshBuilder(VK::StaticMesh *m, const char *mp): + MeshBuilder{m, std::string(mp)} +{ +} + +void +load_mesh(void *obj) +{ + auto self = static_cast(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 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(); + } + + 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 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}; + } +} + +void +unload_mesh(void *obj) +{ + auto self = static_cast(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 +{ + +StaticMesh::StaticMesh(std::string mesh_path) +{ + MeshBuilder mesh_builder(this, mesh_path); + loader.execute(&mesh_builder); +} + +StaticMesh::StaticMesh(const char* mesh_path): + StaticMesh{std::string(mesh_path)} +{ +} + +StaticMesh::~StaticMesh() +{ + MeshBuilder mesh_builder(this, ""); + loader.revert(&mesh_builder); +} + +} diff --git a/src/vk/static_mesh.hpp b/src/vk/static_mesh.hpp new file mode 100644 index 0000000..0ab38b2 --- /dev/null +++ b/src/vk/static_mesh.hpp @@ -0,0 +1,48 @@ +/* + * 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_STATIC_MESH_H +#define CANDY_GEAR_VK_STATIC_MESH_H 1 + +#include +#include + +#include "core.hpp" +#include "destination_buffer.hpp" +#include "queue_family.hpp" +#include "uniform_buffer.hpp" +#include "texture.hpp" + +namespace VK +{ + +struct StaticMesh +{ + QueueFamily *queue_family; + + uint32_t index_count; + SourceBuffer *source_vertex_buffer; + DestinationBuffer *index_buffer; + DestinationBuffer *vertex_buffer; + + StaticMesh(std::string mesh_path); + StaticMesh(const char* mesh_path); + ~StaticMesh(); +}; + +} + +#endif /* CANDY_GEAR_VK_STATIC_MESH_H */ diff --git a/src/vk/static_mesh_vertex.hpp b/src/vk/static_mesh_vertex.hpp new file mode 100644 index 0000000..7a91fe4 --- /dev/null +++ b/src/vk/static_mesh_vertex.hpp @@ -0,0 +1,34 @@ +/* + * 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_STATIC_MESH_VERTEX_H +#define CANDY_GEAR_VK_STATIC_MESH_VERTEX_H 1 + +#include "core.hpp" + +namespace VK +{ + +struct StaticMeshVertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texture_coord; +}; + +} + +#endif /* CANDY_GEAR_VK_STATIC_MESH_VERTEX_H */ diff --git a/src/vk/static_model.cpp b/src/vk/static_model.cpp new file mode 100644 index 0000000..bf1f0d3 --- /dev/null +++ b/src/vk/static_model.cpp @@ -0,0 +1,183 @@ +/* + * 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 "../core.hpp" +#include "uniform_data_object.hpp" + +namespace +{ + +void +load_uniform_buffers(void *obj) +{ + auto self = static_cast(obj); + + try + { + self->uniform_buffers.reserve(cg_core.vk_swapchain->images_count); + for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++) + self->uniform_buffers.emplace_back( + cg_core.vk_device_with_swapchain, sizeof(VK::ODOStaticModel)); + } + catch(const std::exception& e) + { + throw CommandError{e.what()}; + } +} + +void +unload_uniform_buffers(void *obj) +{ + auto self = static_cast(obj); + + self->uniform_buffers.clear(); +} + +void +load_descriptor_set_pool(void *obj) +{ + auto self = static_cast(obj); + + std::array descriptor_pool_sizes{}; + descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_pool_sizes[0].descriptorCount = + self->uniform_buffers.size(); + descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_pool_sizes[1].descriptorCount = + cg_core.vk_swapchain->images_count; + + VkDescriptorPoolCreateInfo pool_info{}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = 0; + pool_info.maxSets = self->uniform_buffers.size(); + pool_info.poolSizeCount = descriptor_pool_sizes.size(); + pool_info.pPoolSizes = descriptor_pool_sizes.data(); + + if(vkCreateDescriptorPool( + self->static_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(obj); + + vkDestroyDescriptorPool( + self->static_mesh->queue_family->device->device, self->descriptor_pool, + nullptr); +} + +void +load_descriptor_sets(void *obj) +{ + auto self = static_cast(obj); + + std::vector layouts( + cg_core.vk_swapchain->images_count, + cg_core.vk_graphics_pipeline_3d_layout->descriptor_set_static_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->static_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(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::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 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 +{ + +StaticModel::StaticModel( + std::shared_ptr static_mesh, + std::shared_ptr texture, std::shared_ptr position, + std::shared_ptr rotation): + static_mesh{static_mesh}, + texture{texture}, + position{position}, + rotation{rotation} +{ + loader.execute(this); +} + +StaticModel::~StaticModel() +{ + loader.revert(this); +} + +} diff --git a/src/vk/static_model.hpp b/src/vk/static_model.hpp new file mode 100644 index 0000000..08c68c8 --- /dev/null +++ b/src/vk/static_model.hpp @@ -0,0 +1,48 @@ +/* + * 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_STATIC_MODEL_H +#define CANDY_GEAR_VK_STATIC_MODEL_H 1 + +#include +#include + +#include "core.hpp" +#include "static_mesh.hpp" + +namespace VK +{ + +struct StaticModel +{ + std::shared_ptr static_mesh; + std::shared_ptr texture; + std::vector uniform_buffers; + std::shared_ptr position, rotation; + + VkDescriptorPool descriptor_pool; + std::vector descriptor_sets; + + StaticModel( + std::shared_ptr static_mesh, + std::shared_ptr texture, std::shared_ptr position, + std::shared_ptr rotation); + ~StaticModel(); +}; + +} + +#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 diff --git a/src/vk/vertex_3d.hpp b/src/vk/vertex_3d.hpp deleted file mode 100644 index 8bb8675..0000000 --- a/src/vk/vertex_3d.hpp +++ /dev/null @@ -1,34 +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. - */ - -#ifndef CANDY_GEAR_VK_VERTEX_3D_H -#define CANDY_GEAR_VK_VERTEX_3D_H 1 - -#include "core.hpp" - -namespace VK -{ - -struct Vertex3D -{ - glm::vec3 position; - glm::vec3 normal; - glm::vec2 texture_coord; -}; - -} - -#endif /* CANDY_GEAR_VK_VERTEX_3D_H */ diff --git a/test/src/mode/demo.rb b/test/src/mode/demo.rb index 5f28454..a9781b6 100644 --- a/test/src/mode/demo.rb +++ b/test/src/mode/demo.rb @@ -20,7 +20,7 @@ module Mode def initialize() texture = CandyGear::Texture.from_image("textures/color_texture.qoi"); - mesh = CandyGear::Mesh.new("meshes/cube.cgmesh"); + mesh = CandyGear::StaticMesh.new("meshes/cube.cgmesh"); font = CandyGear::Font.new("/usr/share/fonts/TTF/HanaMinA.ttf", 30); japanese_text = CandyGear::Texture.from_text( font, "こんにちは世界!"); @@ -28,7 +28,6 @@ module Mode font, "The quick brown fox jumps"); @color = CandyGear::Vector3D.new(0.8, 0.2, 0.2); - @model = CandyGear::Model.new(mesh, texture); @sprite = CandyGear::Sprite.new(texture, 0, 0, 1.0, 1.0); @rectangle = CandyGear::Vector4D.new(103.0, 1.0, 100.0, 100.0); @sprite_position = CandyGear::Vector4D.new(1.0, 1.0, 100.0, 100.0); @@ -42,7 +41,7 @@ module Mode 204.0, japanese_text.height + 2.0, english_text.width, english_text.height); - @instances = [ + instance_positions = [ CandyGear::Vector3D.new(5.0, 0.0, 0.0), CandyGear::Vector3D.new(-5.0, 0.0, 0.0), CandyGear::Vector3D.new(0.0, 5.0, 0.0), @@ -52,6 +51,21 @@ module Mode ]; @instances_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0); + @instances = [ + CandyGear::StaticModel.new( + mesh, texture, instance_positions[0], @instances_rotation), + CandyGear::StaticModel.new( + mesh, texture, instance_positions[1], @instances_rotation), + CandyGear::StaticModel.new( + mesh, texture, instance_positions[2], @instances_rotation), + CandyGear::StaticModel.new( + mesh, texture, instance_positions[3], @instances_rotation), + CandyGear::StaticModel.new( + mesh, texture, instance_positions[4], @instances_rotation), + CandyGear::StaticModel.new( + mesh, texture, instance_positions[5], @instances_rotation) + ] + @camera_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0); @camera_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0); @@ -110,9 +124,7 @@ module Mode @english_text_position.w, @english_text_position.h); @instances_rotation.rotate(0.0, BOX_ROTATION_SPEED); @rectangle.draw_rectangle(@view1, @color); - @instances.each do |i| - @model.draw(i, @instances_rotation); - end + @instances.each {_1.draw()}; end end end -- cgit v1.2.3