diff --git a/.vscode/settings.json b/.vscode/settings.json index 560de08..f3d0974 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,12 @@ "fstream": "cpp", "iosfwd": "cpp", "map": "cpp", - "atomic": "cpp" + "atomic": "cpp", + "iostream": "cpp", + "array": "cpp", + "functional": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp" } } \ No newline at end of file diff --git a/include/Model.h b/include/Model.h new file mode 100644 index 0000000..3ba8af3 --- /dev/null +++ b/include/Model.h @@ -0,0 +1,75 @@ +#ifndef MODEL_H +#define MODEL_H + +#include "Buffers.h" + +#include +#include +#include + +#include + +// Класс узла сцены +class Node +{ + public: + Node(Node* parent = NULL); // Конструктор с заданным родителем (по умолчанию NULL) + Node(const Node& copy); // Конструктор копирования + Node& operator=(const Node& other); // Оператор присваивания + virtual ~Node(); + + void setParent(Node * parent); // Устанавливает родителя для узла + + virtual const glm::mat4& getTransformMatrix(); // Возвращает матрицу трансформации модели + bool isChanged(); // Возвращает необходимость пересчета матрицы трансформации + + const glm::vec3& c_position() const; // Константный доступ к позиции + const glm::quat& c_rotation() const; // Константный доступ к повороту + const glm::vec3& c_scale() const; // Константный доступ к масштабированию + virtual glm::vec3& e_position(); // Неконстантная ссылка для изменений позиции + virtual glm::quat& e_rotation(); // Неконстантная ссылка для изменений поворота + virtual glm::vec3& e_scale(); // Неконстантная ссылка для изменений масштабирования + + Node* getParent(); // Возвращает указатель на родителя + const std::vector& getChildren() const; // Возвращает ссылку на вектор дочерних узлов + + protected: + Node *parent; // Родительский узел + std::vector children; // Узлы-потомки !Не должны указывать на NULL! + + glm::vec3 position; // позиция модели + glm::quat rotation; // поворот модели + glm::vec3 scale; // масштабирование модели + + bool changed; // Флаг необходимости пересчета матрицы трансформации + glm::mat4 transform; // Матрица трансформации модели + bool parent_changed; // Флаг изменений у родителя - необходимость пересчета итоговой трансформации + glm::mat4 result_transform; // Итоговая трансформация с учетом родительской + + virtual void recalcMatrices(); // Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed + void invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed +}; + +// Класс модели +class Model : public Node +{ + public: + Model(Node *parent = NULL); // Конструктор по умолчанию + Model(const Model& copy); // Конструктор копирования + Model& operator=(const Model& other); // Оператор присваивания + virtual ~Model(); + + void render(); // Вызов отрисовки + + void load_verteces(glm::vec3* verteces, GLuint count); // Загрузка вершин в буфер + void load_indices(GLuint* indices, GLuint count); // Загрузка индексов в буфер + void set_index_range(size_t first_byteOffset, size_t count); // Ограничение диапазона из буфера индексов + + private: + VAO vao; + BO vertex_vbo, index_vbo; // вершинный и индексный буферы + GLuint verteces_count; // Количество вершин + size_t first_index_byteOffset, indices_count; // Сдвиг в байтах для первого и количество индексов +}; + +#endif // MODEL_H diff --git a/src/Model.cpp b/src/Model.cpp new file mode 100644 index 0000000..6f3094a --- /dev/null +++ b/src/Model.cpp @@ -0,0 +1,303 @@ +#include "Model.h" + +#include + +// Конструктор с заданным родителем (по умолчанию NULL) +Node::Node(Node* parent_) : parent(parent_), result_transform(1), parent_changed(false), +position(0), rotation(1.0f, 0.0f, 0.0f, 0.0f), scale(1), changed(false), transform(1) +{ + if (parent) + { + // Запишем себя в потомки + parent->children.push_back(this); + parent_changed = true; + } +} + +// Конструктор копирования +Node::Node(const Node& copy): position(copy.position), rotation(copy.rotation), scale(copy.scale), +parent(copy.parent), changed(copy.changed), parent_changed(copy.parent_changed), transform(1), result_transform(1) +{ + // Запишем себя в потомки + if (parent) + parent->children.push_back(this); + // Если у оригинала не было изменений - перепишем матрицу трансформации + if (!changed) + transform = copy.transform; + // Если у родителя не было изменений для оригинала - перепишем результирующую матрицу трансформации + if (!parent_changed) + result_transform = copy.result_transform; +} + +Node::~Node() +{ + setParent(NULL); // Удаляем себя из потомков + // Сообщаем потомкам об удалении родителя + for (Node* child : children) + child->setParent(NULL); +} + +// Возвращает необходимость пересчета матрицы трансформации +bool Node::isChanged() +{ + return changed; +} + +// Константный доступ к позиции +const glm::vec3& Node::c_position() const +{ + return position; +} + +// Константный доступ к повороту +const glm::quat& Node::c_rotation() const +{ + return rotation; +} + +// Константный доступ к масштабированию +const glm::vec3& Node::c_scale() const +{ + return scale; +} + +// Неконстантная ссылка для изменений позиции +glm::vec3& Node::e_position() +{ + changed = true; // Флаг о изменении + invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed + return position; +} + +// Неконстантная ссылка для изменений поворота +glm::quat& Node::e_rotation() +{ + changed = true; // Флаг о изменении + invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed + return rotation; +} + +// Неконстантная ссылка для изменений масштабирования +glm::vec3& Node::e_scale() +{ + changed = true; // Флаг о изменении + invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed + return scale; +} + +// Возвращает матрицу трансформации модели +const glm::mat4& Node::getTransformMatrix() +{ + // Если требуется - пересчитаем матрицу + recalcMatrices(); + + return result_transform; +} + +// Пересчет матрицы трансформации модели, если это требуется +void Node::recalcMatrices() +{ + // Если было изменение по векторам позиции, поворота и масштабирования + if (changed) + { + transform = glm::mat4(1.0f); + // Перемещение модели + transform = glm::translate(transform, position); + // Поворот модели + transform = transform * glm::mat4_cast(rotation); + // Масштабирование + transform = glm::scale(transform, scale); + } + + // Если собственная или родительская матрицы менялись - необходимо пересчитать итоговую + if (changed || parent_changed) + { + if (parent) // Если есть родитель + result_transform = parent->getTransformMatrix() * transform; + else // Если нет родителя + result_transform = transform; + + parent_changed = changed = false; // Изменения применены + } +} + +// Проход потомков в глубину с изменением флага parent_changed +void Node::invalidateParent() +{ + // Цикл по потомкам + for (Node* child : children) + { + child->parent_changed = true; // Флаг + child->invalidateParent(); // Рекурсивный вызов для потомков выбранного потомка + } +} + +// Устанавливает родителя для узла +void Node::setParent(Node * parent) +{ + // Если замена происходит на другого родителя + if (parent != this->parent) + { + Node* tmp = parent; + // Проверка на зацикливание об самого себя + while (tmp) + { + if (tmp == this) + return; // Можно выдать exception + tmp = tmp->parent; + } + // Если есть старый родитель - удалим себя из его потомков + if (this->parent) + { + // Поиск в списке родительских потомков + auto position = std::find(this->parent->children.begin(), this->parent->children.end(), this); + // Если итератор указывает в конец - ничего не найдено + if (position != this->parent->children.end()) + this->parent->children.erase(position); // Само удаление + } + + this->parent = parent; // Заменяем указатель на родителя + // Если родитель не NULL - добавляем себя в детей + if (parent) + parent->children.push_back(this); + // В любом случае необходимо пересчитать собственную итоговую матрицу + parent_changed = true; + } +} + +// Возвращает указатель на родителя +Node* Node::getParent() +{ + return parent; +} + +// Возвращает ссылку на вектор дочерних узлов +const std::vector& Node::getChildren() const +{ + return children; +} + +// Оператор присваивания +Node& Node::operator=(const Node& other) +{ + position = other.position; + rotation = other.rotation; + scale = other.scale; + changed = other.changed; + + if (!changed) + transform = other.transform; + + setParent(other.parent); + + // Если у other флаг parent_changed == false, то можно переписать матрицу результата с него + if (!other.parent_changed) + { + result_transform = other.result_transform; + parent_changed = false; // Сбрасываем флаг после смены родителя + } + + return *this; +} + +// Конструктор по умолчанию +Model::Model(Node *parent) : Node(parent), verteces_count(0), first_index_byteOffset(0), indices_count(0), +vertex_vbo(VERTEX), index_vbo(ELEMENT) +{ + +} + +// Конструктор копирования +Model::Model(const Model& copy) : Node(copy), +vao(copy.vao), +verteces_count(copy.verteces_count), first_index_byteOffset(copy.first_index_byteOffset), indices_count(copy.indices_count), +vertex_vbo(copy.vertex_vbo), index_vbo(copy.index_vbo) +{ + +} + +// Оператор присваивания +Model& Model::operator=(const Model& other) +{ + Node::operator=(other); // Явный вызов родительского оператора копирования + + vao = other.vao; + verteces_count = other.verteces_count; + first_index_byteOffset = other.first_index_byteOffset; + indices_count = other.indices_count; + + vertex_vbo = other.vertex_vbo; + index_vbo = other.index_vbo; + + return *this; +} + +Model::~Model() +{ + +} + +// Вызов отрисовки +void Model::render() +{ + // Подключаем VAO + vao.use(); + // Если есть индексы - рисуем с их использованием + if (indices_count) + { + index_vbo.use(); + glDrawElements(GL_TRIANGLES, indices_count, GL_UNSIGNED_INT, (void*)(first_index_byteOffset)); + } + // Если есть вершины - рисуем на основании массива вершин + else if (verteces_count) + glDrawArrays(GL_TRIANGLES, 0, verteces_count); +} + +// Функция для конфигурации атрибута вершинного буфера +void vertex_attrib_config() +{ + // Определим спецификацию атрибута + glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера + , 3 // количество компонент одного элемента + , GL_FLOAT // тип + , GL_FALSE // необходимость нормировать значения + , 0 // шаг + , (void *)0 // отступ с начала массива + ); + // Включаем необходимый атрибут у выбранного VAO + glEnableVertexAttribArray(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 Model::set_index_range(size_t first_byteOffset, size_t count) +{ + first_index_byteOffset = first_byteOffset; + indices_count = count; +} diff --git a/src/main.cpp b/src/main.cpp index 98db93b..137bc83 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ #include -#include "Buffers.h" +#include "Model.h" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 @@ -112,21 +112,6 @@ GLuint LoadShaders(const char *vertex_file, const char *fragment_file) return programID; } -// Функция для конфигурации атрибутов -void attrib_config() -{ - // Определим спецификацию атрибута - glVertexAttribPointer( 0 // индекс атрибута, должен совпадать с Layout шейдера - , 3 // количество компонент одного элемента - , GL_FLOAT // тип - , GL_FALSE // необходимость нормировать значения - , 0 // шаг - , (void *)0 // отступ с начала массива - ); - // Включаем необходимый атрибут у выбранного VAO - glEnableVertexAttribArray(0); -} - int main(void) { GLFWwindow* window; // Указатель на окно GLFW3 @@ -172,29 +157,23 @@ int main(void) // Активация шейдера glUseProgram(shaderProgram); - // Вершины треугольника + // Вершины прямоугольника glm::vec3 verticies[] = { {-0.5f, -0.5f, 0.0f} , { 0.5f, -0.5f, 0.0f} , { 0.5f, 0.5f, 0.0f} , {-0.5f, 0.5f, 0.0f} }; - - // VAO - VAO vao; - // VBO вершинный - BO vbo_vert(VERTEX); - attrib_config(); // Конфигурация аттрибутов - // Загрузка вершин в используемый буфер вершин - vbo_vert.load(verticies, sizeof(verticies)); + // Модель прямоугольника + Model rectangle; + + // Загрузка вершин модели + rectangle.load_verteces(verticies, sizeof(verticies)/sizeof(glm::vec3)); // индексы вершин GLuint indices[] = {0, 1, 2, 2, 3, 0}; - // VBO элементный (индексы вершин) - BO vbo_elem(ELEMENT); - - // Загрузка индексов в используемый элементный буфер - vbo_elem.load(indices, sizeof(indices)); + // Загрузка индексов модели + rectangle.load_indices(indices, sizeof(indices)); // Установка цвета очистки буфера цвета glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -207,9 +186,7 @@ int main(void) glClear(GL_COLOR_BUFFER_BIT); // Тут производится рендер - vao.use(); - glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLuint), GL_UNSIGNED_INT, (void*)0); - vao.disable(); + rectangle.render(); // Представление содержимого буфера цепочки показа на окно glfwSwapBuffers(window);