#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(const GLuint &model_uniform, UBO &material_buffer) { for (auto & model : models) model.render(model_uniform, 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; // Если в процессе загрузки возникли ошибки - выдадим исключение 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; // текстурные координаты 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) // Загрузка в буферы model.load_verteces (&verteces[0], verteces.size()); model.load_normals (&normals[0], normals.size()); model.load_texCoords(&texCords[0], texCords.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]); // Текстуры Texture diffuse(TEX_DIFFUSE, texture_directory + materials[materials_ids[i]].diffuse_texname); s->set_texture(diffuse); Texture ambient(TEX_AMBIENT, texture_directory + materials[materials_ids[i]].ambient_texname); s->set_texture(ambient); Texture specular(TEX_SPECULAR, texture_directory + materials[materials_ids[i]].specular_texname); s->set_texture(specular); // Материал s->material.ka = glm::vec3(materials[materials_ids[i]].ambient[0], materials[materials_ids[i]].ambient[1], materials[materials_ids[i]].ambient[2]); s->material.kd = glm::vec3(materials[materials_ids[i]].diffuse[0], materials[materials_ids[i]].diffuse[1], materials[materials_ids[i]].diffuse[2]); s->material.ks = glm::vec3(materials[materials_ids[i]].specular[0], materials[materials_ids[i]].specular[1], materials[materials_ids[i]].specular[2]); s->material.p = (materials[materials_ids[i]].shininess > 0.0f) ? 1000.0f / materials[materials_ids[i]].shininess : 1000.0f; } return result; }