Копия проекта с 06

This commit is contained in:
Ковалев Роман Евгеньевич 2022-11-14 20:24:58 +03:00 committed by R.E. Kovalev
parent ebc18f9d4d
commit 969862ca56
16 changed files with 1424 additions and 0 deletions

20
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,20 @@
{
"configurations": [
{
"name": "some_name",
"includePath": [
"${workspaceFolder}/include",
"${workspaceFolder}/../dependencies/GLFW/include",
"${workspaceFolder}/../dependencies/glad/include",
"${workspaceFolder}/../dependencies/glm",
"${workspaceFolder}/../dependencies/stb",
"${workspaceFolder}/../dependencies/tinyobjloader"
],
"compilerPath": "C:/MinGW/bin/g++.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "gcc-x86"
}
],
"version": 4
}

48
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,48 @@
{
"files.associations": {
"fstream": "cpp",
"iosfwd": "cpp",
"map": "cpp",
"atomic": "cpp",
"new": "cpp",
"array": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
}
}

79
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,79 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++.exe сборка активного файла",
"command": "C:/MinGW/bin/g++.exe",
"args": [
"-fdiagnostics-color=always",
"${workspaceRoot}/src/*.cpp",
"${workspaceRoot}/../dependencies/glad/src/glad.c",
"-I${workspaceRoot}/include",
"--std=c++11",
"-I${workspaceRoot}/../dependencies/GLFW/include",
"-L${workspaceRoot}/../dependencies/GLFW/lib-mingw",
"-I${workspaceFolder}/../dependencies/glad/include",
"-I${workspaceFolder}/../dependencies/glm",
"-I${workspaceFolder}/../dependencies/stb",
"-I${workspaceFolder}/../dependencies/tinyobjloader",
"-static",
"-lopengl32",
"-lglfw3dll",
"-o",
"${workspaceRoot}/${workspaceFolderBasename}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Задача создана отладчиком."
},
{
"type": "cppbuild",
"label": "C/C++ x64: g++.exe сборка активного файла",
"command": "C:/MinGW64/bin/g++.exe",
"args": [
"-fdiagnostics-color=always",
"${workspaceRoot}/src/*.cpp",
"${workspaceRoot}/../dependencies/glad/src/glad.c",
"-I${workspaceRoot}/include",
"--std=c++11",
"-I${workspaceRoot}/../dependencies/GLFW/include",
"-L${workspaceRoot}/../dependencies/GLFW/lib-mingw-w64",
"-I${workspaceFolder}/../dependencies/glad/include",
"-I${workspaceFolder}/../dependencies/glm",
"-I${workspaceFolder}/../dependencies/stb",
"-I${workspaceFolder}/../dependencies/tinyobjloader",
"-static",
"-lopengl32",
"-lglfw3dll",
"-o",
"${workspaceRoot}/${workspaceFolderBasename}.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Задача создана отладчиком."
}
],
"version": "2.0.0"
}

