325 lines
14 KiB
C++
325 lines
14 KiB
C++
#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);
|
||
}
|