#include "Scene.h" // Конструктор пустой сцены 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), bones(copy.bones) // скелеты (skeletons) перестраиваются в методе Scene::rebuild_bones { rebuld_tree(copy); } // Оператор присваивания Scene& Scene::operator=(const Scene& other) { root = other.root; nodes = other.nodes; models = other.models; cameras = other.cameras; 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, UBO *bones_buffer, bool recalc_animations) { // Если требуется пересчитаем анимации if (recalc_animations) for (auto & animation : animations) if (animation.isEnabled()) animation.process(); // Рендер моделей for (auto & model : models) model.render(shaderProgram, material_buffer, bones_buffer); } // Перестройка узлов выбранного списка template void Scene::rebuild_Nodes_list(T& nodes, const Scene& from) { for (auto it = nodes.begin(); it != nodes.end(); it++) { // Берем родителя, который указывает на оригинальный объект Node* parent = it->getParent(); // Если родитель - оригинальный корневой узел, то меняем на собственный корневой узел if (parent == &from.root) { it->setParent(&root); continue; } // Если можно привести к модели, то ищем родителя среди моделей if (dynamic_cast(parent)) move_parent(*it, from.models, this->models); else // Иначе проверяем на принадлежность к камерам if (dynamic_cast(parent)) move_parent(*it, from.cameras, this->cameras); // Иначе это пустой узел else move_parent(*it, from.nodes, this->nodes); // Не нашли родителя - значит он не часть этой сцены // и изменений по нему не требуется } } // Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному template void Scene::move_parent(Node& for_node, const std::list& from_nodes, std::list& this_nodes) { // Возьмем адрес родителя Node* parent = for_node.getParent(); // Цикл по элементам списков для перемещения родителя // Списки в процессе копирования идеинтичные, вторая проверка не требуется for (auto it_from = from_nodes.begin(), it_this = this_nodes.begin(); it_from != from_nodes.end(); ++it_from, ++it_this) // Если адрес объекта, на который указывает итератор, совпадает с родителем - меняем родителя по второму итератору (it_this) if (&(*it_from) == parent) for_node.setParent(&(*it_this)); } // Перестройка узлов анимации template void Scene::move_animation_target(Node*& target, const std::list& from_nodes, std::list& this_nodes) { // Цикл по элементам списков для перемещения родителя // Списки в процессе копирования идеинтичные, вторая проверка не требуется for (auto it_from = from_nodes.begin(), it_this = this_nodes.begin(); it_from != from_nodes.end(); ++it_from, ++it_this) // Если адрес объекта, на который указывает итератор, совпадает с родителем - меняем родителя по второму итератору (it_this) if (&(*it_from) == target) 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) { // Восстановим родителей в пустых узлах для копии rebuild_Nodes_list(nodes, from); rebuild_Nodes_list(models, from); rebuild_Nodes_list(cameras, from); // Восстановим указатели на узлы для каналов анимаций for (auto & animation : animations) for (auto & channel : animation.channels) { // Если целевой узел - оригинальный корневой узел, то меняем на собственный корневой узел if (channel.target == &from.root) { channel.target = &root; continue; } // Если можно привести к модели, то ищем родителя среди моделей if (dynamic_cast(channel.target)) move_animation_target(channel.target, from.models, this->models); else // Иначе проверяем на принадлежность к камерам if (dynamic_cast(channel.target)) move_animation_target(channel.target, from.cameras, this->cameras); // Иначе это пустой узел else move_animation_target(channel.target, from.nodes, this->nodes); // Не нашли узел - значит он не часть этой сцены // и изменений по каналу не требуется } // Восстановим указатели на кости и перестроим скелеты rebuld_bones(from); } #define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" #include inline void hash_combine(std::size_t& seed) { } template inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { std::hash hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); hash_combine(seed, rest...); } Scene loadOBJtoScene(const char* filename, const char* mtl_directory, const char* texture_directory) { Scene result; Model model; // Все модели образованные на основании этой модели будут иметь общего родителя model.setParent(&result.root); tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; // Значение гамма-коррекции extern float inv_gamma; // Если в процессе загрузки возникли ошибки - выдадим исключение if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, mtl_directory)) throw std::runtime_error(err); std::vector indices; // индексы модели std::vector verteces; // вершины std::vector normals; // нормали std::vector texCords; // текстурные координаты std::vector tangent, bitangent; // касательный и бикасательный веткоры size_t hash; // Для уникальных вершин std::map uniqueVerteces; // словарь для уникальных вершин: ключ - хеш, значение - индекс вершины int last_material_index = 0; // индекс последнего материала (для группировки моделей) int count = 0, offset; // для индексов начала и конца в индексном буфере std::vector materials_range; // хранилище индексов std::vector materials_ids; // индексы материалов materials_range.push_back(count); // Закидываем начало отрезка в индексном буфере // Цикл по считанным моделям for (const auto& shape : shapes) { offset = count; // Переменная для last_material_index = shape.mesh.material_ids[(count - offset)/3]; // Запоминаем индекс материала // Цикл по индексам модели for (const auto& index : shape.mesh.indices) { hash = 0; hash_combine( hash , attrib.vertices[3 * index.vertex_index + 0], attrib.vertices[3 * index.vertex_index + 1], attrib.vertices[3 * index.vertex_index + 2] , attrib.normals[3 * index.normal_index + 0], attrib.normals[3 * index.normal_index + 1], attrib.normals[3 * index.normal_index + 2] , attrib.texcoords[2 * index.texcoord_index + 0], attrib.texcoords[2 * index.texcoord_index + 1]); if (!uniqueVerteces.count(hash)) { uniqueVerteces[hash] = verteces.size(); // группируем вершины в массив на основании индексов verteces.push_back({ attrib.vertices[3 * index.vertex_index + 0] , attrib.vertices[3 * index.vertex_index + 1] , attrib.vertices[3 * index.vertex_index + 2] }); // группируем нормали в массив на основании индексов normals.push_back({ attrib.normals[3 * index.normal_index + 0] , attrib.normals[3 * index.normal_index + 1] , attrib.normals[3 * index.normal_index + 2] }); // группируем текстурные координаты в массив на основании индексов texCords.push_back({ attrib.texcoords[2 * index.texcoord_index + 0] , 1-attrib.texcoords[2 * index.texcoord_index + 1] }); } // Сохраняем индекс в массив indices.push_back(uniqueVerteces[hash]); // Если индекс последнего материала изменился, то необходимо сохранить его if (last_material_index != shape.mesh.material_ids[(count - offset)/3]) { materials_range.push_back(count); // как конец отрезка materials_ids.push_back(last_material_index); // как используемый материал last_material_index = shape.mesh.material_ids[(count - offset)/3]; } count++; } // for (const auto& index : shape.mesh.indices) // Если последний материал не загружен - загружаем его if (materials_range[materials_range.size()-1] != count-1) { materials_range.push_back(count); // последний конец отрезка materials_ids.push_back(last_material_index); // последний используемый материал } } // for (const auto& shape : shapes) // Изменим размер массивов tangent.resize(verteces.size()); bitangent.resize(verteces.size()); // Расчет касательных и бикасательных векторов calc_tb(indices.data(), indices.size(), verteces.data(), texCords.data(), tangent.data(), bitangent.data()); // Загрузка в буферы model.load_verteces (&verteces[0], verteces.size()); model.load_normals (&normals[0], normals.size()); model.load_texCoords(&texCords[0], texCords.size()); model.load_tangent(&tangent[0], tangent.size()); model.load_bitangent(&bitangent[0], bitangent.size()); // Загрузка индексного буфера model.load_indices (&indices[0], indices.size()); // Создаем копии модели, которые будут рендериться в заданном диапазоне // И присваиваем текстуры копиям на основании материала for (int i = 0; i < materials_range.size()-1; i++) { result.models.push_back(model); // Создание копии с общим VAO auto s = --result.models.end(); s->set_index_range(materials_range[i]*sizeof(GLuint), materials_range[i+1]-materials_range[i]); // Материал s->material.base_color = pow(glm::vec3(materials[materials_ids[i]].diffuse[0], materials[materials_ids[i]].diffuse[1], materials[materials_ids[i]].diffuse[2]), glm::vec3(1/inv_gamma)); s->material.roughness = 1 - sqrt(materials[materials_ids[i]].shininess/1000); // шероховатость поверхности s->material.metallic = (materials[materials_ids[i]].ambient[0] + materials[materials_ids[i]].ambient[1] + materials[materials_ids[i]].ambient[2]) / 3.0f; s->material.specular = (materials[materials_ids[i]].specular[0] + materials[materials_ids[i]].specular[1] + materials[materials_ids[i]].specular[2]) / 3.0f; s->material.emitted = pow(glm::vec3(materials[materials_ids[i]].emission[0], materials[materials_ids[i]].emission[1], materials[materials_ids[i]].emission[2]), glm::vec3(1/inv_gamma)); // Текстуры if (!materials[materials_ids[i]].diffuse_texname.empty()) { Texture diffuse(TEX_ALBEDO, texture_directory + materials[materials_ids[i]].diffuse_texname); s->set_texture(diffuse); } if (!materials[materials_ids[i]].ambient_texname.empty()) { Texture ambient(TEX_METALLIC, texture_directory + materials[materials_ids[i]].ambient_texname); s->set_texture(ambient); } if (!materials[materials_ids[i]].specular_texname.empty()) { Texture specular(TEX_SPECULAR, texture_directory + materials[materials_ids[i]].specular_texname); s->set_texture(specular); } if (!materials[materials_ids[i]].normal_texname.empty()) { Texture normal(TEX_NORMAL, texture_directory + materials[materials_ids[i]].normal_texname); s->set_texture(normal); } if (!materials[materials_ids[i]].bump_texname.empty()) { Texture heights(TEX_HEIGHTS, texture_directory + materials[materials_ids[i]].bump_texname); s->set_texture(heights); } } return result; } // Изменение флага записи идентификатора для всех моделей void Scene::set_group_id(GLuint64 id, GLuint etc) { for (auto& model : models) { model.id.value = id; if (etc) model.id.etc = etc; } } #define TINYGLTF_IMPLEMENTATION #define TINYGLTF_NO_STB_IMAGE_WRITE #define TINYGLTF_NOEXCEPTION #define JSON_NOEXCEPTION #include "tiny_gltf.h" void collectGLTFnodes(int node_id, std::vector &nodes, tinygltf::Model &in_model) { nodes.push_back(node_id); for (auto& child : in_model.nodes[node_id].children) collectGLTFnodes(child, nodes, in_model); } float getFloatChannelOutput(int type, const void* array, int index) { float result; switch (type) { case TINYGLTF_COMPONENT_TYPE_BYTE: { const char* bvalues = reinterpret_cast (array); result = bvalues[index] / 127.0; if (result < -1.0) result = -1.0; break; } case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { const unsigned char* ubvalues = reinterpret_cast (array); result = ubvalues[index] / 255.0; break; } case TINYGLTF_COMPONENT_TYPE_SHORT: { const short* svalues = reinterpret_cast (array); result = svalues[index] / 32767.0; if (result < -1.0) result = -1.0; break; } case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { const unsigned short* usvalues = reinterpret_cast(array); result = usvalues[index] / 65535.0; break; } default: { const float* fvalues = reinterpret_cast (array); result = fvalues[index]; } } return result; } Scene loadGLTFtoScene(std::string filename) { Scene result; tinygltf::TinyGLTF loader; // Объект загрузчика tinygltf::Model in_model; // Модель в формате загрузчика std::string err; // Строка под ошибки std::string warn; // Строка под предупреждения bool success = loader.LoadASCIIFromFile(&in_model, &err, &warn, filename); // Загрузка из файла // Если есть ошибки или предупреждения - выдадим исключение if (!err.empty() || !warn.empty()) throw std::runtime_error(err + '\n' + warn); // Если все успешно считано - продолжаем загрузку if (success) { // Загрузим данные в вершинные и индексные буферы std::vector BOs; for (auto &bufferView : in_model.bufferViews) { auto &buffer = in_model.buffers[bufferView.buffer]; BOs.push_back(BO((BUFFER_TYPE)bufferView.target, buffer.data.data() + bufferView.byteOffset, bufferView.byteLength)); } // Адрес директории для относительных путей изображений std::string dir = filename.substr(0, filename.find_last_of("/\\") + 1); // Загрузим используемые текстуры std::vector textures; for (auto &image : in_model.images) { // Если длинна файла больше 0, то текстура в отдельном файле if (image.uri.size() > 0) { Texture tmp(TEX_AVAILABLE_COUNT, (dir + image.uri).c_str()); textures.push_back(tmp); } else // иначе она является частью буфера { GLuint format = GL_RGBA; GLenum type = GL_UNSIGNED_BYTE; // Формат пикселя if (image.component == 1) format = GL_RED; else if (image.component == 2) format = GL_RG; else if (image.component == 3) format = GL_RGB; // Тип данных if (image.bits == 16) type = GL_UNSIGNED_SHORT; else if (image.bits == 32) type = GL_UNSIGNED_INT; Texture tmp(image.width, image.height, image.image.data(), 0, format, format, type); textures.push_back(tmp); } } // Указатели на узлы для построения иерархии родитель-потомок 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) { // Так как у нас есть информация о потомках корневого узла сцены - пройдем рекурсивно и соберем все узлы из этой сцены: std::vector scene_nodes; // Цикл по узлам рассматриваемой сцены с рекурсивным проходом потомков for (auto &node_id : scene.nodes) collectGLTFnodes(node_id, scene_nodes, in_model); // Цикл по всем узлам рассматриваемой сцены for (auto &node_id : scene_nodes) { auto &node = in_model.nodes[node_id]; Node *tmpParent = &result.root; // Указатель на родителя, используется если узел сложный (несколько мешей или камера-меш) // Запишем текущий узел как родительский для потомков for (auto& child : node.children) parents_id[child] = node_id; // Проверим наличие сложной сетки bool complex_mesh = false; // Если у узла есть полигональная сетка if (node.mesh > -1) if (in_model.meshes[node.mesh].primitives.size() > 1) complex_mesh = true; // Если узел составной: имеет и камеру, и полигональную сетку // или узел пустой // или имеет сложную полигональную сетку (примитивов больше одного) if (node.camera > -1 && node.mesh > -1 || node.camera == -1 && node.mesh == -1 || complex_mesh) { // Создадим вспомогательный родительский узел для трансформаций result.nodes.push_back(Node(&result.root)); pNodes[node_id] = tmpParent = &result.nodes.back(); // Сохраним в массив узлов и как родителя // В противном случае дополнительный узел не требуется } // Обработаем полигональную сетку if (node.mesh > -1) { auto &mesh = in_model.meshes[node.mesh]; // Для каждого примитива связанного с полигональной сеткой for (auto &primitive : mesh.primitives) { Model model(tmpParent); // Тут используется либо корневой узел сцены, либо вспомогательный узел // Скелеты записываются только для моделей (не для узлов), если есть if (node.skin > -1) model.skeleton = &(*std::next(result.skeletons.begin(), node.skin)); // Цикл по атрибутам примитива for (auto &attribute : primitive.attributes) { // Средство доступа auto &accessor = in_model.accessors[attribute.second]; // Границы буфера auto &bufferView = in_model.bufferViews[accessor.bufferView]; // Индекс привязки на шейдере int attribute_index; if (attribute.first.compare("POSITION") == 0) attribute_index = 0; else if (attribute.first.compare("TEXCOORD_0") == 0) 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; // Подключаем вершинный буфер model.setBO(attribute_index, BOs[accessor.bufferView]); BOs[accessor.bufferView].use(); glEnableVertexAttribArray(attribute_index); // Определим спецификацию атрибута // Если это индексы костей - привяжем как 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) // отступ с начала массива ); } // Если есть индексы if (primitive.indices > -1) { // Средство доступа для индексов auto &accessor = in_model.accessors[primitive.indices]; // Границы индексного буфера auto &bufferView = in_model.bufferViews[accessor.bufferView]; model.setIndicesBO(BOs[accessor.bufferView]); model.set_index_range(accessor.byteOffset, accessor.count, accessor.componentType); } // Если есть материал if (primitive.material > -1) { // Параметры материалов auto &material = in_model.materials[primitive.material]; model.material.base_color = {material.pbrMetallicRoughness.baseColorFactor[0], material.pbrMetallicRoughness.baseColorFactor[1], material.pbrMetallicRoughness.baseColorFactor[2]}; model.material.metallic = material.pbrMetallicRoughness.metallicFactor; model.material.roughness = material.pbrMetallicRoughness.roughnessFactor; model.material.emitted = {material.emissiveFactor[0], material.emissiveFactor[1], material.emissiveFactor[2]}; if (material.pbrMetallicRoughness.baseColorTexture.index > -1) { textures[material.pbrMetallicRoughness.baseColorTexture.index].setType(TEX_ALBEDO); model.set_texture(textures[material.pbrMetallicRoughness.baseColorTexture.index]); } if (material.pbrMetallicRoughness.metallicRoughnessTexture.index > -1) { textures[material.pbrMetallicRoughness.metallicRoughnessTexture.index].setType(TEX_METALLIC); model.set_texture(textures[material.pbrMetallicRoughness.metallicRoughnessTexture.index]); model.material.roughness = -2; } if (material.emissiveTexture.index > -1) { textures[material.emissiveTexture.index].setType(TEX_EMITTED); model.set_texture(textures[material.emissiveTexture.index]); } auto specular_ext = material.extensions.find("KHR_materials_specular"); if (specular_ext != material.extensions.end()) { if (specular_ext->second.Has("specularColorFactor")) { auto &specular_color = specular_ext->second.Get("specularColorFactor"); model.material.specular = (specular_color.Get(0).GetNumberAsDouble() + specular_color.Get(1).GetNumberAsDouble() + specular_color.Get(2).GetNumberAsDouble()) / 3; } if (specular_ext->second.Has("specularColorTexture")) { auto &specular_texture = specular_ext->second.Get("specularColorTexture"); int index = specular_texture.Get("index").GetNumberAsInt(); if (index > -1) { textures[index].setType(TEX_SPECULAR); model.set_texture(textures[index]); } } } } result.models.push_back(model); // Добавляем к сцене // Если ещё не сохранили if (!pNodes[node_id]) pNodes[node_id] = &result.models.back(); // Сохраним адрес созданного узла } } // Обработаем камеру if (in_model.nodes[node_id].camera > -1) { auto &in_camera = in_model.cameras[in_model.nodes[node_id].camera]; // Если камера использует проекцию перспективы if (in_camera.type == "perspective") { Camera camera(in_camera.perspective.aspectRatio, glm::vec3(0.0f), CAMERA_DEFAULT_ROTATION, in_camera.perspective.yfov, in_camera.perspective.znear, in_camera.perspective.zfar); result.cameras.push_back(camera); } // Иначе ортографическую проекцию else { Camera camera(in_camera.orthographic.xmag, in_camera.orthographic.ymag, glm::vec3(0.0f), CAMERA_DEFAULT_ROTATION, in_camera.orthographic.znear, in_camera.orthographic.zfar); result.cameras.push_back(camera); } // Если у узла есть полигональная сетка - сделаем камеру потомком модели, адрес которой записан в вектор if (in_model.nodes[node_id].mesh > -1) result.cameras.back().setParent(pNodes[node_id]); // Иначе узел является камерой сам по себе else { result.cameras.back().setParent(&result.root); pNodes[node_id] = &result.cameras.back(); // Сохраним адрес созданного узла } } } } // Цикл по анимациям for (auto &in_animation : in_model.animations) { Animation animation; for (auto &in_channel : in_animation.channels) { Channel channel; channel.target = pNodes[in_channel.target_node]; // Анимируемый узел // Анимируемый параметр if (in_channel.target_path == "translation") channel.path = POSITION; else if (in_channel.target_path == "rotation") channel.path = ROTATION; else if (in_channel.target_path == "scale") channel.path = SCALE; else throw std::runtime_error("Неподдерживаемый параметр анимации"); // Получение сэмплера для канала const auto& sampler = in_animation.samplers[in_channel.sampler]; // Тип интерполяции if (sampler.interpolation == "LINEAR") channel.interpolation = LINEAR; else if (sampler.interpolation == "STEP") channel.interpolation = STEP; else if (sampler.interpolation == "CUBICSPLINE") channel.interpolation = CUBICSPLINE; else throw std::runtime_error("Неподдерживаемый тип интерполяции"); // Получение временных меток ключевых кадров (Input Accessor) const auto& inputAccessor = in_model.accessors[sampler.input]; const auto& inputBufferView = in_model.bufferViews[inputAccessor.bufferView]; const auto& inputBuffer = in_model.buffers[inputBufferView.buffer]; const float* keyframeTimes = reinterpret_cast(&inputBuffer.data[inputBufferView.byteOffset + inputAccessor.byteOffset]); // Скопируем через метод insert channel.timestamps.insert(channel.timestamps.end(), keyframeTimes, keyframeTimes + inputAccessor.count); // Получение данных ключевых кадров (Output Accessor) const auto& outputAccessor = in_model.accessors[sampler.output]; const auto& outputBufferView = in_model.bufferViews[outputAccessor.bufferView]; const auto& outputBuffer = in_model.buffers[outputBufferView.buffer]; // Зарезервируем место channel.values.resize(inputAccessor.count); if (channel.interpolation == CUBICSPLINE) channel.tangents.resize(inputAccessor.count); // Проверим формат и запишем данные с учетом преобразования if (( (channel.path == POSITION || channel.path == SCALE) && outputAccessor.type == TINYGLTF_TYPE_VEC3) // == 3 || ( channel.path == ROTATION && outputAccessor.type == TINYGLTF_TYPE_VEC4) // == 4 ) { // Цикл по ключевым кадрам for (int keyframe = 0; keyframe < inputAccessor.count; keyframe++) // Цикл по компонентам for (int component = 0; component < outputAccessor.type; component++) { // Для CUBICSPLINE интерполяции требуется дополнительно списать касательные if (channel.interpolation == CUBICSPLINE) { if (channel.path == ROTATION) { channel.tangents[keyframe].in. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + component); channel.values [keyframe]. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type + component); channel.tangents[keyframe].out.quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type*2 + component); } else { channel.tangents[keyframe].in. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + component); channel.values [keyframe]. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type + component); channel.tangents[keyframe].out.vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type*2 + component); } } else if (channel.path == ROTATION) channel.values [keyframe]. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type + component); else channel.values [keyframe]. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type + component); } } else throw std::runtime_error("Неподдерживаемые данные анимации"); animation.channels.push_back(channel); } // Имя анимации // Если имени нет - сгенерируем if (in_animation.name.empty()) { std::string name = filename + std::to_string(result.animations.size()); result.animation_names[name] = result.animations.size(); } else result.animation_names[in_animation.name] = result.animations.size(); 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++) { // Проверка на нулевой указатель if (pNodes[node_id]) { // Если есть матрица трансформации - разберем её на составляющие if (in_model.nodes[node_id].matrix.size() == 16) { glm::mat4 transform = glm::make_mat4(in_model.nodes[node_id].matrix.data()); pNodes[node_id]->e_position() = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); pNodes[node_id]->e_scale() = {glm::length(glm::vec3(transform[0][0], transform[1][0], transform[2][0])), glm::length(glm::vec3(transform[0][1], transform[1][1], transform[2][1])), glm::length(glm::vec3(transform[0][2], transform[1][2], transform[2][2]))}; for (int i = 0; i < 3; i++) transform[i] = glm::normalize(transform[i]); pNodes[node_id]->e_rotation() = glm::quat_cast(transform); } else { // Если есть параметры трансформации if (in_model.nodes[node_id].translation.size() == 3) pNodes[node_id]->e_position() = glm::vec3(in_model.nodes[node_id].translation[0], in_model.nodes[node_id].translation[1], in_model.nodes[node_id].translation[2]); if (in_model.nodes[node_id].rotation.size() == 4) pNodes[node_id]->e_rotation() = glm::quat(in_model.nodes[node_id].rotation[3], glm::vec3(in_model.nodes[node_id].rotation[0], in_model.nodes[node_id].rotation[1], in_model.nodes[node_id].rotation[2])); if (in_model.nodes[node_id].scale.size() == 3) pNodes[node_id]->e_scale() = glm::vec3(in_model.nodes[node_id].scale[0], in_model.nodes[node_id].scale[1], in_model.nodes[node_id].scale[2]); } // Если индекс родителя > -1, то родитель создан и это не корневой узел сцены if (parents_id[node_id] > -1) pNodes[node_id]->setParent(pNodes[parents_id[node_id]]); } } } return result; }