From 1b58e6b89b480045c8f0de0b6ffe902aa0f8dd99 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/Model.h | 5 + shaders/gshader.frag | 17 ++- src/Model.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 4 +- 4 files changed, 261 insertions(+), 6 deletions(-) diff --git a/include/Model.h b/include/Model.h index eda704e..fe72c11 100644 --- a/include/Model.h +++ b/include/Model.h @@ -6,11 +6,16 @@ #include "Shader.h" #include +#include +#include +#include +#include #include #define DEFAULT_MTL_DIR "./" class GrouptedModel loadOBJtoGroupted(const char* filename, const char* mtl_directory = DEFAULT_MTL_DIR, const char* texture_directory = DEFAULT_MTL_DIR); +class GrouptedModel loadGLTFtoGroupted(std::string filename); void calc_tb(const GLuint* indices, const int indices_count, const glm::vec3* verteces, const glm::vec2* texCords, glm::vec3* tangent, glm::vec3* bitangent); struct Material diff --git a/shaders/gshader.frag b/shaders/gshader.frag index d1c17a9..4b482a2 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/Model.cpp b/src/Model.cpp index 4d1366c..3cbccad 100644 --- a/src/Model.cpp +++ b/src/Model.cpp @@ -532,6 +532,247 @@ void Model::setIndicesBO(BO & data) index_vbo = data; } +#define TINYGLTF_IMPLEMENTATION +#define TINYGLTF_NO_STB_IMAGE_WRITE +#define TINYGLTF_NOEXCEPTION +#define JSON_NOEXCEPTION +#include "tiny_gltf.h" + +// Проход вложенных узлов для расчета дочерних трансформаций +void node_process(int current, int parrent, tinygltf::Model & model, std::vector & transforms) +{ + // Если текущий индекс отрицательный - выходим из вершины + if (current < 0) + return; + + // Получаем узел + tinygltf::Node & node = model.nodes[current]; + + // Текущая матрица + glm::mat4 cur(1); // Изначально единичная + if (node.matrix.size() == 16) // Если задана - заменяем + cur = glm::make_mat4(node.matrix.data()); + + // Если текущий индекс не равен родительскому - родительский будет левой матрицей в произведении + if (current != parrent) + transforms[current] = transforms[parrent]; + transforms[current] *= cur; // Домножаем на правую - матрицу текущего узла + + // Проходим рекурсивно по вложенным узлам + for (auto & child : node.children) + { + node_process(child, current, model, transforms); + } +} + +GrouptedModel loadGLTFtoGroupted(std::string filename) +{ + GrouptedModel 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 transforms(in_model.nodes.size(), glm::mat4(1)); + for (auto & scene : in_model.scenes) + for (auto & node_id : scene.nodes) + node_process(node_id, node_id, in_model, transforms); + + + // Цикл по всем индексам узлов + for (int node_id = 0; node_id < in_model.nodes.size(); node_id++) + { + // Узел по индексу + auto & node = in_model.nodes[node_id]; + // Если нет полигонов - это групповой узел + if (node.mesh < 0) + continue; // Пропускаем + // Полигональная сетка по индексу из узла + auto & mesh = in_model.meshes[node.mesh]; + + // Для каждого примитива связанного с полигональной сеткой + for (auto & primitive : mesh.primitives) + { + Model model; + + // Цикл по атрибутам примитива + 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); + + // Трансформация относительно родительских узлов + model.position = glm::vec3(transforms[node_id][3][0], transforms[node_id][3][1], transforms[node_id][3][2]); + model.scale = { glm::length(glm::vec3(transforms[node_id][0][0],transforms[node_id][1][0],transforms[node_id][2][0])) + , glm::length(glm::vec3(transforms[node_id][0][1],transforms[node_id][1][1],transforms[node_id][2][1])) + , glm::length(glm::vec3(transforms[node_id][0][2],transforms[node_id][1][2],transforms[node_id][2][2])) + }; + glm::extractEulerAngleXYZ(transforms[node_id], model.rotation.x, model.rotation.y, model.rotation.z); + + // Если есть параметры трансформации + if (node.translation.size() == 3) + model.position += glm::vec3(node.translation[0], node.translation[1], node.translation[2]); + if (node.rotation.size() == 4) + model.rotation += glm::eulerAngles(glm::quat(node.rotation[3], glm::vec3(node.rotation[0], node.rotation[1], node.rotation[2]))); + if (node.scale.size() == 3) + model.scale *= glm::vec3(node.scale[0], node.scale[1], node.scale[2]); + + model.rotation *= 180.0f / 3.14159f; // угол в градусы + + // Параметры материалов + 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]); + } + } + } + + // Добавляем в GrouptedModel + result.parts.push_back(model); + } + } + } + + return result; +} + // Вызов отрисовки групповой модели void GrouptedModel::render(ShaderProgram &shaderProgram, UBO &material_buffer) { diff --git a/src/main.cpp b/src/main.cpp index 17061e5..21bc0d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,8 +115,8 @@ int main(void) gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*)); - // Загрузка сцены из obj файла - GrouptedModel scene = loadOBJtoGroupted("../resources/models/blob.obj", "../resources/models/", "../resources/textures/"); + // Загрузка сцены из glTF файла + GrouptedModel scene = loadGLTFtoGroupted("../resources/models/blob.gltf"); scene.scale = glm::vec3(0.01); scene.position.z = 1; scene.set_group_id((GLuint64) &scene);