#include "Scene.h" // Конструктор пустой сцены Scene::Scene() { } // Конструктор копирования Scene::Scene(const Scene ©): root(copy.root), nodes(copy.nodes), models(copy.models), cameras(copy.cameras) { rebuld_tree(copy); } // Оператор присваивания Scene& Scene::operator=(const Scene& other) { root = other.root; nodes = other.nodes; models = other.models; cameras = other.cameras; rebuld_tree(other); return *this; } // Рендер сцены void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer) { for (auto & model : models) model.render(shaderProgram, material_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)); } // Перестройка дерева после копирования или присваивания void Scene::rebuld_tree(const Scene& from) { // Восстановим родителей в пустых узлах для копии rebuild_Nodes_list(nodes, from); rebuild_Nodes_list(models, from); rebuild_Nodes_list(cameras, 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); } 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); // Цикл по сценам 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); // Тут используется либо корневой узел сцены, либо вспомогательный узел // Цикл по атрибутам примитива 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 continue; // Подключаем вершинный буфер model.setBO(attribute_index, BOs[accessor.bufferView]); BOs[accessor.bufferView].use(); glEnableVertexAttribArray(attribute_index); // Устанавливаем связь между VAO и привязанным VBO glVertexAttribPointer( attribute_index // индекс атрибута, должен совпадать с Layout шейдера , tinygltf::GetNumComponentsInType(accessor.type) // количество компонент одного элемента , accessor.componentType // тип , accessor.normalized ? GL_TRUE : GL_FALSE // нормализованность значений , accessor.ByteStride(bufferView) // шаг , ((char *)NULL + accessor.byteOffset) // отступ с начала массива ); } // Средство доступа для индексов 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 (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; }