diff --git a/include/Scene.h b/include/Scene.h index 2d59f7e..a8c1071 100644 --- a/include/Scene.h +++ b/include/Scene.h @@ -19,32 +19,36 @@ class Scene loadGLTFtoScene(std::string filename); // Класс сцены class Scene { - public: - Scene(); // Конструктор пустой сцены - Scene(const Scene ©); // Конструктор копирования - Scene& operator=(const Scene& other); // Оператор присваивания +public: + Scene(); // Конструктор пустой сцены + Scene(const Scene ©); // Конструктор копирования + Scene& operator=(const Scene& other); // Оператор присваивания - void render(ShaderProgram &shaderProgram, UBO &material_buffer, bool recalc_animations = false); // Рендер сцены + void render(ShaderProgram &shaderProgram, UBO &material_buffer, UBO *bones_buffer = NULL, bool recalc_animations = false); // Рендер сцены - void set_group_id(GLuint64 id, GLuint etc = 0); // Изменение флага записи идентификатора для всех моделей + void set_group_id(GLuint64 id, GLuint etc = 0); // Изменение флага записи идентификатора для всех моделей - Node root; // Корневой узел + Node root; // Корневой узел - // Списки объектов, выступающих узлами - std::list nodes; // Список пустых узлов - std::list models; // Список моделей для рендера - std::list cameras; // Список камер + // Списки объектов, выступающих узлами + std::list nodes; // Список пустых узлов + std::list models; // Список моделей для рендера + std::list cameras; // Список камер - std::vector animations; // Список анимаций - std::map animation_names; // Имя анимации - индекс - protected: - void rebuld_tree(const Scene& from); // Перестройка дерева после копирования или присваивания - template - void rebuild_Nodes_list(T& nodes, const Scene& from); // Перестройка узлов выбранного списка - template - void move_parent(Node& for_node, const std::list& from_nodes, std::list& this_nodes); // Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному - template - void move_animation_target(Node*& target, const std::list& from_nodes, std::list& this_nodes); // Перестройка узлов анимации + std::list bones; // Узлы, выступающие в роли костей + std::list skeletons; // Скелеты + + std::vector animations; // Список анимаций + std::map animation_names; // Имя анимации - индекс +protected: + void rebuld_tree(const Scene& from); // Перестройка дерева после копирования или присваивания + template + void rebuild_Nodes_list(T& nodes, const Scene& from); // Перестройка узлов выбранного списка + template + void move_parent(Node& for_node, const std::list& from_nodes, std::list& this_nodes); // Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному + template + void move_animation_target(Node*& target, const std::list& from_nodes, std::list& this_nodes); // Перестройка узлов анимации + void rebuld_bones(const Scene &from); // Перестройка указателей на кости и узлы, которые выступают в роли костей }; #endif // SCENE_H diff --git a/src/Scene.cpp b/src/Scene.cpp index a58e576..f5c86fa 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -9,7 +9,8 @@ Scene::Scene() // Конструктор копирования Scene::Scene(const Scene ©): root(copy.root), nodes(copy.nodes), models(copy.models), cameras(copy.cameras), -animations(copy.animations), animation_names(copy.animation_names) +animations(copy.animations), animation_names(copy.animation_names), +bones(copy.bones) // скелеты (skeletons) перестраиваются в методе Scene::rebuild_bones { rebuld_tree(copy); } @@ -24,13 +25,15 @@ Scene& Scene::operator=(const Scene& other) animations = other.animations; animation_names = other.animation_names; + bones = other.bones; // скелеты (skeletons) перестраиваются в методе Scene::rebuild_bones + rebuld_tree(other); return *this; } // Рендер сцены -void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer, bool recalc_animations) +void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer, UBO *bones_buffer, bool recalc_animations) { // Если требуется пересчитаем анимации if (recalc_animations) @@ -40,7 +43,7 @@ void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer, bool reca // Рендер моделей for (auto & model : models) - model.render(shaderProgram, material_buffer); + model.render(shaderProgram, material_buffer, bones_buffer); } // Перестройка узлов выбранного списка @@ -101,6 +104,71 @@ void Scene::move_animation_target(Node*& target, const std::list& from_nodes, target = &(*it_this); } +// Перестройка указателей на кости и узлы, которые выступают в роли костей +void Scene::rebuld_bones(const Scene &from) +{ + // Цикл по костям + for (auto bone = bones.begin(); bone != bones.end(); ++bone) + { + // Если целевой узел - оригинальный корневой узел, то меняем на собственный корневой узел + if (bone->node == &from.root) + { + bone->node = &root; + continue; + } + + // Если можно привести к модели, то ищем родителя среди моделей + if (dynamic_cast(bone->node)) + move_animation_target(bone->node, from.models, this->models); + else + // Иначе проверяем на принадлежность к камерам + if (dynamic_cast(bone->node)) + move_animation_target(bone->node, from.cameras, this->cameras); + // Иначе это пустой узел + else + move_animation_target(bone->node, from.nodes, this->nodes); + + // Не нашли узел - значит он не часть этой сцены + // и изменений по каналу не требуется + } + + + // Построим скелеты с нуля + // Цикл по скелетам оригинала + for (auto & f_skeleton : from.skeletons) + { + Skeleton tmp; + + // Цикл по костям в конкретном скелете оригинала + for (Bone* fs_bones : f_skeleton.bones) + { + auto it_bone = this->bones.begin(); + // Цикл по общему списку костей оригинала + for (auto f_bone = from.bones.begin(); f_bone != from.bones.end(); ++f_bone, ++it_bone) + // Если адрес объекта, на который указывает итератор, равен кости в скелете + if (&(*f_bone) == fs_bones) + tmp.bones.push_back(&(*it_bone)); + } + + skeletons.push_back(tmp); + } + + // Обновим указатели в моделях для скелета + for (auto & model : models) + { + // Если есть скелет + if (model.skeleton) + { + auto it_skeleton = this->skeletons.begin(); + // Цикл по скелетам оригинала + for (auto f_skeleton = from.skeletons.begin(); f_skeleton != from.skeletons.end(); ++f_skeleton, ++it_skeleton) + // Если адрес оригинального скелета, на который указывает итератор, равен используемому в модели + if (&(*f_skeleton) == model.skeleton) + model.skeleton = &(*it_skeleton); + } + } +} + // Перестройка дерева после копирования или присваивания void Scene::rebuld_tree(const Scene& from) { @@ -134,6 +202,9 @@ void Scene::rebuld_tree(const Scene& from) // Не нашли узел - значит он не часть этой сцены // и изменений по каналу не требуется } + + // Восстановим указатели на кости и перестроим скелеты + rebuld_bones(from); } #define TINYOBJLOADER_IMPLEMENTATION @@ -440,6 +511,9 @@ Scene loadGLTFtoScene(std::string filename) std::vector pNodes(in_model.nodes.size(), NULL); // Индексы родителей (-1 - корневой узел сцены) std::vector parents_id(in_model.nodes.size(), -1); + + // Зарезервируем место под скелеты + result.skeletons.resize(in_model.skins.size()); // Цикл по сценам for (auto &scene : in_model.scenes) @@ -491,6 +565,10 @@ Scene loadGLTFtoScene(std::string filename) { Model model(tmpParent); // Тут используется либо корневой узел сцены, либо вспомогательный узел + // Скелеты записываются только для моделей (не для узлов), если есть + if (node.skin > -1) + model.skeleton = &(*std::next(result.skeletons.begin(), node.skin)); + // Цикл по атрибутам примитива for (auto &attribute : primitive.attributes) { @@ -507,6 +585,10 @@ Scene loadGLTFtoScene(std::string filename) attribute_index = 1; else if (attribute.first.compare("NORMAL") == 0) attribute_index = 2; + else if (attribute.first.compare("JOINTS_0") == 0) + attribute_index = 5; + else if (attribute.first.compare("WEIGHTS_0") == 0) + attribute_index = 6; else continue; @@ -516,13 +598,23 @@ Scene loadGLTFtoScene(std::string filename) glEnableVertexAttribArray(attribute_index); // Определим спецификацию атрибута - glVertexAttribPointer( attribute_index // индекс атрибута, должен совпадать с Layout шейдера - , tinygltf::GetNumComponentsInType(accessor.type) // количество компонент одного элемента - , accessor.componentType // тип - , accessor.normalized ? GL_TRUE : GL_FALSE // нормализованность значений - , accessor.ByteStride(bufferView) // шаг - , ((char *)NULL + accessor.byteOffset) // отступ с начала массива - ); + // Если это индексы костей - привяжем как int + if (attribute_index == 5) + glVertexAttribIPointer( attribute_index // индекс атрибута, должен совпадать с Layout шейдера + , tinygltf::GetNumComponentsInType(accessor.type) // количество компонент одного элемента + , accessor.componentType // тип + , accessor.ByteStride(bufferView) // шаг + , ((char *)NULL + accessor.byteOffset) // отступ с начала массива + ); + // Иначе как float + else + glVertexAttribPointer ( attribute_index // индекс атрибута, должен совпадать с Layout шейдера + , tinygltf::GetNumComponentsInType(accessor.type) // количество компонент одного элемента + , accessor.componentType // тип + , accessor.normalized ? GL_TRUE : GL_FALSE // нормализованность значений + , accessor.ByteStride(bufferView) // шаг + , ((char *)NULL + accessor.byteOffset) // отступ с начала массива + ); } // Если есть индексы @@ -733,6 +825,38 @@ Scene loadGLTFtoScene(std::string filename) result.animations.push_back(animation); } + auto result_skeleton = result.skeletons.begin(); // Итератор по скелетам в нашей сцене + // Цикл по скелетам (зарезервированы в начале) + for (auto &in_skin : in_model.skins) + { + // Цикл по костям скелета + for (int i = 0; i < in_skin.joints.size(); i++) + { + Bone bone; + // Если есть матрица обратного преобразования связывания + if (in_skin.inverseBindMatrices > 0) + { + // Получение матриц обратного преобразования связывания + const auto& accessor = in_model.accessors[in_skin.inverseBindMatrices]; + const auto& bufferView = in_model.bufferViews[accessor.bufferView]; + const auto& buffer = in_model.buffers[bufferView.buffer]; + const glm::mat4* matrix = reinterpret_cast(&(buffer.data[bufferView.byteOffset + accessor.byteOffset])); + + bone.inverseBindMatrix = matrix[i]; + } + + // Узел, выступающий в роли кости + bone.node = pNodes[in_skin.joints[i]]; + + // Добавим в список костей сцены + result.bones.push_back(bone); + + // Добавим кость к скелету + result_skeleton->bones.push_back(&(result.bones.back())); + } + ++result_skeleton; // Двигаем итератор + } + // Зададим трансформацию и родителей для узлов // Цикл по всем индексам узлов for (int node_id = 0; node_id < in_model.nodes.size(); node_id++) diff --git a/src/main.cpp b/src/main.cpp index 061b8f0..798d019 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -153,14 +153,14 @@ int main(void) gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*)); // Загрузка сцены из obj файла - Scene scene = loadGLTFtoScene("../resources/models/rotating-cube_cubic-spline.gltf"); + Scene scene = loadGLTFtoScene("../resources/models/LowpolyHuman_Hi.gltf"); scene.root.e_position().y = -1; scene.root.e_position().z = 3; scene.set_group_id((GLuint64) &scene.root); // Включим первую анимацию, если есть if (scene.animations.size()) scene.animations[0].begin(); - + // Установка цвета очистки буфера цвета glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -458,6 +458,9 @@ int main(void) TextureCube reflections_texture(skybox_texture); reflections_texture.setType(9); + // Буфер для костей + UBO bones_matrices_data(NULL, sizeof(glm::mat4)*MAX_BONES, 5); + // Пока не произойдет событие запроса закрытия окна while(!glfwWindowShouldClose(window)) { @@ -476,7 +479,7 @@ int main(void) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Тут производится рендер - scene.render(gShader, material_data, true); + scene.render(gShader, material_data, &bones_matrices_data, true); rectangle.render(gShader, material_data); // Отрисовка отладочных лампочек со специальным шейдером @@ -530,7 +533,7 @@ int main(void) // Очистка буфера глубины glClear(GL_DEPTH_BUFFER_BIT); // Рендерим геометрию в буфер глубины - scene.render(sunShadowShader, material_data); + scene.render(sunShadowShader, material_data, &bones_matrices_data); rectangle.render(sunShadowShader, material_data); // Изменим размер вывода для стороны кубической карты точечного источника @@ -546,7 +549,7 @@ int main(void) { glUniform1i(pointShadowShader.getUniformLoc("light_i"), i); // Рендерим геометрию в буфер глубины - scene.render(pointShadowShader, material_data); + scene.render(pointShadowShader, material_data, &bones_matrices_data); rectangle.render(pointShadowShader, material_data); }