899 lines
48 KiB
C++
899 lines
48 KiB
C++
#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 <class T>
|
||
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<Model*>(parent))
|
||
move_parent(*it, from.models, this->models);
|
||
else
|
||
// Иначе проверяем на принадлежность к камерам
|
||
if (dynamic_cast<Camera*>(parent))
|
||
move_parent(*it, from.cameras, this->cameras);
|
||
// Иначе это пустой узел
|
||
else
|
||
move_parent(*it, from.nodes, this->nodes);
|
||
|
||
// Не нашли родителя - значит он не часть этой сцены
|
||
// и изменений по нему не требуется
|
||
}
|
||
}
|
||
|
||
// Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
|
||
template <class T>
|
||
void Scene::move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& 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 <class T>
|
||
void Scene::move_animation_target(Node*& target, const std::list<T>& from_nodes, std::list<T>& 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<Model*>(bone->node))
|
||
move_animation_target(bone->node, from.models, this->models);
|
||
else
|
||
// Иначе проверяем на принадлежность к камерам
|
||
if (dynamic_cast<Camera*>(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<Model*>(channel.target))
|
||
move_animation_target(channel.target, from.models, this->models);
|
||
else
|
||
// Иначе проверяем на принадлежность к камерам
|
||
if (dynamic_cast<Camera*>(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 <functional>
|
||
|
||
inline void hash_combine(std::size_t& seed) { }
|
||
|
||
template <typename T, typename... Rest>
|
||
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
|
||
std::hash<T> 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<tinyobj::shape_t> shapes;
|
||
std::vector<tinyobj::material_t> 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<GLuint> indices; // индексы модели
|
||
std::vector<glm::vec3> verteces; // вершины
|
||
std::vector<glm::vec3> normals; // нормали
|
||
std::vector<glm::vec2> texCords; // текстурные координаты
|
||
std::vector<glm::vec3> tangent, bitangent; // касательный и бикасательный веткоры
|
||
size_t hash; // Для уникальных вершин
|
||
std::map <int, int> uniqueVerteces; // словарь для уникальных вершин: ключ - хеш, значение - индекс вершины
|
||
|
||
int last_material_index = 0; // индекс последнего материала (для группировки моделей)
|
||
int count = 0, offset; // для индексов начала и конца в индексном буфере
|
||
std::vector<int> materials_range; // хранилище индексов
|
||
std::vector<int> 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<int> &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<const char*> (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<const unsigned char*> (array);
|
||
result = ubvalues[index] / 255.0;
|
||
|
||
break;
|
||
}
|
||
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
||
{
|
||
const short* svalues = reinterpret_cast<const short*> (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<const unsigned short*>(array);
|
||
result = usvalues[index] / 65535.0;
|
||
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
const float* fvalues = reinterpret_cast<const float*> (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<BO> 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<Texture> 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<Node *> pNodes(in_model.nodes.size(), NULL);
|
||
// Индексы родителей (-1 - корневой узел сцены)
|
||
std::vector<int> parents_id(in_model.nodes.size(), -1);
|
||
|
||
// Зарезервируем место под скелеты
|
||
result.skeletons.resize(in_model.skins.size());
|
||
|
||
// Цикл по сценам
|
||
for (auto &scene : in_model.scenes)
|
||
{
|
||
// Так как у нас есть информация о потомках корневого узла сцены - пройдем рекурсивно и соберем все узлы из этой сцены:
|
||
std::vector<int> 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<const float*>(&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<const glm::mat4*>(&(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;
|
||
}
|