07/src/Model.cpp

325 lines
14 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 "Model.h"
#include "Camera.h"
extern Camera camera;
Movable::Movable() : position(0), rotation(0), scale(1) {}
// Конструктор копирования
Movable::Movable(const Movable& copy) : position(copy.position), rotation(copy.rotation), scale(copy.scale) {}
// Конструктор без параметров
Model::Model() : verteces_count(0), first_index(0), indices_count(0),
vertex_vbo(VERTEX), index_vbo(ELEMENT), normals_vbo(VERTEX), texCoords_vbo(VERTEX)
{
}
// Конструктор копирования
Model::Model(const Model& copy) :
vao(copy.vao),
verteces_count(copy.verteces_count), first_index(copy.first_index), indices_count(copy.indices_count),
vertex_vbo(copy.vertex_vbo), index_vbo(copy.index_vbo), normals_vbo(copy.normals_vbo), texCoords_vbo(copy.texCoords_vbo),
texture_diffuse(copy.texture_diffuse), material(copy.material)
{
}
Model::~Model()
{
}
// Вызов отрисовки
void Model::render(const GLuint &model_uniform, UBO &material_buffer, const glm::mat4& global_tranform)
{
// Расчитаем матрицу трансформации
glm::mat4 model = global_tranform * this->getTransformMatrix();
glUniformMatrix4fv(model_uniform, 1, GL_FALSE, &model[0][0]);
// Подключаем текстуры
texture_diffuse.use();
// Загружаем данные о материале
material_buffer.load(&material, sizeof(material));
// Подключаем VAO
vao.use();
// Если есть индексы - рисуем с их использованием
if (indices_count)
glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index*sizeof(GLuint)));
// Если есть вершины - рисуем на основании массива вершин
else if (verteces_count)
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
}
// Функция для конфигурации атрибута вершинного буфера
void vertex_attrib_config()
{
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(0);
// Устанавливаем связь между VAO и привязанным VBO
glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера
, 3 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // нормализованность значений
, 0 // шаг
, (void *)0 // отступ с начала массива
);
}
// Загрузка вершин в буфер
void Model::load_verteces(glm::vec3* verteces, GLuint count)
{
// Подключаем VAO и вершинный буфер
vao.use();
vertex_vbo.use();
// Загрузка вершин в память буфера
vertex_vbo.load(verteces, sizeof(glm::vec3)*count);
vertex_attrib_config();
// Запоминаем количество вершин для отрисовки
verteces_count = count;
}
// Загрузка индексов в буфер
void Model::load_indices(GLuint* indices, GLuint count)
{
// Подключаем VAO и индексный буфер
vao.use();
index_vbo.use();
// Загрузка вершин в память буфера
index_vbo.load(indices, sizeof(GLuint)*count);
// Запоминаем количество вершин для отрисовки
indices_count = count;
}
// Функция для конфигурации атрибута вершинного буфера
void texCoords_attrib_config()
{
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(1);
// Устанавливаем связь между VAO и привязанным VBO
glVertexAttribPointer( 1 // индекс атрибута, должен совпадать с Layout шейдера
, 2 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // нормализованность значений
, 0 // шаг
, (void *)0 // отступ с начала массива
);
}
// Загрузка текстурных координат в буфер
void Model::load_texCoords(glm::vec2* texCoords, GLuint count)
{
// Подключаем VAO
vao.use();
texCoords_vbo.use();
// Загрузка вершин в память буфера
texCoords_vbo.load(texCoords, sizeof(glm::vec2)*count);
texCoords_attrib_config();
}
// Функция для конфигурации атрибута вершинного буфера
void normals_attrib_config()
{
// Включаем необходимый атрибут у выбранного VAO
glEnableVertexAttribArray(2);
// Устанавливаем связь между VAO и привязанным VBO
glVertexAttribPointer( 2 // индекс атрибута, должен совпадать с Layout шейдера
, 3 // количество компонент одного элемента
, GL_FLOAT // тип
, GL_FALSE // нормализованность значений
, 0 // шаг
, (void *)0 // отступ с начала массива
);
}
// Загрузка нормалей в буфер
void Model::load_normals(glm::vec3* normals, GLuint count)
{
// Подключаем VAO
vao.use();
normals_vbo.use();
// Загрузка вершин в память буфера
normals_vbo.load(normals, sizeof(glm::vec3)*count);
normals_attrib_config();
}
#include <glm/gtc/matrix_transform.hpp>
// Матрица трансформации модели
glm::mat4 Movable::getTransformMatrix()
{
glm::mat4 transformMatrix = glm::mat4(1.0f);
// Перемещение модели
transformMatrix = glm::translate(transformMatrix, position);
// Поворот модели
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.x), glm::vec3(1.0, 0.0, 0.0));
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.y), glm::vec3(0.0, 1.0, 0.0));
transformMatrix = glm::rotate(transformMatrix, glm::radians(rotation.z), glm::vec3(0.0, 0.0, 1.0));
// Масштабирование
transformMatrix = glm::scale(transformMatrix, scale);
return transformMatrix;
}
// Привязка текстуры к модели
void Model::set_texture(Texture& texture)
{
GLuint type = texture.getType();
switch(type)
{
case TEX_DIFFUSE:
texture_diffuse = texture;
break;
};
}
// Ограничение диапазона из буфера индексов
void Model::set_index_range(GLuint beg, GLuint count)
{
first_index = beg;
indices_count = count;
}
#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...);
}
GrouptedModel loadOBJtoGroupted(const char* filename, const char* mtl_directory, const char* texture_directory)
{
GrouptedModel result;
Model model;
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
// Если в процессе загрузки возникли ошибки - выдадим исключение
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; // текстурные координаты
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)
// Загрузка в буферы
model.load_verteces (&verteces[0], verteces.size());
model.load_normals (&normals[0], normals.size());
model.load_texCoords(&texCords[0], texCords.size());
// Загрузка индексного буфера
model.load_indices (&indices[0], indices.size());
// Создаем копии модели, которые будут рендериться в заданном диапазоне
// И присваиваем текстуры копиям на основании материала
for (int i = 0; i < materials_range.size()-1; i++)
{
result.parts.push_back(model); // Создание копии с общим VAO
auto s = --result.parts.end();
s->set_index_range(materials_range[i], materials_range[i+1]-materials_range[i]);
// Текстуры
Texture diffuse(TEX_DIFFUSE, texture_directory + materials[materials_ids[i]].diffuse_texname);
s->set_texture(diffuse);
// Материал
s->material.ka = glm::vec3(materials[materials_ids[i]].ambient[0], materials[materials_ids[i]].ambient[1], materials[materials_ids[i]].ambient[2]);
s->material.kd = glm::vec3(materials[materials_ids[i]].diffuse[0], materials[materials_ids[i]].diffuse[1], materials[materials_ids[i]].diffuse[2]);
s->material.ks = glm::vec3(materials[materials_ids[i]].specular[0], materials[materials_ids[i]].specular[1], materials[materials_ids[i]].specular[2]);
s->material.p = (materials[materials_ids[i]].shininess > 0.0f) ? 1000.0f / materials[materials_ids[i]].shininess : 1000.0f;
}
return result;
}
// Вызов отрисовки групповой модели
void GrouptedModel::render(const GLuint &model_uniform, UBO &material_buffer)
{
glm::mat4 transform = this->getTransformMatrix();
for (auto& model : parts)
model.render(model_uniform, material_buffer, transform);
}