62
include/Buffers.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef BUFFERS_H
#define BUFFERS_H
#include <glad/glad.h>
#include <map>
// Объект массива вершин
class VAO
{
public:
VAO(); // Создает VAO и активирует его
~VAO(); // Уничтожает VAO
VAO(const VAO & copy); // Конструктор копирования
VAO& operator=(const VAO & other); // Оператор присваивания
void use(); // Активация VAO
static void disable(); // Деактивация активного VAO
private:
GLuint handler; // Дескриптор
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
};
// Тип буфера
enum BUFFER_TYPE { VERTEX = GL_ARRAY_BUFFER
, ELEMENT = GL_ELEMENT_ARRAY_BUFFER
, UNIFORM = GL_UNIFORM_BUFFER
};
// Объект вершинного буфера
class BO
{
public:
BO(BUFFER_TYPE type); // Создает пустой буфер заданного типа
BO(BUFFER_TYPE type, const void *data, int size); // Создает и загружает туда данные
~BO(); // Уничтожает буфер
BO(const BO & copy); // Конструктор копирования
BO& operator=(const BO & other); // Оператор присваивания
void load(const void *data, int size, GLuint mode = GL_STATIC_DRAW); // Загрузка данных в буфер
void use();
protected:
GLuint handler; // Дескриптор
BUFFER_TYPE type; // Тип буфера
private:
static std::map<GLuint, GLuint> handler_count; // Счетчик использований дескриптора
};
// Объект uniform-буфера
class UBO : public BO
{
public:
UBO(int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
UBO(const void *data, int size, int binding); // Создает пустой uniform-буфер заданного размера с автоматической привязкой
void rebind(int binding); // Перепривязка
void loadSub(const void *data, int size, int offset = 0); // Загрузка с отступом
};
#endif // BUFFERS_H

49
include/Camera.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef CAMERA_H
#define CAMERA_H
#include <GLM/glm.hpp>
// Ближняя граница области отсечения
#define CAMERA_NEAR 0.1f
// Дальняя граница области отсечения
#define CAMERA_FAR 100.0f
// Вектор, задающий верх для камеры
#define CAMERA_UP_VECTOR glm::vec3(0.0f, 1.0f, 0.0f)
// Стандартный угол обзора
#define CAMERA_FOVy 60.0f
class Camera
{
public:
Camera(float aspect, const glm::vec3 &position = glm::vec3(0.0f, 0.0f, 0.0f), const glm::vec2 &xyOffset = glm::vec2(90.0f, 0.0f), float fovy = CAMERA_FOVy); // Конструктор камеры с проекцией перспективы
Camera(float width, float height, const glm::vec3 &position = glm::vec3(0.0f, 0.0f, 0.0f), const glm::vec2 &xyOffset = glm::vec2(90.0f, 0.0f)); // Конструктор ортографической камеры
virtual ~Camera(); // Деструктор
const glm::mat4& getVP(); // Возвращает ссылку на константную матрицу произведения матриц вида и проекции
const glm::mat4& getProjection(); // Возвращает ссылку на константную матрицу проекции
const glm::mat4& getView(); // Возвращает ссылку на константную матрицу вида
void rotate(const glm::vec2 &xyOffset); // Поворачивает камеру на dx и dy пикселей
void move(const glm::vec3 &posOffset); // Сдвигает камеру на указанный вектор (dx,dy,dz)
void setPosition(const glm::vec3 &position); // Устанавливает местоположение
void setRotation(const glm::vec2 &xyOffset); // Устанавливает угол поворота камеры
void setPerspective(float fov, float aspect); // Устанавливает заданную матрицу перспективы
void setOrtho(float width, float height); // Устанавливает заданную ортографическую матрицу
void setSensitivity(float sensitivity); // Изменяет чувствительность мыши
protected:
Camera(const glm::vec3 &position, const glm::vec2 &xyOffset); // Защищенный (protected) констуктор камеры без перспективы
void recalcTarget(); // Пересчет цели, на которую смотрит камера
void recalcView(); // Пересчет матрицы вида
void recalcVP(); // Пересчет произведения матриц
glm::vec3 position; // Местоположение камеры
glm::vec3 target; // Цель, на которую смотрит камера
glm::vec2 currentRotation; // Текущий поворот камеры
glm::mat4 projection; // Матрица проекции
glm::mat4 view; // Матрица вида
glm::mat4 vp; // Матрица произведения вида и проекции
bool requiredRecalcVP; // Необходимость пересчета матрицы вида и проекции камеры
bool requiredRecalcView; // Необходимость пересчета матрицы вида камеры
float sensitivity; // Чувствительность мыши
};
#endif // CAMERA_H

59
include/Model.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef MODEL_H
#define MODEL_H
#include "Buffers.h"
#include "Texture.h"
#include <GLM/glm.hpp>
#include <vector>
#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 Movable
{
public:
Movable(); // Конструктор без параметров
Movable(const Movable& copy); // Конструктор копирования
glm::vec3 position; // позиция модели
glm::vec3 rotation; // поворот модели
glm::vec3 scale; // масштабирование модели
glm::mat4 getTransformMatrix(); // Матрица трансформации модели
};
// Класс модели с примененным материалом
class Model : public Movable
{
public:
Model(); // Конструктор без параметров
Model(const Model& copy); // Конструктор копирования
~Model();
void render(const GLuint &model_uniform, const glm::mat4& global_tranform = glm::mat4(1)); // Вызов отрисовки
void load_verteces(glm::vec3* verteces, GLuint count); // Загрузка вершин в буфер
void load_indices(GLuint* indices, GLuint count); // Загрузка индексов в буфер
void load_texCoords(glm::vec2* texCoords, GLuint count); // Загрузка текстурных координат в буфер
void load_normals(glm::vec3* normals, GLuint count); // Загрузка нормалей в буфер
void set_texture(Texture& texture); // Привязка текстуры к модели
void set_index_range(GLuint beg, GLuint count); // Ограничение диапазона из буфера индексов
private:
VAO vao;
BO vertex_vbo, index_vbo; // вершинный и индексный буферы
BO normals_vbo, texCoords_vbo; // буферы с нормалями и текстурными координатами
GLuint verteces_count; // Количество вершин
GLuint first_index, indices_count; // Первый и количество индексов
Texture texture_diffuse; // Диффузная текстура
};
// Класс сгруппированной модели
class GrouptedModel: public Movable
{
public:
void render(const GLuint &model_uniform); // Вызов отрисовки
std::vector<Model> parts;
};
#endif // MODEL_H

30
include/Shader.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <map>
#include <string>
// Класс шейдерной программы
class ShaderProgram
{
public:
ShaderProgram();
ShaderProgram(const ShaderProgram &copy);
~ShaderProgram();
ShaderProgram& operator=(const ShaderProgram& other);
void use(); // Использование шейдеров
void load(GLuint type, const char* filename); // Функция для загрузки шейдеров
void link(); // Формирование программы из загруженных шейдеров
GLuint getUniformLoc(const char* name); // Возвращает местоположение uniform-переменной
void bindUniformBlock(const char* name, int binding); // Привязка uniform-блока
void bindTextures(const char* textures_base_shader_names[], int count); // Инициализация текстур на шейдере
private:
GLuint program; // Дескриптор
static std::map<int, int> handler_count; // Получение количества использований по дескриптору шейдера (Shared pointer)
std::map<const char*, GLuint> uniformLocations; // Местоположения uniform-переменных
};
#endif // SHADER_H

33
include/Texture.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include <glad/glad.h>
#include <map>
#include <string>
enum TexType {
TEX_DIFFUSE,
TEX_AVAILABLE_COUNT
};
class Texture
{
public:
Texture(GLuint type = TEX_AVAILABLE_COUNT, const std::string& filename = ""); // Загрузка текстуры с диска или использование "пустой"
Texture(const Texture& other); // Конструктор копирования
~Texture();
Texture& operator=(const Texture& other); // Оператор присваивания
void use(); // Привязка текстуры
static void disable(GLuint type); // Отвязка текстуры по типу
GLuint getType(); // Возвращает тип текстуры
private:
GLuint handler; // Дескриптор текстуры
GLuint type; // Тип текстуры, соответствует её слоту
static std::map<std::string, int> filename_handler; // Получение дескриптора текстуры по её имени
static std::map<int, int> handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
};
#endif // TEXTURE_H

13
shaders/shader.frag Normal file
View File

@ -0,0 +1,13 @@
#version 330 core
in vec2 texCoord;
uniform sampler2D tex_diffuse;
out vec4 color;
void main()
{
color = texture(tex_diffuse, texCoord);
}

21
shaders/shader.vert Normal file
View File

@ -0,0 +1,21 @@
#version 420 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec2 inTexCoord;
layout(location = 2) in vec3 normals;
layout(std140, binding = 0) uniform Camera
{
mat4 projection;
mat4 view;
};
uniform mat4 model;
out vec2 texCoord;
void main()
{
gl_Position = projection * view * model * vec4(pos, 1.0);
texCoord = inTexCoord;
}

144
src/Buffers.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "Buffers.h"
// Счетчики использований дескрипторов
std::map<GLuint, GLuint> VAO::handler_count;
std::map<GLuint, GLuint> BO::handler_count;
// Создает VAO и активирует его
VAO::VAO()
{
glGenVertexArrays(1, &handler); // Генерация одного объекта массива вершин
glBindVertexArray(handler); // Привязка для использования
handler_count[handler] = 1; // Инициализация счетчика для дескриптора
}
// Уничтожает VAO
VAO::~VAO()
{
// Если дескриптор никем не используется - освободим его
if (!--handler_count[handler])
{
glDeleteVertexArrays(1, &handler);
handler_count.erase(handler); // Удаление из словаря
}
}
// Конструктор копирования
VAO::VAO(const VAO & copy) : handler(copy.handler)
{
handler_count[handler]++;
}
// Оператор присваивания
VAO& VAO::operator=(const VAO & other)
{
// Если это разные дескрипторы
if (handler != other.handler)
{ // то следуюет удалить текущий перед заменой
this->~VAO();
handler = other.handler;
handler_count[handler]++;
}
return *this;
}
// Активация VAO
void VAO::use()
{
glBindVertexArray(handler); // Привязка VAO для использования
}
// Деактивация активного VAO
void VAO::disable()
{
glBindVertexArray(0); // Отключение VAO
}
// Создает пустой буфер заданного типа
BO::BO(BUFFER_TYPE t) : type(t)
{
glGenBuffers(1, &handler); // Генерация одного объекта буфера
handler_count[handler] = 1;
use(); // Привязка буфера
}
// Создает и загружает туда данные
BO::BO(BUFFER_TYPE t, const void *data, int size) : BO(t)
{
load(data, size);
}
// Уничтожает буфер
BO::~BO()
{
if (handler) // Если буфер был создан
{
// Если дескриптор никем не используется - освободим его
if (!--handler_count[handler])
{
glDeleteBuffers(1, &handler);
handler_count.erase(handler); // Удаление из словаря
}
handler = 0;
}
}
// Конструктор копирования
BO::BO(const BO & copy) : handler(copy.handler), type(copy.type)
{
handler_count[handler]++;
}
// Оператор присваивания
BO& BO::operator=(const BO & other)
{
// Если это разные дескрипторы
if (handler != other.handler)
{ // то следуюет удалить текущий перед заменой
this->~BO();
handler = other.handler;
handler_count[handler]++;
}
// Изменим тип
type = other.type;
return *this;
}
// Загрузка вершин в буфер
void BO::load(const void *data, int size, GLuint mode)
{
use(); // Привязка буфера
glBufferData(type, size, data, mode);
}
void BO::use()
{
glBindBuffer(type, handler); // Привязка элементного буфера
}
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
UBO::UBO(int size, int binding) : BO(UNIFORM, 0, size)
{
rebind(binding);
}
// Создает пустой uniform-буфер заданного размера с автоматической привязкой
UBO::UBO(const void *data, int size, int binding) : BO(UNIFORM, data, size)
{
rebind(binding);
}
// перепривязка
void UBO::rebind(int binding)
{
glBindBufferBase(type, binding, handler);
}
// Загрузка с отступом
void UBO::loadSub(const void *data, int size, int offset)
{
use();
glBufferSubData(type, offset, size, data);
}

141
src/Camera.cpp Normal file
View File

@ -0,0 +1,141 @@
#include "Camera.h"
#include <GLM/gtc/matrix_transform.hpp>
#include <GLM/ext/matrix_transform.hpp>
// Защищенный (protected) конструктор камеры без перспективы
Camera::Camera(const glm::vec3 &pos, const glm::vec2 &xyOffset)
: position(pos), currentRotation(xyOffset)
{
sensitivity = 0.05;
recalcTarget();
}
// Конструктор камеры с проекцией перспективы
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec2 &xyOffset, float fovy)
: Camera(position, xyOffset)
{
setPerspective(fovy, aspect);
}
// Конструктор ортографической камеры
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec2 &xyOffset)
: Camera(position, xyOffset)
{
setOrtho(width, height);
}
// Деструктор
Camera::~Camera() { }
// Пересчет цели, на которую смотрит камера
void Camera::recalcTarget()
{
if(currentRotation.y > 89.0f)
currentRotation.y = 89.0f;
if(currentRotation.y < -89.0f)
currentRotation.y = -89.0f;
target.x = cos(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
target.y = sin(glm::radians(currentRotation.y));
target.z = sin(glm::radians(currentRotation.x)) * cos(glm::radians(currentRotation.y));
requiredRecalcView = true;
requiredRecalcVP = true;
}
// Пересчет матрицы вида
void Camera::recalcView()
{
view = glm::lookAt(position, position + target, CAMERA_UP_VECTOR);
requiredRecalcView = false;
}
// Пересчет произведения матриц
void Camera::recalcVP()
{
vp = projection * view;
requiredRecalcVP = false;
}
// Возвращает ссылку на константную матрицу проекции
const glm::mat4& Camera::getProjection()
{
return projection;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getView()
{
if (requiredRecalcView)
recalcView();
return view;
}
// Возвращает ссылку на константную матрицу вида
const glm::mat4& Camera::getVP()
{
if (requiredRecalcVP)
{
if (requiredRecalcView)
recalcView();
recalcVP();
}
return vp;
}
// Поворачивает камеру на dx и dy пикселей
void Camera::rotate(const glm::vec2 &xyOffset)
{
currentRotation += xyOffset * sensitivity;
recalcTarget();
requiredRecalcView = true;
requiredRecalcVP = true;
}
// Сдвигает камеру на указанный вектор (dx,dy,dz)
void Camera::move(const glm::vec3 &posOffset)
{
position += posOffset;
requiredRecalcView = true;
requiredRecalcVP = true;
}
// Устанавливает местоположение
void Camera::setPosition(const glm::vec3 &pos)
{
position = pos;
requiredRecalcView = true;
requiredRecalcVP = true;
}
// Устанавливает угол поворота камеры
void Camera::setRotation(const glm::vec2 &xyOffset)
{
currentRotation = xyOffset;
recalcTarget();
}
// Устанавливает заданную матрицу перспективы
void Camera::setPerspective(float fovy, float aspect)
{
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = true;
}
// Устанавливает заданную ортографическую матрицу
void Camera::setOrtho(float width, float height)
{
const float aspect = width / height;
projection = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, CAMERA_NEAR, CAMERA_FAR);
requiredRecalcVP = true;
}
// Изменяет чувствительность мыши
void Camera::setSensitivity(float sens)
{
sensitivity = sens;
}

314
src/Model.cpp Normal file
View File

@ -0,0 +1,314 @@
#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)
{
}
Model::~Model()
{
}
// Вызов отрисовки
void Model::render(const GLuint &model_uniform, const glm::mat4& global_tranform)
{
// Расчитаем матрицу трансформации
glm::mat4 model = global_tranform * this->getTransformMatrix();
glUniformMatrix4fv(model_uniform, 1, GL_FALSE, &model[0][0]);
// Подключаем текстуры
texture_diffuse.use();
// Подключаем 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);
}
return result;
}
// Вызов отрисовки групповой модели
void GrouptedModel::render(const GLuint &model_uniform)
{
glm::mat4 transform = this->getTransformMatrix();
for (auto& model : parts)
model.render(model_uniform, transform);
}

