From 74ff0a70c039673ee9fdb5c3bd9153cc7f1fbfe7 Mon Sep 17 00:00:00 2001 From: "R.E. Kovalev" Date: Mon, 24 Jul 2023 14:44:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F-?= =?UTF-8?q?=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D1=87=D0=B8=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Scene.h | 6 + shaders/gshader.frag | 17 ++- src/Scene.cpp | 302 +++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- 4 files changed, 322 insertions(+), 5 deletions(-) diff --git a/include/Scene.h b/include/Scene.h index b86b436..aecae3e 100644 --- a/include/Scene.h +++ b/include/Scene.h @@ -6,8 +6,14 @@ #include "Model.h" #include "Camera.h" +#include +#include +#include +#include + #define DEFAULT_MTL_DIR "./" class Scene loadOBJtoScene(const char* filename, const char* mtl_directory = DEFAULT_MTL_DIR, const char* texture_directory = DEFAULT_MTL_DIR); +class Scene loadGLTFtoScene(std::string filename); // Класс сцены class Scene diff --git a/shaders/gshader.frag b/shaders/gshader.frag index d930562..786423b 100644 --- a/shaders/gshader.frag +++ b/shaders/gshader.frag @@ -104,10 +104,19 @@ void main() // Сохранение базового цвета gBaseColor.rgb = base_color.r<0?texture(tex_albedo, new_texCoord).rgb:base_color; - // Сохранение шероховатости - gRMS.r = roughness<0?texture(tex_roughness, new_texCoord).r:roughness; - // Сохранение металличности - gRMS.g = metallic<0?texture(tex_metallic, new_texCoord).r:metallic; + // Если используется двухканальная текстура + if (roughness < -1) + { + // Сохранение шероховатости и металличности + gRMS.rg = texture(tex_metallic, new_texCoord).bg; + } + else + { + // Сохранение шероховатости + gRMS.r = roughness<0?texture(tex_roughness, new_texCoord).r:roughness; + // Сохранение металличности + gRMS.g = metallic<0?texture(tex_metallic, new_texCoord).r:metallic; + } // Сохранение интенсивности блика диэлектриков gRMS.b = specular<0?texture(tex_specular, new_texCoord).r:specular; // Сохранение идентификатора объекта diff --git a/src/Scene.cpp b/src/Scene.cpp index 40fff1b..fd9dc98 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -263,3 +263,305 @@ void Scene::set_group_id(GLuint64 id, GLuint 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); + // Определим спецификацию атрибута + 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 (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; +} diff --git a/src/main.cpp b/src/main.cpp index fe6a225..c9c939c 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -153,7 +153,7 @@ int main(void) gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*)); // Загрузка сцены из obj файла - Scene scene = loadOBJtoScene("../resources/models/blob.obj", "../resources/models/", "../resources/textures/"); + Scene scene = loadGLTFtoScene("../resources/models/blob.gltf"); scene.root.e_scale() = glm::vec3(0.01); scene.root.e_position().z = 1; scene.set_group_id((GLuint64) &scene.root);