21/src/Scene.cpp

899 lines
48 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Scene.h"
// Конструктор пустой сцены
Scene::Scene()
{
}
// Конструктор копирования
Scene::Scene(const Scene &copy): 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;
}