154
src/Shader.cpp Normal file
View File

@ -0,0 +1,154 @@
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <sstream>
std::map<int, int> ShaderProgram::handler_count; // Получение количества использований по дескриптору ШП (Shared pointer)
ShaderProgram::ShaderProgram()
{
program = glCreateProgram();
handler_count[program] = 1;
}
ShaderProgram::ShaderProgram(const ShaderProgram &copy) : program(copy.program)
{
handler_count[program]++;
}
ShaderProgram::~ShaderProgram()
{
if (!--handler_count[program]) // Если количество ссылок = 0
{
// Удаление шейдерной программы
glDeleteProgram(program);
}
}
// Оператор присваивания
ShaderProgram& ShaderProgram::operator=(const ShaderProgram& other)
{
// Если это разные шейдерные программы
if (program != other.program)
{
this->~ShaderProgram(); // Уничтожаем имеющуюся
// Заменяем новой
program = other.program;
handler_count[program]++;
}
return *this;
}
// Использование шейдеров
void ShaderProgram::use()
{
glUseProgram(program);
}
// Функция чтения шейдера из файла
std::string readFile(const char* filename)
{
std::string text;
std::ifstream file(filename, std::ios::in); // Открываем файл на чтение
// Если файл доступен и успешно открыт
if (file.is_open())
{
std::stringstream sstr; // Буфер для чтения
sstr << file.rdbuf(); // Считываем файл
text = sstr.str(); // Преобразуем буфер к строке
file.close(); // Закрываем файл
}
return text;
}
// Функция для загрузки шейдеров
void ShaderProgram::load(GLuint type, const char* filename)
{
// Создание дескрипторов шейдера
GLuint handler = glCreateShader(type);
// Переменные под результат компиляции
GLint result = GL_FALSE;
int infoLogLength;
// Считываем текст вершинного шейдера
std::string code = readFile(filename);
const char* pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк
// Компиляция кода вершинного шейдера
glShaderSource(handler, 1, &pointer, NULL);
glCompileShader(handler);
// Проверка результата компиляции
glGetShaderiv(handler, GL_COMPILE_STATUS, &result);
glGetShaderiv(handler, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0)
{
char* errorMessage = new char[infoLogLength + 1];
glGetShaderInfoLog(handler, infoLogLength, NULL, errorMessage);
std::cout << errorMessage;
delete errorMessage;
}
// Привязка скомпилированного шейдера
glAttachShader(program, handler);
// Освобождение дескриптора шейдера
glDeleteShader(handler);
}
// Формирование программы из загруженных шейдеров
void ShaderProgram::link()
{
// Переменные под результат компиляции
GLint result = GL_FALSE;
int infoLogLength;
// Формирование программы из привязанных шейдеров
glLinkProgram(program);
// Проверка программы
glGetProgramiv(program, GL_LINK_STATUS, &result);
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0)
{
char* errorMessage = new char[infoLogLength + 1];
glGetProgramInfoLog(program, infoLogLength, NULL, errorMessage);
std::cout << errorMessage;
delete errorMessage;
}
// Используем шейдерную программу объекта из которого вызван метод
this->use();
}
// Возвращает местоположение uniform-переменной
GLuint ShaderProgram::getUniformLoc(const char* name)
{
GLuint result; // Результат
// Если такую переменную ещё не искали - найдем, иначе вернем уже известный дескриптор
if (!uniformLocations.count(name))
uniformLocations[name] = result = glGetUniformLocation(program, name);
else
result = uniformLocations[name];
return result;
}
// Привязка uniform-блока
void ShaderProgram::bindUniformBlock(const char* name, int binding)
{
glUniformBlockBinding( program
, glGetUniformBlockIndex(program, name)
, binding);
}
// Инициализация текстур на шейдере
void ShaderProgram::bindTextures(const char* textures_base_shader_names[], int count)
{
// Цикл по всем доступным текстурам
for (int i = 0; i < count; i++)
glUniform1i(getUniformLoc(textures_base_shader_names[i]), i);
}

