556 lines
24 KiB
C++
556 lines
24 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),
|
||
tangent_vbo(VERTEX), bitangent_vbo(VERTEX),
|
||
normalmapped(false), parallaxmapped(false), displacementmapped(false)
|
||
{
|
||
|
||
}
|
||
|
||
// Конструктор копирования
|
||
Model::Model(const Model& copy) : Movable(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),
|
||
tangent_vbo(copy.tangent_vbo), bitangent_vbo(copy.bitangent_vbo),
|
||
texture_diffuse(copy.texture_diffuse), texture_ambient(copy.texture_ambient), texture_specular(copy.texture_specular),
|
||
texture_heights(copy.texture_heights), texture_normals(copy.texture_normals), material(copy.material),
|
||
normalmapped(copy.normalmapped), parallaxmapped(copy.parallaxmapped), displacementmapped(copy.displacementmapped)
|
||
{
|
||
|
||
}
|
||
|
||
Model::~Model()
|
||
{
|
||
|
||
}
|
||
|
||
// Вызов отрисовки без uniform-даных
|
||
void Model::render()
|
||
{
|
||
// Подключаем VAO
|
||
vao.use();
|
||
// Если есть индексы - рисуем с их использованием
|
||
if (indices_count)
|
||
{
|
||
index_vbo.use();
|
||
glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index*sizeof(GLuint)));
|
||
}
|
||
// Если есть вершины - рисуем на основании массива вершин
|
||
else if (verteces_count)
|
||
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
|
||
}
|
||
|
||
// Вызов отрисовки
|
||
void Model::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||
{
|
||
// Расчитаем матрицу трансформации
|
||
glm::mat4 model = this->getTransformMatrix();
|
||
GLuint model_uniform = shaderProgram.getUniformLoc("model");
|
||
glUniformMatrix4fv(model_uniform, 1, GL_FALSE, &model[0][0]);
|
||
|
||
// Подключаем текстуры
|
||
texture_diffuse.use();
|
||
texture_ambient.use();
|
||
texture_specular.use();
|
||
texture_heights.use();
|
||
texture_normals.use();
|
||
|
||
// Загрузим параметры рельефного текстурирования
|
||
glUniform1i(shaderProgram.getUniformLoc("normalmapped"), normalmapped);
|
||
glUniform1i(shaderProgram.getUniformLoc("parallaxmapped"), parallaxmapped);
|
||
glUniform1i(shaderProgram.getUniformLoc("displacementmapped"), displacementmapped);
|
||
|
||
// Загружаем данные о материале
|
||
material_buffer.load(&material, sizeof(material));
|
||
|
||
render();
|
||
}
|
||
|
||
// Функция для конфигурации атрибута вершинного буфера
|
||
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::scale(transformMatrix, scale);
|
||
// Поворот модели
|
||
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));
|
||
|
||
return transformMatrix;
|
||
}
|
||
|
||
// Привязка текстуры к модели
|
||
void Model::set_texture(Texture& texture)
|
||
{
|
||
GLuint type = texture.getType();
|
||
switch(type)
|
||
{
|
||
case TEX_DIFFUSE:
|
||
texture_diffuse = texture;
|
||
break;
|
||
case TEX_AMBIENT:
|
||
texture_ambient = texture;
|
||
break;
|
||
case TEX_SPECULAR:
|
||
texture_specular = texture;
|
||
break;
|
||
case TEX_HEIGHTS:
|
||
texture_heights = texture;
|
||
break;
|
||
case TEX_NORMAL:
|
||
texture_normals = texture;
|
||
break;
|
||
};
|
||
}
|
||
|
||
// Ограничение диапазона из буфера индексов
|
||
void Model::set_index_range(GLuint beg, GLuint count)
|
||
{
|
||
first_index = beg;
|
||
indices_count = count;
|
||
}
|
||
|
||
// Функция для конфигурации атрибута вершинного буфера
|
||
void tangent_attrib_config()
|
||
{
|
||
// Включаем необходимый атрибут у выбранного VAO
|
||
glEnableVertexAttribArray(3);
|
||
// Устанавливаем связь между VAO и привязанным BO
|
||
glVertexAttribPointer( 3 // индекс атрибута, должен совпадать с Layout шейдера
|
||
, 3 // количество компонент одного элемента
|
||
, GL_FLOAT // тип
|
||
, GL_FALSE // нормализованность значений
|
||
, 0 // шаг
|
||
, (void *)0 // отступ с начала массива
|
||
);
|
||
}
|
||
|
||
// Функция для конфигурации атрибута вершинного буфера
|
||
void bitangent_attrib_config()
|
||
{
|
||
// Включаем необходимый атрибут у выбранного VAO
|
||
glEnableVertexAttribArray(4);
|
||
// Устанавливаем связь между VAO и привязанным BO
|
||
glVertexAttribPointer( 4 // индекс атрибута, должен совпадать с Layout шейдера
|
||
, 3 // количество компонент одного элемента
|
||
, GL_FLOAT // тип
|
||
, GL_FALSE // нормализованность значений
|
||
, 0 // шаг
|
||
, (void *)0 // отступ с начала массива
|
||
);
|
||
}
|
||
|
||
// Загрузка касательных векторов в буфер
|
||
void Model::load_tangent(glm::vec3* tangent, GLuint count)
|
||
{
|
||
// Подключаем VAO
|
||
vao.use();
|
||
|
||
tangent_vbo.use();
|
||
|
||
// Загрузка вершин в память буфера
|
||
tangent_vbo.load(tangent, sizeof(glm::vec3)*count);
|
||
tangent_attrib_config();
|
||
}
|
||
|
||
// Загрузка бикасательных векторов в буфер
|
||
void Model::load_bitangent(glm::vec3* bitangent, GLuint count)
|
||
{
|
||
// Подключаем VAO
|
||
vao.use();
|
||
|
||
bitangent_vbo.use();
|
||
|
||
// Загрузка вершин в память буфера
|
||
bitangent_vbo.load(bitangent, sizeof(glm::vec3)*count);
|
||
bitangent_attrib_config();
|
||
}
|
||
|
||
#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...);
|
||
}
|
||
|
||
// Расчет касательных и бикасательных векторов
|
||
void calc_tb(const GLuint* indices, const int indices_count, const glm::vec3* verteces, const glm::vec2* texCords, glm::vec3* tangent, glm::vec3* bitangent)
|
||
{
|
||
glm::vec2 dTex1, dTex2; // Разница по текстурным координатам
|
||
glm::vec3 dPos1, dPos2; // Разница по координатам вершин
|
||
float f; // Разность произведений
|
||
glm::vec3 tmp; // Для вычислений вектора
|
||
|
||
for (int i = 0; i < indices_count; i+=3)
|
||
{
|
||
// Разности векторов
|
||
dTex1 = texCords[indices[i+1]] - texCords[indices[i]];
|
||
dTex2 = texCords[indices[i+2]] - texCords[indices[i]];
|
||
dPos1 = verteces[indices[i+1]] - verteces[indices[i]];
|
||
dPos2 = verteces[indices[i+2]] - verteces[indices[i]];
|
||
f = dTex1.x * dTex2.y - dTex2.x * dTex1.y;
|
||
|
||
// Покомпонентное вычисление касательного вектора
|
||
tmp.x = (dTex2.y * dPos1.x - dTex1.y * dPos2.x) / f;
|
||
tmp.y = (dTex2.y * dPos1.y - dTex1.y * dPos2.y) / f;
|
||
tmp.z = (dTex2.y * dPos1.z - dTex1.y * dPos2.z) / f;
|
||
// Добавим вектор в контейнер
|
||
tangent[indices[i ]] += tmp; // Для каждого индекса полигона
|
||
tangent[indices[i+1]] += tmp; // значение вектора
|
||
tangent[indices[i+2]] += tmp; // одинаковое
|
||
|
||
// Покомпонентное вычисление бикасательного вектора
|
||
tmp.x = (-dTex2.x * dPos1.x + dTex1.x * dPos2.x) / f;
|
||
tmp.y = (-dTex2.x * dPos1.y + dTex1.x * dPos2.y) / f;
|
||
tmp.z = (-dTex2.x * dPos1.z + dTex1.x * dPos2.z) / f;
|
||
// Добавим вектор в контейнер
|
||
bitangent[indices[i ]] += tmp; // Для каждого индекса полигона
|
||
bitangent[indices[i+1]] += tmp; // значение вектора
|
||
bitangent[indices[i+2]] += tmp; // одинаковое
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
// Значение гамма-коррекции
|
||
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.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);
|
||
Texture ambient(TEX_AMBIENT, texture_directory + materials[materials_ids[i]].ambient_texname);
|
||
s->set_texture(ambient);
|
||
Texture specular(TEX_SPECULAR, texture_directory + materials[materials_ids[i]].specular_texname);
|
||
s->set_texture(specular);
|
||
Texture normal(TEX_NORMAL, texture_directory + materials[materials_ids[i]].normal_texname);
|
||
s->set_texture(normal);
|
||
Texture heights(TEX_HEIGHTS, texture_directory + materials[materials_ids[i]].bump_texname);
|
||
s->set_texture(heights);
|
||
|
||
// Материал
|
||
s->material.ka = pow(glm::vec3(materials[materials_ids[i]].ambient[0], materials[materials_ids[i]].ambient[1], materials[materials_ids[i]].ambient[2]), glm::vec3(1/inv_gamma));
|
||
s->material.kd = 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.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(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||
{
|
||
for (auto& model : parts)
|
||
{
|
||
model.position += position;
|
||
model.rotation += rotation;
|
||
model.scale *= scale;
|
||
model.render(shaderProgram, material_buffer);
|
||
model.position -= position;
|
||
model.rotation -= rotation;
|
||
model.scale /= scale;
|
||
}
|
||
}
|
||
|
||
Model& GrouptedModel::operator[](int index)
|
||
{
|
||
return parts[index];
|
||
}
|
||
|
||
// Генерирует сферу заданного радиуса с определенным количеством сегментов
|
||
void genShpere(Model& model, float radius, int sectorsCount)
|
||
{
|
||
std::vector<glm::vec3> vertices;
|
||
std::vector<glm::vec3> normals;
|
||
std::vector<GLuint> indices;
|
||
|
||
float x, y, z, xy; // Позиция вершины
|
||
float nx, ny, nz, lengthInv = 1.0f / radius; // Нормаль вершины
|
||
float PI = 3.14159265;
|
||
float sectorStep = PI / sectorsCount; // Шаг сектора
|
||
float longAngle, latAngle; // Углы
|
||
|
||
for(int i = 0; i <= sectorsCount; ++i)
|
||
{
|
||
latAngle = PI / 2 - i * sectorStep; // Начиная с pi/2 до -pi/2
|
||
xy = radius * cos(latAngle); // r * cos(lat)
|
||
z = radius * sin(latAngle); // r * sin(lat)
|
||
|
||
// добавляем (sectorCount+1) вершин на сегмент
|
||
// Последняя и первая вершины имеют одинаковые нормали и координаты
|
||
for(int j = 0; j <= sectorsCount; ++j)
|
||
{
|
||
longAngle = j * 2 * sectorStep; // Начиная с 0 до 2*pi
|
||
|
||
// Положение вершины (x, y, z)
|
||
x = xy * cos(longAngle); // r * cos(lat) * cos(long)
|
||
y = xy * sin(longAngle); // r * cos(lat) * sin(long)
|
||
vertices.push_back({x, y, z});
|
||
|
||
// Нормали (nx, ny, nz)
|
||
nx = x * lengthInv;
|
||
ny = y * lengthInv;
|
||
nz = z * lengthInv;
|
||
normals.push_back({nx, ny, nz});
|
||
}
|
||
}
|
||
int k1, k2;
|
||
for(int i = 0; i < sectorsCount; ++i)
|
||
{
|
||
k1 = i * (sectorsCount + 1); // начало текущего сегмента
|
||
k2 = k1 + sectorsCount + 1; // начало следующего сегмента
|
||
|
||
for(int j = 0; j < sectorsCount; ++j, ++k1, ++k2)
|
||
{
|
||
// 2 треугольника на один сегмент
|
||
// k1, k2, k1+1
|
||
if(i != 0)
|
||
{
|
||
indices.push_back(k1);
|
||
indices.push_back(k2);
|
||
indices.push_back(k1 + 1);
|
||
}
|
||
|
||
// k1+1, k2, k2+1
|
||
if(i != (sectorsCount-1))
|
||
{
|
||
indices.push_back(k1 + 1);
|
||
indices.push_back(k2);
|
||
indices.push_back(k2 + 1);
|
||
}
|
||
|
||
}
|
||
}
|
||
// Загрузка в модель
|
||
model.load_verteces(&vertices[0], vertices.size());
|
||
model.load_normals(&normals[0], normals.size());
|
||
model.load_indices(&indices[0], indices.size());
|
||
}
|