119
src/Texture.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "Texture.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
std::map<std::string, int> Texture::filename_handler; // Получение дескриптора текстуры по её имени
std::map<int, int> Texture::handler_count; // Получение количества использований по дескриптору текстуры (Shared pointer)
// Загрузка текстуры с диска или использование "пустой"
Texture::Texture(GLuint t, const std::string& filename) : type(t)
{
if (!filename_handler.count(filename))
{
std::string empty = "";
int width, height, channels; // Ширина, высота и цветовые каналы текстуры
unsigned char* image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения
// Если изображение успешно счиитано с диска или отсутствует пустая текстура
if (image || !filename_handler.count(empty))
{
glActiveTexture(type + GL_TEXTURE0);
glGenTextures(1, &handler); // Генерация одной текстуры
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
filename_handler[filename] = handler; // Запоминим её дескриптор для этого имени файла
handler_count[handler] = 0; // Создадим счетчик использований дескриптора, который будет изменен в конце
// Если изображение успешно считано
if (image)
{
// Загрузка данных с учетом прозрачности
if (channels == 3) // RGB
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
else if (channels == 4) // RGBA
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D); // Генерация мипмапа для активной текстуры
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка активной текстуры
stbi_image_free(image); // Освобождение оперативной памяти
}
// Иначе изображение не считано и надо создать пустую текстуру
else
{
image = new unsigned char[3] {255,255,255}; // RGB по 1 байту на
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Загрузка данных на видеокарту
delete image; // Освобождение оперативной памяти
filename_handler[empty] = handler; // Запоминим дополнительно её дескриптор для NULL-строки
}
}
// Иначе используем существующую пустую текстуру (текстура не загружена, пустую создавать не нужно)
else
handler = filename_handler[empty];
}
// Иначе используем уже существующую по имени файла
else
handler = filename_handler[filename];
handler_count[handler]++;
}
// Конструктор копирования
Texture::Texture(const Texture& other) : handler(other.handler), type(other.type)
{
// Делаем копию и увеличиваем счетчик
handler_count[handler]++;
}
// Оператор присваивания
Texture& Texture::operator=(const Texture& other)
{
// Если это разные текстуры
if (handler != other.handler)
{
this->~Texture(); // Уничтожаем имеющуюся
// Заменяем новой
handler = other.handler;
handler_count[handler]++;
}
type = other.type;
return *this;
}
Texture::~Texture()
{
if (!--handler_count[handler]) // Если количество ссылок = 0
{
glDeleteTextures(1, &handler); // Удаление текстуры
// Удаление из словаря имен файлов и дескрипторов
for (auto it = filename_handler.begin(); it != filename_handler.end();)
{
if (it->second == handler)
it = filename_handler.erase(it);
else
it++;
}
}
}
// Привязка текстуры
void Texture::use()
{
glActiveTexture(type + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
}
// Отвязка текстуры по типу
void Texture::disable(GLuint type)
{
glActiveTexture(type + GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка текстуры
}
// Возвращает тип текстуры
GLuint Texture::getType()
{
return type;
}

138
src/main.cpp Normal file
View File

@ -0,0 +1,138 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLM/glm.hpp>
#include <iostream>
#include "Camera.h"
#include "Model.h"
#include "Texture.h"
#include "Shader.h"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
Camera camera(800.0f/600.0f);
bool firstMouse = true;
float lastX, lastY;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
glm::vec2 offset(xpos - lastX, lastY - ypos);
lastX = xpos;
lastY = ypos;
camera.rotate(offset);
}
int main(void)
{
GLFWwindow* window; // Указатель на окно GLFW3
// Инициализация GLFW3
if (!glfwInit())
{
std::cout << "GLFW init error\n";
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Мажорная версия спецификаций OpenGL
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Минорная версия спецификаций OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции
// Создание окна GLFW3 с заданными шириной, высотой и заголовком окна
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL);
if (!window)
{
std::cout << "GLFW create window error\n";
glfwTerminate(); // Завершение работы с GLFW3 в случае ошибки
return -1;
}
// Установка основного контекста окна
glfwMakeContextCurrent(window);
// Установка callback-функции для изменения размеров окна и буфера кадра
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSwapInterval(1); // Вертикальная синхронизация
// Установка callback-функции для мыши и камеры
glfwSetCursorPosCallback(window, mouse_callback);
// Загрузка функций OpenGL с помощью GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "GLAD load GL error\n";
glfwTerminate(); // Завершение работы с GLFW3 в случае ошибки
return -1;
}
// Включаем проверку по буферу глубины
glEnable(GL_DEPTH_TEST);
// Базовый шейдер
ShaderProgram base;
// Загрузка и компиляция шейдеров
base.load(GL_VERTEX_SHADER, "shaders/shader.vert");
base.load(GL_FRAGMENT_SHADER, "shaders/shader.frag");
base.link();
// Установим значения текстур
const char* textures_base_shader_names[] = {"tex_diffuse"};
base.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*));
// camera.move({0,0,-20});
// Загрузка сцены из obj файла
GrouptedModel scene = loadOBJtoGroupted("../resources/models/cubes.obj", "../resources/models/", "../resources/textures/");
// Установка цвета очистки буфера цвета
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Расположение Uniform-переменной
GLuint model_uniform = base.getUniformLoc("model");
// Uniform-буферы
UBO cameraUB(sizeof(glm::mat4)*2, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Использование уменьшенных версий mipmap
// Пока не произойдет событие запроса закрытия окна
while(!glfwWindowShouldClose(window))
{
// Очистка буфера цвета
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Загрузка данных о камере
cameraUB.loadSub(&camera.getProjection(), sizeof(glm::mat4), 0);
cameraUB.loadSub(&camera.getView(), sizeof(glm::mat4), sizeof(glm::mat4));
// Тут производится рендер
scene.render(model_uniform);
// Представление содержимого буфера цепочки показа на окно
glfwSwapBuffers(window);
// Обработка системных событий
glfwPollEvents();
}
// Отключение атрибутов
glDisableVertexAttribArray(0);
// Завершение работы с GLFW3 перед выходом
glfwTerminate();
}