Копия с 18
This commit is contained in:
203
src/Buffers.cpp
Normal file
203
src/Buffers.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
// Создает буфер кадра с нужным числом прикреплений текстур
|
||||
FBO::FBO(GLuint *attachments, int count)
|
||||
{
|
||||
glGenFramebuffers(1, &handler);
|
||||
use();
|
||||
glDrawBuffers(count, attachments);
|
||||
}
|
||||
|
||||
// Уничтожение буфера
|
||||
FBO::~FBO()
|
||||
{
|
||||
glDeleteFramebuffers(1, &handler);
|
||||
}
|
||||
|
||||
// Активирует буфер кадра в заданном режиме
|
||||
void FBO::use(GLuint mode)
|
||||
{
|
||||
glBindFramebuffer(mode, handler);
|
||||
}
|
||||
|
||||
// Активирует базовый буфер в заданном режиме
|
||||
void FBO::useDefault(GLuint mode)
|
||||
{
|
||||
glBindFramebuffer(mode, 0);
|
||||
}
|
||||
|
||||
// Привязка рендер буфера
|
||||
void FBO::assignRenderBuffer(GLuint hander, GLuint attachment)
|
||||
{
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, hander);
|
||||
}
|
||||
|
||||
// Создает буфер рендера с заданными параметрами размеров и используемых компонент
|
||||
RBO::RBO(int w, int h, GLuint component)
|
||||
{
|
||||
glGenRenderbuffers(1, &handler);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, handler);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
|
||||
}
|
||||
|
||||
// Уничтожение буфера
|
||||
RBO::~RBO()
|
||||
{
|
||||
glDeleteRenderbuffers(1, &handler);
|
||||
}
|
||||
|
||||
// Изменяет размеры буфера рендера
|
||||
void RBO::reallocate(int w, int h, GLuint component)
|
||||
{
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, handler); // Привязка элементного буфера
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
|
||||
}
|
||||
|
||||
// Возвращает дескриптор буфера рендера
|
||||
GLuint RBO::getHandler()
|
||||
{
|
||||
return handler;
|
||||
}
|
||||
239
src/Camera.cpp
Normal file
239
src/Camera.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "Camera.h"
|
||||
|
||||
// Указатель на текущую используемую камеру
|
||||
Camera* Camera::p_current = NULL;
|
||||
|
||||
// Границы каскадов
|
||||
const float camera_cascade_distances[] = {CAMERA_NEAR, CAMERA_FAR / 50.0f, CAMERA_FAR / 10.0f, CAMERA_FAR / 3.0f, CAMERA_FAR};
|
||||
|
||||
// Защищенный (protected) конструктор камеры без перспективы
|
||||
Camera::Camera(const glm::vec3 &pos, const glm::vec3 &initialRotation) : Node(NULL) // Пусть по умолчанию камера не относится к сцене
|
||||
{
|
||||
sensitivity = CAMERA_DEFAULT_SENSIVITY;
|
||||
position = pos; // задаем позицию
|
||||
// Определяем начальный поворот
|
||||
glm::quat rotationAroundX = glm::angleAxis( glm::radians(initialRotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::quat rotationAroundY = glm::angleAxis(-glm::radians(initialRotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::quat rotationAroundZ = glm::angleAxis( glm::radians(initialRotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
rotation = rotationAroundX * rotationAroundY * rotationAroundZ;
|
||||
// Признак изменения
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Конструктор камеры с проекцией перспективы
|
||||
Camera::Camera(float aspect, const glm::vec3 &position, const glm::vec3 &initialRotation, float fovy)
|
||||
: Camera(position, initialRotation)
|
||||
{
|
||||
setPerspective(fovy, aspect);
|
||||
}
|
||||
|
||||
// Конструктор ортографической камеры
|
||||
Camera::Camera(float width, float height, const glm::vec3 &position, const glm::vec3 &initialRotation)
|
||||
: Camera(position, initialRotation)
|
||||
{
|
||||
setOrtho(width, height);
|
||||
}
|
||||
|
||||
// Конструктор копирования камеры
|
||||
Camera::Camera(const Camera& copy)
|
||||
: Node(copy), projection(copy.projection), requiredRecalcVP(copy.requiredRecalcVP), sensitivity(copy.sensitivity),
|
||||
requiredRecalcCoords(true)
|
||||
{
|
||||
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
|
||||
if (!requiredRecalcVP)
|
||||
vp = copy.vp;
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
Camera& Camera::operator=(const Camera& other)
|
||||
{
|
||||
Node::operator=(other); // Вызов родительского оператора= для переноса узла
|
||||
|
||||
projection = other.projection;
|
||||
requiredRecalcVP = other.requiredRecalcVP;
|
||||
sensitivity = other.sensitivity;
|
||||
|
||||
// Если у оригинала не было изменений - перепишем матрицу вида-проекции
|
||||
if (!requiredRecalcVP)
|
||||
vp = other.vp;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Деструктор
|
||||
Camera::~Camera()
|
||||
{
|
||||
if (p_current == this)
|
||||
p_current = NULL;
|
||||
}
|
||||
|
||||
// Возвращает ссылку на константную матрицу проекции
|
||||
const glm::mat4& Camera::getProjection()
|
||||
{
|
||||
return projection;
|
||||
}
|
||||
|
||||
// Возвращает ссылку на константную матрицу вида
|
||||
const glm::mat4& Camera::getView()
|
||||
{
|
||||
recalcMatrices();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
// Возвращает ссылку на константную матрицу вида
|
||||
const glm::mat4& Camera::getVP()
|
||||
{
|
||||
recalcMatrices();
|
||||
|
||||
return vp;
|
||||
}
|
||||
|
||||
// Устанавливает заданную матрицу перспективы
|
||||
void Camera::setPerspective(float fovy, float aspect)
|
||||
{
|
||||
projection = glm::perspective(glm::radians(fovy), aspect, CAMERA_NEAR, CAMERA_FAR);
|
||||
requiredRecalcVP = true;
|
||||
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||
cascade_proj[cascade] = glm::perspective(glm::radians(fovy), aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
|
||||
}
|
||||
|
||||
// Устанавливает заданную ортографическую матрицу
|
||||
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;
|
||||
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||
cascade_proj[cascade] = glm::ortho(-1.0f, 1.0f, -1.0f/aspect, 1.0f/aspect, camera_cascade_distances[cascade], camera_cascade_distances[cascade+1]);
|
||||
|
||||
}
|
||||
|
||||
// Изменяет чувствительность мыши
|
||||
void Camera::setSensitivity(float sens)
|
||||
{
|
||||
sensitivity = sens;
|
||||
}
|
||||
|
||||
// Возвращает чувствительность мыши
|
||||
const float& Camera::getSensitivity() const
|
||||
{
|
||||
return sensitivity;
|
||||
}
|
||||
|
||||
// Метод пересчета матрицы вида и произведения Вида*Проекции по необходимости, должен сбрасывать флаг changed
|
||||
void Camera::recalcMatrices()
|
||||
{
|
||||
if (changed || parent_changed)
|
||||
{
|
||||
glm::vec3 _position = position;
|
||||
glm::quat _rotation = rotation;
|
||||
if (parent) // Если есть родитель
|
||||
{
|
||||
glm::mat4 normalized_transform = parent->getTransformMatrix();
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
glm::vec3 axis = glm::vec3(normalized_transform[i]);
|
||||
normalized_transform[i] = glm::vec4(glm::normalize(axis), normalized_transform[i].w);
|
||||
}
|
||||
glm::vec4 tmp = normalized_transform * glm::vec4(_position, 1.0f);
|
||||
tmp /= tmp.w;
|
||||
_position = glm::vec3(tmp);
|
||||
_rotation = glm::quat_cast(normalized_transform) * _rotation;
|
||||
}
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(glm::conjugate(_rotation));
|
||||
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), -_position);
|
||||
view = rotationMatrix * translationMatrix;
|
||||
requiredRecalcVP = true;
|
||||
}
|
||||
|
||||
Node::recalcMatrices();
|
||||
|
||||
if (requiredRecalcVP)
|
||||
{
|
||||
vp = projection * view;
|
||||
requiredRecalcCoords = true; // Требуется пересчитать точки пространства камеры
|
||||
requiredRecalcVP = false; // Изменения применены
|
||||
}
|
||||
}
|
||||
|
||||
// Поворачивает камеру на dx и dy пикселей с учетом чувствительности
|
||||
void Camera::rotate(const glm::vec2 &xyOffset)
|
||||
{
|
||||
// xyOffset - сдвиги координат мыши, xyOffset.x означает поворот вокруг оси Y, а xyOffset.y - поворот вокруг оси X
|
||||
|
||||
// Вращение вокруг оси Y
|
||||
glm::quat qY = glm::angleAxis(-xyOffset.x * sensitivity, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
// Вращение вокруг оси X
|
||||
glm::quat qX = glm::angleAxis(xyOffset.y * sensitivity, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
|
||||
// Сначала применяем вращение вокруг Y, затем вокруг X
|
||||
rotation = qY * rotation * qX;
|
||||
|
||||
changed = true;
|
||||
invalidateParent(); // Проход потомков в глубину с изменением флага parent_changed
|
||||
}
|
||||
|
||||
// Использование этой камеры как текущей
|
||||
void Camera::use()
|
||||
{
|
||||
p_current = this;
|
||||
}
|
||||
|
||||
// Ссылка на текущую используемую камеру
|
||||
Camera& Camera::current()
|
||||
{
|
||||
static Camera default_cam(800.0f/600.0f);
|
||||
|
||||
if (!p_current)
|
||||
return default_cam;
|
||||
else
|
||||
return *p_current;
|
||||
}
|
||||
|
||||
// Данные о камере для шейдера
|
||||
CameraData& Camera::getData()
|
||||
{
|
||||
static CameraData data;
|
||||
data = {getProjection(), getView(), position};
|
||||
return data;
|
||||
}
|
||||
|
||||
// Доступ к координатам с флагом изменения, описывающим пространство вида с пересчетом, если это требуется
|
||||
std::pair<bool, const glm::vec4(*)[8]> Camera::getProjCoords()
|
||||
{
|
||||
const glm::mat4& cam_vp = getVP(); // Получение ссылки на матрицу вида-проекции с пересчетом, если требуется и активацией флага requiredRecalcCoords
|
||||
bool changes = false; // Возвращаемое значение
|
||||
|
||||
if (requiredRecalcCoords)
|
||||
{
|
||||
// Инверсия матрицы вида/проекции камеры
|
||||
glm::mat4 inv = glm::inverse(cam_vp);
|
||||
// Типовые точки, описывающие пространство
|
||||
glm::vec4 typical_points[8] = { { 1, 1, 1,1}
|
||||
, { 1, 1,-1,1}
|
||||
, { 1,-1, 1,1}
|
||||
, { 1,-1,-1,1}
|
||||
, {-1, 1, 1,1}
|
||||
, {-1, 1,-1,1}
|
||||
, {-1,-1, 1,1}
|
||||
, {-1,-1,-1,1}};
|
||||
|
||||
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||
{
|
||||
glm::mat4 inv = glm::inverse(cascade_proj[cascade] * getView());
|
||||
// Цикл по типовым точкам
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
coords[cascade][i] = inv * typical_points[i];
|
||||
coords[cascade][i] /= coords[cascade][i].w;
|
||||
}
|
||||
}
|
||||
|
||||
requiredRecalcCoords = false; // Сбрасываем флаг
|
||||
changes = true;
|
||||
}
|
||||
|
||||
return std::make_pair(changes, coords);
|
||||
}
|
||||
379
src/Lights.cpp
Normal file
379
src/Lights.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
#include "Lights.h"
|
||||
|
||||
#include "Scene.h" // Для отладочного вывода лампочек
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
GLuint Light::count = 0; // количество используемых источников (должно быть <= MAX_LIGHTS)
|
||||
LightData Light::data[MAX_LIGHTS]; // Массив данных по источникам света
|
||||
Light Light::lights[MAX_LIGHTS]; // Массив источников-узлов сцены
|
||||
|
||||
Sun Sun::instance; // Экземпляр синглтона
|
||||
bool Sun::uploadReq = true; // Необходимость загрузки в следствии изменений
|
||||
|
||||
// возвращает размер буфера в байтах
|
||||
int Light::getUBOsize()
|
||||
{
|
||||
return sizeof(LightData) * MAX_LIGHTS + sizeof(GLuint);
|
||||
}
|
||||
|
||||
// Загрузка данных в буфер
|
||||
void Light::upload(UBO& lights_data)
|
||||
{
|
||||
GLuint LightDataSize = sizeof(LightData); // Одного экземпляра структуры LightData
|
||||
int first = MAX_LIGHTS, last = -1; // Начало и конец диапазона загрузки источников
|
||||
static GLuint prev_count = -1; // Кол-во источников в прошлую посылку
|
||||
|
||||
if (count)
|
||||
{
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
{
|
||||
lights[i].recalcMatrices(); // Пересчитаем матрицы по необходимости (проверка внутри метода)
|
||||
|
||||
// Если требуется загрузка
|
||||
if (lights[i].uploadReq)
|
||||
{
|
||||
lights[i].toData(); // Перевод ноды в данные для шейдера
|
||||
|
||||
// Определение диапазона загрузки
|
||||
if (first > lights[i].index)
|
||||
first = lights[i].index;
|
||||
if (last < lights[i].index)
|
||||
last = lights[i].index;
|
||||
|
||||
lights[i].uploadReq = false; // Сброс флага
|
||||
}
|
||||
}
|
||||
|
||||
// Если есть что загрузить (определен диапазон)
|
||||
if (last > -1)
|
||||
lights_data.loadSub(data + first, LightDataSize*(last - first +1), LightDataSize*(first)); // Загрузка данных об источниках
|
||||
}
|
||||
|
||||
// Если кол-во изменилось
|
||||
if (prev_count != count)
|
||||
{
|
||||
prev_count = count;
|
||||
|
||||
// Загружаем кол-во источников
|
||||
lights_data.loadSub(&count, sizeof(count), LightDataSize*MAX_LIGHTS);
|
||||
}
|
||||
}
|
||||
|
||||
// Метод пересчета матрицы трансформации по необходимости, должен сбрасывать флаг changed
|
||||
void Light::recalcMatrices()
|
||||
{
|
||||
// Если были изменения - необходимо загрузить данные
|
||||
if (changed || parent_changed)
|
||||
uploadReq = true;
|
||||
|
||||
// Выполняем вычисление матриц методом родительского класса
|
||||
Node::recalcMatrices();
|
||||
}
|
||||
|
||||
// Константный доступ к цвету
|
||||
const glm::vec3& Light::c_color() const
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
// Неконстантная ссылка для изменений цвета
|
||||
glm::vec3& Light::e_color()
|
||||
{
|
||||
uploadReq = true;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
// Проверка что не взаимодествуем с пустым источником
|
||||
void Light::check_id()
|
||||
{
|
||||
if (index < 0
|
||||
|| index >= count)
|
||||
throw std::runtime_error("Попытка использовать ссылку на пустой или некорректный источник");
|
||||
}
|
||||
|
||||
// Преобразует информацию об источнике в структуру LightData
|
||||
void Light::toData()
|
||||
{
|
||||
check_id(); // Проверка на работу с корректным индексом
|
||||
|
||||
// Если позиция изменилась
|
||||
if (data[index].position.x != result_transform[3].x
|
||||
|| data[index].position.y != result_transform[3].y
|
||||
|| data[index].position.z != result_transform[3].z
|
||||
)
|
||||
{
|
||||
data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации
|
||||
recalcVP(); // Пересчет матрицы вида-проекции для расчета теней
|
||||
}
|
||||
data[index].color = color; // Цвет
|
||||
// Если радиус изменился
|
||||
if (data[index].attenuation.r != radius)
|
||||
{
|
||||
data[index].attenuation.r = radius; // Радиус действия источника
|
||||
data[index].attenuation[1] = 4.5/radius; // Линейный коэф. угасания
|
||||
data[index].attenuation[2] = 4 * data[index].attenuation[1] * data[index].attenuation[1]; // Квадратичный коэф. угасания
|
||||
}
|
||||
// Направление и угол источника
|
||||
data[index].direction_angle = glm::vec4( glm::normalize(glm::vec3(result_transform * DEFAULT_LIGHT_DIRECTION))
|
||||
, angle / 2 // Половинный угол для вычислений на шейдере
|
||||
);
|
||||
}
|
||||
|
||||
// Возвращает ссылку на новый источник света
|
||||
Light& Light::getNew()
|
||||
{
|
||||
Light& refNew = findByIndex(-1);
|
||||
|
||||
refNew.index = count++;
|
||||
refNew.uploadReq = true;
|
||||
|
||||
return refNew;
|
||||
}
|
||||
|
||||
// Уничтожает источник света
|
||||
void Light::destroy()
|
||||
{
|
||||
check_id(); // Проверка на работу с корректным индексом
|
||||
// Если удаляемый элемент не последний
|
||||
if (count-1 != index)
|
||||
{
|
||||
// Найдем элемент для замены
|
||||
Light& replace = findByIndex(--count);
|
||||
|
||||
replace.uploadReq = true; // Требуется загрузить данные
|
||||
replace.index = index; // Заменяем индекс данных
|
||||
}
|
||||
|
||||
operator=(Light()); // Обнулим источник путем замены на новый
|
||||
}
|
||||
|
||||
// Возвращает ссылку на источник с нужным индексом
|
||||
Light& Light::findByIndex(GLuint index)
|
||||
{
|
||||
// Если нет источников - возвращаем нулевой
|
||||
if (!count)
|
||||
return lights[0];
|
||||
|
||||
// Цикл по перебору источников
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
if (lights[i].index == index)
|
||||
return lights[i];
|
||||
|
||||
throw std::runtime_error("Запрашиваемый источник освещения не найден, либо достигнут лимит");
|
||||
}
|
||||
|
||||
// Конструктор без параметров
|
||||
Light::Light() : Node(), index(-1), uploadReq(false), color(1.0f), radius(10.0f), angle(360.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
Light& Light::operator=(const Light& other)
|
||||
{
|
||||
// Проверка на самоприсваивание
|
||||
if (this != &other)
|
||||
{
|
||||
index = other.index; // Переносим индекс
|
||||
uploadReq = other.uploadReq; // Необходимость загрузки
|
||||
color = other.color;
|
||||
radius = other.radius;
|
||||
angle = other.angle;
|
||||
|
||||
Node::operator=(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Light::~Light()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Рисование отладочных лампочек
|
||||
void Light::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||
{
|
||||
// Загрузка модели лампочки при первом вызове функции
|
||||
static Scene bulb = loadOBJtoScene("../resources/models/bulb.obj", "../resources/models/", "../resources/textures/");
|
||||
static Model sphere = genShpere(1, 16, &bulb.root);
|
||||
|
||||
GLuint angle_uniform = shaderProgram.getUniformLoc("angle");
|
||||
GLuint direction_uniform = shaderProgram.getUniformLoc("direction");
|
||||
|
||||
// Цикл по источникам света
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Идентификатор источника как узла сцены для всей модели лампочки
|
||||
bulb.set_group_id((GLuint64) &lights[i]);
|
||||
sphere.id.value = (GLuint64) &lights[i];
|
||||
|
||||
// Загрузим направление
|
||||
glUniform3fv(direction_uniform, 1, &data[i].direction_angle.x);
|
||||
// Угол для лампочки = 180 (рисуем целую модель)
|
||||
glUniform1f(angle_uniform, 180); // Зададим параметры материала сфере действия
|
||||
|
||||
// Сдвиг на позицию источника
|
||||
bulb.root.e_position() = data[i].position;
|
||||
sphere.e_scale() = glm::vec3(data[i].attenuation.r); // Масштабирование сферы
|
||||
// Задание цвета
|
||||
bulb.models.begin()->material.base_color = sphere.material.base_color = data[i].color;
|
||||
|
||||
// Вызов отрисовки
|
||||
bulb.render(shaderProgram, material_buffer);
|
||||
|
||||
// Угол для сферы (рисуем направленный конус)
|
||||
glUniform1f(angle_uniform, data[i].direction_angle.a);
|
||||
|
||||
// Рисование сферы покрытия источника в режиме линий
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
sphere.render(shaderProgram, material_buffer);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
}
|
||||
|
||||
// Константный доступ к радиусу
|
||||
const float& Light::c_radius() const
|
||||
{
|
||||
return radius;
|
||||
}
|
||||
|
||||
// Неконстантная ссылка для изменений радиуса
|
||||
float& Light::e_radius()
|
||||
{
|
||||
uploadReq = true;
|
||||
|
||||
return radius;
|
||||
}
|
||||
|
||||
// Константный доступ к углу освещенности
|
||||
const float& Light::c_angle() const
|
||||
{
|
||||
return angle;
|
||||
}
|
||||
|
||||
// Неконстантная ссылка для изменений угла освещенности
|
||||
float& Light::e_angle()
|
||||
{
|
||||
uploadReq = true;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
// Конструктор направленного источника с параметрами направления и цвета
|
||||
Sun::Sun(const glm::vec3 &dir, const glm::vec3 &c) : direction(dir), color(c)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Доступ к синглтону
|
||||
Sun& Sun::get()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Загрузка данных об источнике на шейдер
|
||||
void Sun::upload(UBO& sun_data)
|
||||
{
|
||||
instance.recalcVP(); // Пересчет матрицы вида-проекции источника по необходимости (влияет на флаг uploadReq)
|
||||
|
||||
if (uploadReq)
|
||||
{
|
||||
sun_data.loadSub(&instance, sizeof(instance));
|
||||
|
||||
uploadReq = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Константный доступ к направлению лучей источника
|
||||
const glm::vec3& Sun::c_direction() const
|
||||
{
|
||||
return instance.direction;
|
||||
}
|
||||
|
||||
// Неконстантная ссылка для изменений направления лучей источника
|
||||
glm::vec3& Sun::e_direction()
|
||||
{
|
||||
uploadReq = true;
|
||||
|
||||
return instance.direction;
|
||||
}
|
||||
|
||||
// Константный доступ к цвету
|
||||
const glm::vec3& Sun::c_color() const
|
||||
{
|
||||
return instance.color;
|
||||
}
|
||||
|
||||
// Неконстантная ссылка для изменений цвета
|
||||
glm::vec3& Sun::e_color()
|
||||
{
|
||||
uploadReq = true;
|
||||
|
||||
return instance.color;
|
||||
}
|
||||
|
||||
// Пересчитывает по необходимости матрицу вида-проекции
|
||||
void Sun::recalcVP()
|
||||
{
|
||||
// Точки по краям проекции камеры
|
||||
std::pair <bool, const glm::vec4(*)[8]> camProjCoords = Camera::current().getProjCoords();
|
||||
|
||||
// Есть изменения по источнику или камере
|
||||
if (uploadReq || camProjCoords.first)
|
||||
{
|
||||
uploadReq = true; // Требуется загрузка в следствии пересчета матрицы
|
||||
|
||||
glm::vec3 mean; // Среднее арифметическое
|
||||
glm::vec4 max, min; // макс и мин координаты
|
||||
glm::vec4 point; // Точка приведенная в пространство источника света
|
||||
|
||||
|
||||
for (int cascade = 0; cascade < CAMERA_CASCADE_COUNT; cascade++)
|
||||
{
|
||||
mean = glm::vec3(0);
|
||||
// Найдем среднее арифметическое от точек для нахождения центра прямоугольника
|
||||
for (int i = 0; i < 8; i++)
|
||||
mean += glm::vec3(camProjCoords.second[cascade][i]);
|
||||
mean /= 8;
|
||||
// Используем среднее арифметическое для получения матрицы вида параллельного источника
|
||||
glm::mat4 lightView = glm::lookAt(mean + glm::normalize(direction), mean, CAMERA_UP_VECTOR);
|
||||
|
||||
// Примем первую точку как минимальную и максимальную (приведя в пространство вида источника)
|
||||
min = max = lightView * camProjCoords.second[cascade][0];
|
||||
// Для оставшихся точек
|
||||
for (int i = 1; i < 8; i++)
|
||||
{
|
||||
// Приведем в пространство вида источника
|
||||
point = lightView * camProjCoords.second[cascade][i];
|
||||
max = glm::max(max, point);
|
||||
min = glm::min(min, point);
|
||||
}
|
||||
|
||||
// Максимальное значение глубины
|
||||
max.z = std::max(fabs(max.z), fabs(min.z));
|
||||
// На основании максимальных и минимальных координат создадим матрицу проекции источника
|
||||
vp[cascade] = glm::ortho(min.x, max.x, min.y, max.y, min.z, max.z) * lightView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Пересчитывает по необходимости матрицу вида-проекции
|
||||
void Light::recalcVP()
|
||||
{
|
||||
float near_plane = 0.1f;
|
||||
glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), 1.0f, near_plane, radius);
|
||||
data[index].vp[0] = shadowProj * glm::lookAt(position, position + glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
data[index].vp[1] = shadowProj * glm::lookAt(position, position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
data[index].vp[2] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
data[index].vp[3] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f));
|
||||
data[index].vp[4] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
data[index].vp[5] = shadowProj * glm::lookAt(position, position + glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Возвращает количество источников
|
||||
int Light::getCount()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
613
src/Model.cpp
Normal file
613
src/Model.cpp
Normal file
@@ -0,0 +1,613 @@
|
||||
#include "Model.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// Конструктор с заданным родителем (по умолчанию 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*>& 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), normals_vbo(VERTEX), texCoords_vbo(VERTEX),
|
||||
tangent_vbo(VERTEX), bitangent_vbo(VERTEX)
|
||||
{
|
||||
// Приведение указателя к целому 8байт
|
||||
id.value = (GLuint64) this;
|
||||
id.etc = 0;
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
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), normals_vbo(copy.normals_vbo), texCoords_vbo(copy.texCoords_vbo),
|
||||
tangent_vbo(copy.tangent_vbo), bitangent_vbo(copy.bitangent_vbo),
|
||||
texture_albedo(copy.texture_albedo), texture_roughness(copy.texture_roughness), texture_metallic(copy.texture_metallic), texture_specular(copy.texture_specular), texture_emitted(copy.texture_emitted),
|
||||
texture_heights(copy.texture_heights), texture_normals(copy.texture_normals),
|
||||
material(copy.material)
|
||||
{
|
||||
// Приведение указателя к целому 8байт
|
||||
id.value = (GLuint64) this;
|
||||
id.etc = copy.id.etc;
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
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;
|
||||
texCoords_vbo = other.texCoords_vbo;
|
||||
|
||||
tangent_vbo = other.tangent_vbo;
|
||||
bitangent_vbo = other.bitangent_vbo;
|
||||
|
||||
texture_albedo = other.texture_albedo;
|
||||
texture_roughness = other.texture_roughness;
|
||||
texture_metallic = other.texture_metallic;
|
||||
texture_specular = other.texture_specular;
|
||||
texture_emitted = other.texture_emitted;
|
||||
|
||||
texture_heights = other.texture_heights;
|
||||
texture_normals = other.texture_normals;
|
||||
|
||||
material = other.material;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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_byteOffset));
|
||||
}
|
||||
// Если есть вершины - рисуем на основании массива вершин
|
||||
else if (verteces_count)
|
||||
glDrawArrays(GL_TRIANGLES, 0, verteces_count);
|
||||
}
|
||||
|
||||
// Вызов отрисовки
|
||||
void Model::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||
{
|
||||
// Загрузка идентификатора объекта
|
||||
glUniform3uiv(shaderProgram.getUniformLoc("ID"), 1, (GLuint*) &id);
|
||||
|
||||
// Расчитаем матрицу трансформации
|
||||
glUniformMatrix4fv(shaderProgram.getUniformLoc("model"), 1, GL_FALSE, &this->getTransformMatrix()[0][0]);
|
||||
|
||||
// Подключаем текстуры
|
||||
texture_albedo.use();
|
||||
texture_roughness.use();
|
||||
texture_metallic.use();
|
||||
texture_specular.use();
|
||||
texture_emitted.use();
|
||||
texture_heights.use();
|
||||
texture_normals.use();
|
||||
|
||||
// Загружаем данные о материале
|
||||
material_buffer.load(&material, sizeof(material));
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
// Функция для конфигурации атрибута вершинного буфера
|
||||
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 texCoords_attrib_config()
|
||||
{
|
||||
// Определим спецификацию атрибута
|
||||
glVertexAttribPointer( 1 // индекс атрибута, должен совпадать с Layout шейдера
|
||||
, 2 // количество компонент одного элемента
|
||||
, GL_FLOAT // тип
|
||||
, GL_FALSE // необходимость нормировать значения
|
||||
, 0 // шаг
|
||||
, (void *)0 // отступ с начала массива
|
||||
);
|
||||
// Включаем необходимый атрибут у выбранного VAO
|
||||
glEnableVertexAttribArray(1);
|
||||
}
|
||||
|
||||
// Загрузка текстурных координат в буфер
|
||||
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 и привязанным VBO
|
||||
glVertexAttribPointer( 2 // индекс атрибута, должен совпадать с Layout шейдера
|
||||
, 3 // количество компонент одного элемента
|
||||
, GL_FLOAT // тип
|
||||
, GL_FALSE // необходимость нормировать значения
|
||||
, 0 // шаг
|
||||
, (void *)0 // отступ с начала массива
|
||||
);
|
||||
// Включаем необходимый атрибут у выбранного VAO
|
||||
glEnableVertexAttribArray(2);
|
||||
}
|
||||
|
||||
// Загрузка нормалей в буфер
|
||||
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();
|
||||
}
|
||||
|
||||
// Ограничение диапазона из буфера индексов
|
||||
void Model::set_index_range(size_t first_byteOffset, size_t count)
|
||||
{
|
||||
first_index_byteOffset = first_byteOffset;
|
||||
indices_count = count;
|
||||
}
|
||||
|
||||
// Привязка текстуры к модели
|
||||
void Model::set_texture(Texture& texture)
|
||||
{
|
||||
GLuint type = texture.getType();
|
||||
switch(type)
|
||||
{
|
||||
case TEX_ALBEDO:
|
||||
texture_albedo = texture;
|
||||
material.base_color.r = -1;
|
||||
break;
|
||||
case TEX_ROUGHNESS:
|
||||
texture_roughness = texture;
|
||||
material.roughness = -1;
|
||||
break;
|
||||
case TEX_METALLIC:
|
||||
texture_metallic = texture;
|
||||
material.metallic = -1;
|
||||
break;
|
||||
case TEX_SPECULAR:
|
||||
texture_specular = texture;
|
||||
material.specular = -1;
|
||||
break;
|
||||
case TEX_EMITTED:
|
||||
texture_emitted = texture;
|
||||
material.emitted.r = -1;
|
||||
break;
|
||||
case TEX_HEIGHTS:
|
||||
texture_heights = texture;
|
||||
break;
|
||||
case TEX_NORMAL:
|
||||
texture_normals = texture;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// Функция для конфигурации атрибута вершинного буфера
|
||||
void tangent_attrib_config()
|
||||
{
|
||||
// Определим спецификацию атрибута
|
||||
glVertexAttribPointer( 3 // индекс атрибута, должен совпадать с Layout шейдера
|
||||
, 3 // количество компонент одного элемента
|
||||
, GL_FLOAT // тип
|
||||
, GL_FALSE // необходимость нормировать значения
|
||||
, 0 // шаг
|
||||
, (void *)0 // отступ с начала массива
|
||||
);
|
||||
// Включаем необходимый атрибут у выбранного VAO
|
||||
glEnableVertexAttribArray(3);
|
||||
}
|
||||
|
||||
// Функция для конфигурации атрибута вершинного буфера
|
||||
void bitangent_attrib_config()
|
||||
{
|
||||
// Определим спецификацию атрибута
|
||||
glVertexAttribPointer( 4 // индекс атрибута, должен совпадать с Layout шейдера
|
||||
, 3 // количество компонент одного элемента
|
||||
, GL_FLOAT // тип
|
||||
, GL_FALSE // необходимость нормировать значения
|
||||
, 0 // шаг
|
||||
, (void *)0 // отступ с начала массива
|
||||
);
|
||||
// Включаем необходимый атрибут у выбранного VAO
|
||||
glEnableVertexAttribArray(4);
|
||||
}
|
||||
|
||||
// Загрузка касательных векторов в буфер
|
||||
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();
|
||||
}
|
||||
|
||||
// Генерирует сферу заданного радиуса с определенным количеством сегментов
|
||||
Model genShpere(float radius, int sectorsCount, Node* parent)
|
||||
{
|
||||
Model result(parent);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Загрузка в модель
|
||||
result.load_verteces(&vertices[0], vertices.size());
|
||||
result.load_normals(&normals[0], normals.size());
|
||||
result.load_indices(&indices[0], indices.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Расчет касательных и бикасательных векторов
|
||||
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;
|
||||
// Нормируем значение
|
||||
tmp = glm::normalize(tmp);
|
||||
// Добавим вектор в контейнер
|
||||
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;
|
||||
// Нормируем значение
|
||||
tmp = glm::normalize(tmp);
|
||||
// Добавим вектор в контейнер
|
||||
bitangent[indices[i ]] = tmp; // Для каждого индекса полигона
|
||||
bitangent[indices[i+1]] = tmp; // значение вектора
|
||||
bitangent[indices[i+2]] = tmp; // одинаковое
|
||||
}
|
||||
}
|
||||
265
src/Scene.cpp
Normal file
265
src/Scene.cpp
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "Scene.h"
|
||||
|
||||
// Конструктор пустой сцены
|
||||
Scene::Scene()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
Scene::Scene(const Scene ©): root(copy.root),
|
||||
nodes(copy.nodes), models(copy.models), cameras(copy.cameras)
|
||||
{
|
||||
rebuld_tree(copy);
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
Scene& Scene::operator=(const Scene& other)
|
||||
{
|
||||
root = other.root;
|
||||
nodes = other.nodes;
|
||||
models = other.models;
|
||||
cameras = other.cameras;
|
||||
|
||||
rebuld_tree(other);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Рендер сцены
|
||||
void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||
{
|
||||
for (auto & model : models)
|
||||
model.render(shaderProgram, material_buffer);
|
||||
}
|
||||
|
||||
// Перестройка узлов выбранного списка
|
||||
template <class T>
|
||||
void Scene::rebuild_Nodes_list(T& nodes, const Scene& from)
|
||||
{
|
||||
for (auto it = nodes.begin(); it != nodes.end(); it++)
|
||||
{
|
||||
// Берем родителя, который указывает на оригинальный объект
|
||||
Node* parent = it->getParent();
|
||||
|
||||
// Если родитель - оригинальный корневой узел, то меняем на собственный корневой узел
|
||||
if (parent == &from.root)
|
||||
{
|
||||
it->setParent(&root);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если можно привести к модели, то ищем родителя среди моделей
|
||||
if (dynamic_cast<Model*>(parent))
|
||||
move_parent(*it, from.models, this->models);
|
||||
else
|
||||
// Иначе проверяем на принадлежность к камерам
|
||||
if (dynamic_cast<Camera*>(parent))
|
||||
move_parent(*it, from.cameras, this->cameras);
|
||||
// Иначе это пустой узел
|
||||
else
|
||||
move_parent(*it, from.nodes, this->nodes);
|
||||
|
||||
// Не нашли родителя - значит он не часть этой сцены
|
||||
// и изменений по нему не требуется
|
||||
}
|
||||
}
|
||||
|
||||
// Сдвигает родителя узла между двумя списками при условии его принадлежности к оригинальному
|
||||
template <class T>
|
||||
void Scene::move_parent(Node& for_node, const std::list<T>& from_nodes, std::list<T>& this_nodes)
|
||||
{
|
||||
// Возьмем адрес родителя
|
||||
Node* parent = for_node.getParent();
|
||||
// Цикл по элементам списков для перемещения родителя
|
||||
// Списки в процессе копирования идеинтичные, вторая проверка не требуется
|
||||
for (auto it_from = from_nodes.begin(), it_this = this_nodes.begin(); it_from != from_nodes.end(); ++it_from, ++it_this)
|
||||
// Если адрес объекта, на который указывает итератор, совпадает с родителем - меняем родителя по второму итератору (it_this)
|
||||
if (&(*it_from) == parent)
|
||||
for_node.setParent(&(*it_this));
|
||||
}
|
||||
|
||||
// Перестройка дерева после копирования или присваивания
|
||||
void Scene::rebuld_tree(const Scene& from)
|
||||
{
|
||||
// Восстановим родителей в пустых узлах для копии
|
||||
rebuild_Nodes_list(nodes, from);
|
||||
rebuild_Nodes_list(models, from);
|
||||
rebuild_Nodes_list(cameras, from);
|
||||
}
|
||||
|
||||
#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...);
|
||||
}
|
||||
|
||||
Scene loadOBJtoScene(const char* filename, const char* mtl_directory, const char* texture_directory)
|
||||
{
|
||||
Scene result;
|
||||
Model model;
|
||||
// Все модели образованные на основании этой модели будут иметь общего родителя
|
||||
model.setParent(&result.root);
|
||||
|
||||
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.models.push_back(model); // Создание копии с общим VAO
|
||||
auto s = --result.models.end();
|
||||
s->set_index_range(materials_range[i]*sizeof(GLuint), materials_range[i+1]-materials_range[i]);
|
||||
|
||||
// Материал
|
||||
s->material.base_color = 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.roughness = 1 - sqrt(materials[materials_ids[i]].shininess/1000); // шероховатость поверхности
|
||||
s->material.metallic = (materials[materials_ids[i]].ambient[0] + materials[materials_ids[i]].ambient[1] + materials[materials_ids[i]].ambient[2]) / 3.0f;
|
||||
s->material.specular = (materials[materials_ids[i]].specular[0] + materials[materials_ids[i]].specular[1] + materials[materials_ids[i]].specular[2]) / 3.0f;
|
||||
s->material.emitted = pow(glm::vec3(materials[materials_ids[i]].emission[0], materials[materials_ids[i]].emission[1], materials[materials_ids[i]].emission[2]), glm::vec3(1/inv_gamma));
|
||||
|
||||
// Текстуры
|
||||
if (!materials[materials_ids[i]].diffuse_texname.empty())
|
||||
{
|
||||
Texture diffuse(TEX_ALBEDO, texture_directory + materials[materials_ids[i]].diffuse_texname);
|
||||
s->set_texture(diffuse);
|
||||
}
|
||||
if (!materials[materials_ids[i]].ambient_texname.empty())
|
||||
{
|
||||
Texture ambient(TEX_METALLIC, texture_directory + materials[materials_ids[i]].ambient_texname);
|
||||
s->set_texture(ambient);
|
||||
}
|
||||
if (!materials[materials_ids[i]].specular_texname.empty())
|
||||
{
|
||||
Texture specular(TEX_SPECULAR, texture_directory + materials[materials_ids[i]].specular_texname);
|
||||
s->set_texture(specular);
|
||||
}
|
||||
if (!materials[materials_ids[i]].normal_texname.empty())
|
||||
{
|
||||
Texture normal(TEX_NORMAL, texture_directory + materials[materials_ids[i]].normal_texname);
|
||||
s->set_texture(normal);
|
||||
}
|
||||
if (!materials[materials_ids[i]].bump_texname.empty())
|
||||
{
|
||||
Texture heights(TEX_HEIGHTS, texture_directory + materials[materials_ids[i]].bump_texname);
|
||||
s->set_texture(heights);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Изменение флага записи идентификатора для всех моделей
|
||||
void Scene::set_group_id(GLuint64 id, GLuint etc)
|
||||
{
|
||||
for (auto& model : models)
|
||||
{
|
||||
model.id.value = id;
|
||||
if (etc)
|
||||
model.id.etc = etc;
|
||||
}
|
||||
}
|
||||
154
src/Shader.cpp
Normal file
154
src/Shader.cpp
Normal 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 ©) : 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);
|
||||
}
|
||||
187
src/TRS.cpp
Normal file
187
src/TRS.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "TRS.h"
|
||||
#include <GLM/gtx/matrix_decompose.hpp>
|
||||
|
||||
// Инициализирует дополнительную информацию модели
|
||||
void TRS::init_etc()
|
||||
{
|
||||
int value = 1;
|
||||
for (auto it = tool.models.begin(); it != tool.models.end(); ++it)
|
||||
it->id.etc = value++;
|
||||
}
|
||||
|
||||
// Рендер инструмента нужного типа для выбранного объекта
|
||||
void TRS::render(GLuint64 selectedID, ShaderProgram &shaderProgram, UBO &material_buffer)
|
||||
{
|
||||
// Если есть выбранная модель - рендерим инструмент для неё
|
||||
if (selectedID)
|
||||
{
|
||||
// Указатель на объект
|
||||
Node* selectedObject = (Node*) selectedID;
|
||||
|
||||
// Смещение выбранного объекта в глобальных координатах из его матрицы трансформации (включая родительские)
|
||||
tool.root.e_position() = glm::vec3(selectedObject->getTransformMatrix()[3]);
|
||||
|
||||
// Замена идентификатора инструмента идентификатором выбранного объекта
|
||||
tool.set_group_id(selectedID); // без замены доп. информации
|
||||
|
||||
// Рендер инструмента
|
||||
tool.render(shaderProgram, material_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Transform::Transform()
|
||||
{
|
||||
tool = loadOBJtoScene("../resources/models/tools/transform.obj", "../resources/models/tools/", "../resources/textures/");
|
||||
|
||||
// Масштабирование
|
||||
tool.root.e_scale() = glm::vec3(0.3);
|
||||
|
||||
// Инициализация дополнительной информации
|
||||
init_etc();
|
||||
}
|
||||
|
||||
// Взаимодействие с инструментом
|
||||
void Transform::process(GLuint64 selectedID, GLuint etc, const glm::vec4& dpos)
|
||||
{
|
||||
// Если взаимодействие с осями инструмента
|
||||
if (etc > 0)
|
||||
// Если есть выбранная модель - рендерим инструмент для неё
|
||||
if (selectedID)
|
||||
{
|
||||
// Указатель на объект
|
||||
Node* selectedObject = (Node*) selectedID;
|
||||
|
||||
glm::vec3 dVec(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Сдвиг с учетом чувствительности для соответствующих осей
|
||||
if (etc & 01)
|
||||
dVec.x = dpos.x * T_SENSITIVITY;
|
||||
if (etc & 02)
|
||||
dVec.y = dpos.y * T_SENSITIVITY;
|
||||
if (etc & 04)
|
||||
dVec.z = dpos.z * T_SENSITIVITY;
|
||||
|
||||
// Если есть родитель - требуется учесть его поворот
|
||||
Node* parent = selectedObject->getParent();
|
||||
if (parent)
|
||||
{
|
||||
// Извлекаем 3x3 подматрицу, отвечающую за вращение и масштаб
|
||||
glm::mat3 rotationMatrix = glm::mat3(parent->getTransformMatrix());
|
||||
|
||||
// Нормализуем столбцы подматрицы, чтобы исключить масштабирование
|
||||
for (int i = 0; i < 3; i++)
|
||||
rotationMatrix[i] = glm::normalize(rotationMatrix[i]);
|
||||
|
||||
// Применим поворот родителя к вектору сдвига
|
||||
dVec = glm::inverse(rotationMatrix) * dVec;
|
||||
}
|
||||
|
||||
// Добавим сдвиг от инструмента к позиции выбранного объекта
|
||||
selectedObject->e_position() += dVec;
|
||||
}
|
||||
}
|
||||
|
||||
Rotate::Rotate()
|
||||
{
|
||||
tool = loadOBJtoScene("../resources/models/tools/rotate.obj", "../resources/models/tools/", "../resources/textures/");
|
||||
|
||||
// Масштабирование
|
||||
tool.root.e_scale() = glm::vec3(0.3);
|
||||
|
||||
int value = 1;
|
||||
for (auto it = tool.models.begin(); it != tool.models.end(); ++it)
|
||||
{
|
||||
it->id.etc = value;
|
||||
value *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Взаимодействие с инструментом
|
||||
void Rotate::process(GLuint64 selectedID, GLuint etc, const glm::vec4& drot)
|
||||
{
|
||||
// Если взаимодействие с осями инструмента
|
||||
if (etc > 0)
|
||||
// Если есть выбранная модель - рендерим инструмент для неё
|
||||
if (selectedID)
|
||||
{
|
||||
// Указатель на объект
|
||||
Node* selectedObject = (Node*) selectedID;
|
||||
glm::quat &selectedRot = selectedObject->e_rotation();
|
||||
|
||||
// Матрица родительского поворота
|
||||
glm::mat3 parentRotation(1);
|
||||
|
||||
// Учет родительского поворота для вращения
|
||||
Node* parent = selectedObject->getParent();
|
||||
if (parent)
|
||||
{
|
||||
// Извлекаем 3x3 подматрицу, отвечающую за вращение и масштаб
|
||||
parentRotation = glm::mat3(parent->getTransformMatrix());
|
||||
|
||||
// Нормализуем столбцы подматрицы, чтобы исключить масштабирование
|
||||
for (int i = 0; i < 3; i++)
|
||||
parentRotation[i] = glm::normalize(parentRotation[i]);
|
||||
}
|
||||
|
||||
// Поворот по осям
|
||||
if (etc & 01)
|
||||
selectedRot = glm::angleAxis(drot.y * R_SENSITIVITY, parentRotation * glm::vec3(1.0f, 0.0f, 0.0f)) * selectedRot;
|
||||
if (etc & 02)
|
||||
selectedRot = glm::angleAxis(drot.x * R_SENSITIVITY, parentRotation * glm::vec3(0.0f, 1.0f, 0.0f)) * selectedRot;
|
||||
if (etc & 04)
|
||||
selectedRot = glm::angleAxis(drot.z * R_SENSITIVITY, parentRotation * glm::vec3(0.0f, 0.0f, 1.0f)) * selectedRot;
|
||||
}
|
||||
}
|
||||
|
||||
Scale::Scale()
|
||||
{
|
||||
tool = loadOBJtoScene("../resources/models/tools/scale.obj", "../resources/models/tools/", "../resources/textures/");
|
||||
|
||||
// Масштабирование
|
||||
tool.root.e_scale() = glm::vec3(0.3);
|
||||
|
||||
// Инициализация дополнительной информации
|
||||
init_etc();
|
||||
}
|
||||
|
||||
// Взаимодействие с инструментом
|
||||
void Scale::process(GLuint64 selectedID, GLuint etc, const glm::vec4& dscale)
|
||||
{
|
||||
// Если взаимодействие с осями инструмента
|
||||
if (etc > 0)
|
||||
// Если есть выбранная модель - рендерим инструмент для неё
|
||||
if (selectedID)
|
||||
{
|
||||
// Указатель на объект
|
||||
Node* selectedObject = (Node*) selectedID;
|
||||
|
||||
// Для хранения результата
|
||||
glm::vec3 dVec(0);
|
||||
|
||||
// Масштабирование с учетом чувствительности для соответствующих осей
|
||||
if (etc & 01)
|
||||
dVec.x = dscale.x * S_SENSITIVITY;
|
||||
if (etc & 02)
|
||||
dVec.y = dscale.y * S_SENSITIVITY;
|
||||
if (etc & 04)
|
||||
dVec.z = dscale.z * S_SENSITIVITY;
|
||||
|
||||
// Если есть родитель - требуется учесть его поворот
|
||||
Node* parent = selectedObject->getParent();
|
||||
if (parent)
|
||||
{
|
||||
// Извлекаем 3x3 подматрицу, отвечающую за вращение и масштаб
|
||||
glm::mat3 rotationMatrix = glm::mat3(parent->getTransformMatrix());
|
||||
|
||||
// Нормализуем столбцы подматрицы, чтобы исключить масштабирование
|
||||
for (int i = 0; i < 3; i++)
|
||||
rotationMatrix[i] = glm::normalize(rotationMatrix[i]);
|
||||
|
||||
// Применим поворот родителя к вектору сдвига
|
||||
dVec = glm::inverse(rotationMatrix) * dVec;
|
||||
}
|
||||
|
||||
// Прибавим вектор масштабирования к объекту
|
||||
selectedObject->e_scale() += dVec;
|
||||
}
|
||||
}
|
||||
452
src/Texture.cpp
Normal file
452
src/Texture.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
#include "Texture.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
std::map<std::string, int> BaseTexture::filename_handler; // Получение дескриптора текстуры по её имени
|
||||
std::map<int, int> BaseTexture::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)
|
||||
{
|
||||
// Выбор формата с учетом типа текстуры (нормали не sRGB) и числа каналов
|
||||
GLuint internalformat = GL_RGB, format = GL_RGB;
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
internalformat = format = GL_RED;
|
||||
break;
|
||||
case 2:
|
||||
internalformat = format = GL_RG;
|
||||
break;
|
||||
case 3:
|
||||
format = GL_RGB;
|
||||
if (type == TEX_NORMAL || type == TEX_HEIGHTS)
|
||||
internalformat = GL_RGB;
|
||||
else
|
||||
internalformat = GL_SRGB;
|
||||
break;
|
||||
case 4:
|
||||
format = GL_RGBA;
|
||||
if (type == TEX_NORMAL || type == TEX_HEIGHTS)
|
||||
internalformat = GL_RGBA;
|
||||
else
|
||||
internalformat = GL_SRGB_ALPHA;
|
||||
break;
|
||||
|
||||
}
|
||||
// Загрузка данных с учетом формата
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, 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(GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
type = texType;
|
||||
// Генерация текстуры заданного размера
|
||||
glGenTextures(1, &handler);
|
||||
glBindTexture(GL_TEXTURE_2D, handler);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// Привязка к буферу кадра
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, handler, 0);
|
||||
|
||||
// Создаем счетчик использований дескриптора
|
||||
handler_count[handler] = 1;
|
||||
}
|
||||
|
||||
// Конструктор текстуры заданного размера без привязки к буферу с загрузкой пикселей по указателю
|
||||
Texture::Texture(GLuint width, GLuint height, void* data, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
type = texType;
|
||||
// Генерация текстуры заданного размера
|
||||
glGenTextures(1, &handler);
|
||||
glBindTexture(GL_TEXTURE_2D, handler);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// Создаем счетчик использований дескриптора
|
||||
handler_count[handler] = 1;
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
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;
|
||||
}
|
||||
|
||||
BaseTexture::~BaseTexture()
|
||||
{
|
||||
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::reallocate(GLuint width, GLuint height, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
use();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, NULL);
|
||||
}
|
||||
|
||||
// Привязка текстуры
|
||||
void Texture::use()
|
||||
{
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, handler); // Привязка текстуры как активной
|
||||
}
|
||||
|
||||
// Отвязка текстуры по типу
|
||||
void BaseTexture::disable(GLuint type)
|
||||
{
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // Отвязка текстуры
|
||||
}
|
||||
|
||||
// Возвращает тип текстуры
|
||||
GLuint BaseTexture::getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
// Задает тип текстуры
|
||||
void BaseTexture::setType(GLuint type)
|
||||
{
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
// Конструктор текстуры заданного размера для использования в буфере
|
||||
TextureArray::TextureArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
type = texType;
|
||||
// Генерация текстуры заданного размера
|
||||
glGenTextures(1, &handler);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handler);
|
||||
glTexImage3D(
|
||||
GL_TEXTURE_2D_ARRAY, 0, internalformat, width, height, levels, 0, format, dataType, 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// Привязка к буферу кадра
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||
|
||||
// Создаем счетчик использований дескриптора
|
||||
handler_count[handler] = 1;
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
TextureArray::TextureArray(const TextureArray& other)
|
||||
{
|
||||
handler = other.handler;
|
||||
type = other.type;
|
||||
// Делаем копию и увеличиваем счетчик
|
||||
handler_count[handler]++;
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
TextureArray& TextureArray::operator=(const TextureArray& other)
|
||||
{
|
||||
// Если это разные текстуры
|
||||
if (handler != other.handler)
|
||||
{
|
||||
this->~TextureArray(); // Уничтожаем имеющуюся
|
||||
// Заменяем новой
|
||||
handler = other.handler;
|
||||
handler_count[handler]++;
|
||||
}
|
||||
type = other.type;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Пересоздает текстуру для имеющегося дескриптора
|
||||
void TextureArray::reallocate(GLuint levels, GLuint width, GLuint height, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
use();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, dataType, NULL);
|
||||
}
|
||||
|
||||
// Привязка текстуры
|
||||
void TextureArray::use()
|
||||
{
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, handler); // Привязка текстуры как активной
|
||||
}
|
||||
|
||||
// Загрузка текстуры с диска или использование "пустой"
|
||||
TextureCube::TextureCube(GLuint t, const std::string (&filename)[6])
|
||||
{
|
||||
type = t;
|
||||
std::string complex_name;
|
||||
for (int i = 0; i < 6; i++)
|
||||
complex_name += filename[i];
|
||||
if (!filename_handler.count(complex_name))
|
||||
{
|
||||
std::string empty = "";
|
||||
int width, height, channels; // Ширина, высота и цветовые каналы текстуры
|
||||
unsigned char* image;
|
||||
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glGenTextures(1, &handler); // Генерация одной текстуры
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, handler); // Привязка текстуры как активной
|
||||
|
||||
filename_handler[complex_name] = handler; // Запомним её дескриптор для этого имени файла
|
||||
handler_count[handler] = 0; // Создадим счетчик использований дескриптора, который будет изменен в конце
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
image = stbi_load(filename[i].c_str(), &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения
|
||||
|
||||
// Если изображение успешно считано
|
||||
if (image)
|
||||
{
|
||||
// Выбор формата с учетом типа текстуры (нормали не sRGB) и числа каналов
|
||||
GLuint internalformat = GL_RGB, format = GL_RGB;
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
internalformat = format = GL_RED;
|
||||
break;
|
||||
case 2:
|
||||
internalformat = format = GL_RG;
|
||||
break;
|
||||
case 3:
|
||||
format = GL_RGB;
|
||||
if (type == TEX_NORMAL || type == TEX_HEIGHTS)
|
||||
internalformat = GL_RGB;
|
||||
else
|
||||
internalformat = GL_SRGB;
|
||||
break;
|
||||
case 4:
|
||||
format = GL_RGBA;
|
||||
if (type == TEX_NORMAL || type == TEX_HEIGHTS)
|
||||
internalformat = GL_RGBA;
|
||||
else
|
||||
internalformat = GL_SRGB_ALPHA;
|
||||
break;
|
||||
|
||||
}
|
||||
// Загрузка данных с учетом формата
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalformat, width, height, 0, format, GL_UNSIGNED_BYTE, image);
|
||||
|
||||
stbi_image_free(image); // Освобождение оперативной памяти
|
||||
}
|
||||
// Иначе изображение не считано и надо создать пустую текстуру
|
||||
else
|
||||
{
|
||||
image = new unsigned char[3] {255,255,255}; // RGB по 1 байту на
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Загрузка данных на видеокарту
|
||||
delete[] image; // Освобождение оперативной памяти
|
||||
}
|
||||
}
|
||||
}
|
||||
// Иначе используем уже существующую по имени файла
|
||||
else
|
||||
handler = filename_handler[complex_name];
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
|
||||
handler_count[handler]++;
|
||||
}
|
||||
|
||||
// Конструктор текстуры заданного размера для использования в буфере
|
||||
TextureCube::TextureCube(GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
type = texType;
|
||||
// Генерация текстуры заданного размера
|
||||
glGenTextures(1, &handler);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, handler);
|
||||
for (int i = 0; i < 6; ++i)
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalformat, width, height, 0, format, dataType, 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// Привязка к буферу кадра
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||
|
||||
// Создаем счетчик использований дескриптора
|
||||
handler_count[handler] = 1;
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
TextureCube::TextureCube(const TextureCube& other)
|
||||
{
|
||||
handler = other.handler;
|
||||
type = other.type;
|
||||
// Делаем копию и увеличиваем счетчик
|
||||
handler_count[handler]++;
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
TextureCube& TextureCube::operator=(const TextureCube& other)
|
||||
{
|
||||
// Если это разные текстуры
|
||||
if (handler != other.handler)
|
||||
{
|
||||
this->~TextureCube(); // Уничтожаем имеющуюся
|
||||
// Заменяем новой
|
||||
handler = other.handler;
|
||||
handler_count[handler]++;
|
||||
}
|
||||
type = other.type;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Пересоздает текстуру для имеющегося дескриптора
|
||||
void TextureCube::reallocate(GLuint width, GLuint height, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
use();
|
||||
for (int i = 0; i < 6; ++i)
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalformat, width, height, 0, format, dataType, 0);
|
||||
}
|
||||
|
||||
// Привязка текстуры
|
||||
void TextureCube::use()
|
||||
{
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, handler); // Привязка текстуры как активной
|
||||
}
|
||||
|
||||
// Конструктор текстуры заданного размера для использования в буфере
|
||||
TextureCubeArray::TextureCubeArray(GLuint levels, GLuint width, GLuint height, GLuint attachment, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
type = texType;
|
||||
// Генерация текстуры заданного размера
|
||||
glGenTextures(1, &handler);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, handler);
|
||||
glTexImage3D(
|
||||
GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, width, height, 6*levels, 0, format, dataType, 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
// Привязка к буферу кадра
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attachment, handler, 0);
|
||||
|
||||
// Создаем счетчик использований дескриптора
|
||||
handler_count[handler] = 1;
|
||||
}
|
||||
|
||||
// Конструктор копирования
|
||||
TextureCubeArray::TextureCubeArray(const TextureCubeArray& other)
|
||||
{
|
||||
handler = other.handler;
|
||||
type = other.type;
|
||||
// Делаем копию и увеличиваем счетчик
|
||||
handler_count[handler]++;
|
||||
}
|
||||
|
||||
// Оператор присваивания
|
||||
TextureCubeArray& TextureCubeArray::operator=(const TextureCubeArray& other)
|
||||
{
|
||||
// Если это разные текстуры
|
||||
if (handler != other.handler)
|
||||
{
|
||||
this->~TextureCubeArray(); // Уничтожаем имеющуюся
|
||||
// Заменяем новой
|
||||
handler = other.handler;
|
||||
handler_count[handler]++;
|
||||
}
|
||||
type = other.type;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Пересоздает текстуру для имеющегося дескриптора
|
||||
void TextureCubeArray::reallocate(GLuint levels, GLuint width, GLuint height, GLuint texType, GLint internalformat, GLint format, GLenum dataType)
|
||||
{
|
||||
use();
|
||||
glTexImage3D(
|
||||
GL_TEXTURE_CUBE_MAP_ARRAY, 0, internalformat, width, height, 6*levels, 0, format, dataType, 0);
|
||||
}
|
||||
|
||||
// Привязка текстуры
|
||||
void TextureCubeArray::use()
|
||||
{
|
||||
glActiveTexture(type + GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, handler); // Привязка текстуры как активной
|
||||
}
|
||||
618
src/main.cpp
Executable file
618
src/main.cpp
Executable file
@@ -0,0 +1,618 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLM/glm.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include "Scene.h"
|
||||
#include "Shader.h"
|
||||
#include "Lights.h"
|
||||
#include "TRS.h"
|
||||
|
||||
#define WINDOW_CAPTION "OPENGL notes on rekovalev.site"
|
||||
|
||||
// Указатели на текстуры для изменения размеров окна
|
||||
Texture* pgPosition = NULL;
|
||||
Texture* pgNormal = NULL;
|
||||
Texture* pgBaseColor = NULL;
|
||||
Texture* pgRMS = NULL;
|
||||
Texture* pgEmittedColor = NULL;
|
||||
Texture* pgID = NULL;
|
||||
RBO* pgrbo = NULL;
|
||||
Texture* pssaoTexture = NULL;
|
||||
Texture* pssaoTexture_raw = NULL;
|
||||
// Размеры окна
|
||||
int WINDOW_WIDTH = 800;
|
||||
int WINDOW_HEIGHT = 600;
|
||||
|
||||
// Значение гамма-коррекции
|
||||
float inv_gamma = 1/2.2;
|
||||
|
||||
// Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
// Изменение размеров текстур для G-буфера
|
||||
if (pgPosition)
|
||||
pgPosition->reallocate(width, height, 0, GL_RGB32F, GL_RGB);
|
||||
if (pgNormal)
|
||||
pgNormal->reallocate(width, height, 1, GL_RGB16F, GL_RGB);
|
||||
if (pgBaseColor)
|
||||
pgBaseColor->reallocate(width, height, 2, GL_RGB);
|
||||
if (pgRMS)
|
||||
pgRMS->reallocate(width, height, 3, GL_RGB, GL_RGB);
|
||||
if (pgEmittedColor)
|
||||
pgEmittedColor->reallocate(width, height, 8, GL_RGB, GL_RGB);
|
||||
if (pgID)
|
||||
pgID->reallocate(width, height, 7, GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT);
|
||||
// И буфера глубины
|
||||
if (pgrbo)
|
||||
pgrbo->reallocate(width, height);
|
||||
// SSAO
|
||||
if (pssaoTexture)
|
||||
pssaoTexture->reallocate(width, height, 6, GL_RED, GL_RED);
|
||||
if (pssaoTexture_raw)
|
||||
pssaoTexture_raw->reallocate(width, height, 0, GL_RED, GL_RED);
|
||||
|
||||
// Запомним новые размеры окна
|
||||
WINDOW_WIDTH = width;
|
||||
WINDOW_HEIGHT = height;
|
||||
|
||||
// Изменим параметры перспективной матрицы проекции для камеры
|
||||
Camera::current().setPerspective(CAMERA_FOVy, (float)width/height);
|
||||
}
|
||||
|
||||
// Данные о мыши
|
||||
struct Mouse
|
||||
{
|
||||
float x = 0, y = 0; // Координаты курсора
|
||||
float prev_x = 0, prev_y = 0; // Координаты курсора на предыдущем кадре
|
||||
uint16_t left = 040100, right = 040100; // Состояние кнопок
|
||||
} mouse;
|
||||
|
||||
void process_mouse_button(uint16_t& button)
|
||||
{
|
||||
if ((++button & 037777) == 037777)
|
||||
button &= 0140100;
|
||||
}
|
||||
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
mouse.x = xpos;
|
||||
mouse.y = ypos;
|
||||
}
|
||||
|
||||
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
|
||||
{
|
||||
uint16_t& mouse_button = (button == GLFW_MOUSE_BUTTON_LEFT)?mouse.left:mouse.right;
|
||||
|
||||
if (action == GLFW_PRESS && !(mouse_button & 0100000))
|
||||
mouse_button = 0100000;
|
||||
else if (action == GLFW_RELEASE)
|
||||
mouse_button = 040000;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
GLFWwindow* window; // Указатель на окно GLFW3
|
||||
|
||||
// Инициализация GLFW3
|
||||
if (!glfwInit())
|
||||
{
|
||||
std::cout << "GLFW init error\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Завершение работы с GLFW3 перед выходом
|
||||
atexit(glfwTerminate);
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций 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";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Установка основного контекста окна
|
||||
glfwMakeContextCurrent(window);
|
||||
// Установка callback-функции для изменения размеров окна и буфера кадра
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
|
||||
glfwSwapInterval(1); // Вертикальная синхронизация
|
||||
|
||||
// Установка callback-функции для мыши и камеры
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
glfwSetMouseButtonCallback(window, mouse_button_callback);
|
||||
|
||||
// Загрузка функций OpenGL с помощью GLAD
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "GLAD load GL error\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Включаем проверку по буферу глубины
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Шейдер для G-буфера
|
||||
ShaderProgram gShader;
|
||||
// Загрузка и компиляция шейдеров
|
||||
gShader.load(GL_VERTEX_SHADER, "shaders/gshader.vert");
|
||||
gShader.load(GL_FRAGMENT_SHADER, "shaders/gshader.frag");
|
||||
gShader.link();
|
||||
// Установим значения текстур
|
||||
const char* textures_base_shader_names[] = {"tex_albedo", "tex_roughness", "tex_metallic", "tex_specular", "tex_emitted", "tex_heights", "tex_normal"};
|
||||
gShader.bindTextures(textures_base_shader_names, sizeof(textures_base_shader_names)/sizeof(const char*));
|
||||
|
||||
// Загрузка сцены из obj файла
|
||||
Scene scene = loadOBJtoScene("../resources/models/blob.obj", "../resources/models/", "../resources/textures/");
|
||||
scene.root.e_scale() = glm::vec3(0.01);
|
||||
scene.root.e_position().z = 1;
|
||||
scene.set_group_id((GLuint64) &scene.root);
|
||||
|
||||
// Установка цвета очистки буфера цвета
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
// Сдвинем направленный источник света и камеру
|
||||
Sun::get().e_direction().z = -1.0;
|
||||
Camera::current().e_position().x = 0.3f;
|
||||
|
||||
// Источники света
|
||||
Light& first = Light::getNew();
|
||||
first.e_color() = {1.0f, 0.0f, 0.0f}; // цвет
|
||||
first.e_position() = {0.3f, 0.0f, 0.6f}; // Позиция
|
||||
first.e_angle() = 100.0f;
|
||||
Light& second = Light::getNew();
|
||||
second.e_color() = {0.0f, 0.0f, 1.0f}; // цвет
|
||||
second.e_position() = {-0.3f, 0.3f, 0.5f}; // Позиция
|
||||
|
||||
// Uniform-буферы
|
||||
UBO cameraUB(sizeof(CameraData), 0);
|
||||
UBO material_data(sizeof(Material), 1);
|
||||
UBO light_data(Light::getUBOsize(), 2);
|
||||
UBO sun_data(sizeof(Sun), 3);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Использование уменьшенных версий mipmap
|
||||
|
||||
// Создадим G-буфер с данными о используемых привязках
|
||||
GLuint attachments[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
|
||||
FBO gbuffer(attachments, sizeof(attachments) / sizeof(GLuint));
|
||||
// Создадим текстуры для буфера кадра
|
||||
Texture gPosition(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 0, GL_RGB32F, GL_RGB); // Позиция вершины
|
||||
Texture gNormal(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT1, 1, GL_RGB16F, GL_RGB); // Нормали
|
||||
Texture gBaseColor(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT2, 2, GL_RGB, GL_RGB); // Базовый цвет материала
|
||||
Texture gRMS(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT3, 3, GL_RGB, GL_RGB); // Шероховатость, металличность, интенсивность блика
|
||||
Texture gID(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT4, 7, GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT); // Идентификатор объекта
|
||||
Texture gEmittedColor(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT5, 8, GL_RGB, GL_RGB); // Излучаемый свет
|
||||
// Создадим буфер рендера под буфер глубины и привяжем его
|
||||
RBO grbo(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
gbuffer.assignRenderBuffer(grbo.getHandler());
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
|
||||
// Сохраним указатели на текстуры для изменения размеров окна
|
||||
pgPosition = &gPosition;
|
||||
pgNormal = &gNormal;
|
||||
pgBaseColor = &gBaseColor;
|
||||
pgRMS = &gRMS;
|
||||
pgrbo = &grbo;
|
||||
pgID = &gID;
|
||||
pgEmittedColor = &gEmittedColor;
|
||||
|
||||
// Шейдер для расчета освещенности
|
||||
ShaderProgram lightShader;
|
||||
// Загрузка и компиляция шейдеров
|
||||
lightShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
|
||||
lightShader.load(GL_FRAGMENT_SHADER, "shaders/lighting.frag");
|
||||
lightShader.link();
|
||||
// Привязка текстур
|
||||
const char* gtextures_shader_names[] = {"gPosition", "gNormal", "gBaseColor", "gRMS", "sunShadowDepth", "pointShadowDepth", "ssao", "gID", "gEmittedColor", "reflections"};
|
||||
lightShader.bindTextures(gtextures_shader_names, sizeof(gtextures_shader_names)/sizeof(const char*));
|
||||
// Загрузка данных о границах каскадов
|
||||
glUniform1fv(lightShader.getUniformLoc("camera_cascade_distances"), CAMERA_CASCADE_COUNT, &camera_cascade_distances[1]);
|
||||
|
||||
glm::vec3 quadVertices[] = { {-1.0f, 1.0f, 0.0f}
|
||||
, {-1.0f, -1.0f, 0.0f}
|
||||
, { 1.0f, 1.0f, 0.0f}
|
||||
, { 1.0f, -1.0f, 0.0f}
|
||||
};
|
||||
|
||||
GLuint quadIndices[] = {0,1,2,2,1,3};
|
||||
|
||||
Model quadModel;
|
||||
quadModel.load_verteces(quadVertices, 4);
|
||||
quadModel.load_indices(quadIndices, 6);
|
||||
|
||||
// Размер текстуры тени от солнца
|
||||
const GLuint sunShadow_resolution = 1024;
|
||||
// Создадим буфер кадра для рендера теней
|
||||
FBO sunShadowBuffer;
|
||||
// Создадим текстуры для буфера кадра
|
||||
TextureArray sunShadowDepth(CAMERA_CASCADE_COUNT, sunShadow_resolution, sunShadow_resolution, GL_DEPTH_ATTACHMENT, 4, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
|
||||
// Правка фантомных теней
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
float shadowBorderColor[] = { 1.0, 1.0, 1.0, 1.0 };
|
||||
glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, shadowBorderColor);
|
||||
// Отключим работу с цветом
|
||||
glDrawBuffer(GL_NONE);
|
||||
glReadBuffer(GL_NONE);
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
|
||||
// Шейдер для расчета теней
|
||||
ShaderProgram sunShadowShader;
|
||||
// Загрузим шейдер
|
||||
sunShadowShader.load(GL_VERTEX_SHADER, "shaders/sun_shadow.vert");
|
||||
sunShadowShader.load(GL_GEOMETRY_SHADER, "shaders/sun_shadow.geom");
|
||||
sunShadowShader.load(GL_FRAGMENT_SHADER, "shaders/empty.frag");
|
||||
sunShadowShader.link();
|
||||
|
||||
// Размер одной стороны кубической карты
|
||||
const GLuint pointShadow_resolution = 500;
|
||||
// Создадим буфер кадра для рендера теней от источников света
|
||||
FBO pointShadowBuffer;
|
||||
// Создадим текстуры для буфера кадра
|
||||
TextureCubeArray pointShadowDepth(MAX_LIGHTS, pointShadow_resolution, pointShadow_resolution, GL_DEPTH_ATTACHMENT, 5, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT);
|
||||
// Отключим работу с цветом
|
||||
glDrawBuffer(GL_NONE);
|
||||
glReadBuffer(GL_NONE);
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
|
||||
// Шейдер для расчета теней от точечных источников
|
||||
ShaderProgram pointShadowShader;
|
||||
// Загрузим шейдер
|
||||
pointShadowShader.load(GL_VERTEX_SHADER, "shaders/sun_shadow.vert");
|
||||
pointShadowShader.load(GL_GEOMETRY_SHADER, "shaders/point_shadow.geom");
|
||||
pointShadowShader.load(GL_FRAGMENT_SHADER, "shaders/point_shadow.frag");
|
||||
pointShadowShader.link();
|
||||
|
||||
// Создадим буфер для вычисления SSAO
|
||||
GLuint attachments_ssao[] = { GL_COLOR_ATTACHMENT0 };
|
||||
FBO ssaoBuffer(attachments_ssao, sizeof(attachments_ssao) / sizeof(GLuint));
|
||||
// Создадим текстуры для буфера кадра
|
||||
Texture ssaoTexture_raw(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 0, GL_RED, GL_RED);
|
||||
pssaoTexture_raw = &ssaoTexture_raw;
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
|
||||
// Стандартные параметры SSAO
|
||||
SSAO_data ssao_data;
|
||||
// Расчет масштабирования текстуры шума
|
||||
ssao_data.scale = {WINDOW_WIDTH/4,WINDOW_HEIGHT/4};
|
||||
// Генерируем случайные векторы
|
||||
std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // Генерирует случайные вещественные числа в заданном диапазоне
|
||||
std::default_random_engine generator;
|
||||
glm::vec3 sample; // Выборка
|
||||
for (int i = 0; i < ssao_data.size; i++)
|
||||
{
|
||||
sample = { randomFloats(generator) * 2.0 - 1.0
|
||||
, randomFloats(generator) * 2.0 - 1.0
|
||||
, randomFloats(generator)
|
||||
};
|
||||
sample = glm::normalize(sample);
|
||||
sample *= randomFloats(generator);
|
||||
|
||||
// Отмасштабируем выборку
|
||||
sample *= 0.1 + 0.9 * (i / (float)ssao_data.size) * (i / (float)ssao_data.size);
|
||||
ssao_data.samples[i] = sample;
|
||||
}
|
||||
// Загрузка данных в uniform-буфер
|
||||
UBO ssaoUB(&ssao_data, sizeof(SSAO_data), 4);
|
||||
|
||||
// Текстура шума
|
||||
glm::vec3 noise_vecs[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
noise_vecs[i] = { randomFloats(generator) * 2.0 - 1.0
|
||||
, randomFloats(generator) * 2.0 - 1.0
|
||||
, 0.0f
|
||||
};
|
||||
Texture noiseTexture(4,4, noise_vecs, 2, GL_RGBA32F, GL_RGB);
|
||||
|
||||
// Шейдер для расчета SSAO
|
||||
ShaderProgram ssaoShader;
|
||||
// Загрузим шейдер
|
||||
ssaoShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
|
||||
ssaoShader.load(GL_FRAGMENT_SHADER, "shaders/ssao.frag");
|
||||
ssaoShader.link();
|
||||
// Текстуры, используемые в шейдере
|
||||
const char* ssaoShader_names[] = {"gPosition", "gNormal", "noise"};
|
||||
ssaoShader.bindTextures(ssaoShader_names, sizeof(ssaoShader_names)/sizeof(const char*));
|
||||
|
||||
// Создадим буфер для размытия SSAO
|
||||
FBO ssaoBlurBuffer(attachments_ssao, sizeof(attachments_ssao) / sizeof(GLuint));
|
||||
// Создадим текстуры для буфера кадра
|
||||
Texture ssaoTexture(WINDOW_WIDTH, WINDOW_HEIGHT, GL_COLOR_ATTACHMENT0, 6, GL_RED, GL_RED);
|
||||
pssaoTexture = &ssaoTexture;
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
|
||||
// Шейдер для размытия SSAO
|
||||
ShaderProgram ssaoBlurShader;
|
||||
// Загрузим шейдер
|
||||
ssaoBlurShader.load(GL_VERTEX_SHADER, "shaders/quad.vert");
|
||||
ssaoBlurShader.load(GL_FRAGMENT_SHADER, "shaders/ssaoBlur.frag");
|
||||
ssaoBlurShader.link();
|
||||
|
||||
// Модель прямоугольника
|
||||
Scene rectangle = loadOBJtoScene("../resources/models/plane2.obj", "../resources/models/", "../resources/textures/");
|
||||
|
||||
// Зададим горизонтальное положение перед камерой
|
||||
rectangle.root.e_position().y = -1;
|
||||
rectangle.root.e_position().z = 2;
|
||||
rectangle.root.e_rotation() = glm::quat(0.707f, 0.707f, 0.0f, 0.0f);
|
||||
rectangle.root.e_scale() = glm::vec3(4);
|
||||
|
||||
// Текстуры для прямоугольника
|
||||
Texture rectangle_diffuse(TEX_ALBEDO, "../resources/textures/rekovalev_diffusemap.png");
|
||||
rectangle.models.begin()->set_texture(rectangle_diffuse);
|
||||
Texture rectangle_normal(TEX_NORMAL, "../resources/textures/rekovalev_normalmap.png");
|
||||
rectangle.models.begin()->set_texture(rectangle_normal);
|
||||
Texture rectangle_heights(TEX_HEIGHTS, "../resources/textures/rekovalev_bumpmap.png");
|
||||
rectangle.models.begin()->set_texture(rectangle_heights);
|
||||
|
||||
// Шейдер для рисования отладочных лампочек
|
||||
ShaderProgram bulbShader;
|
||||
// Загрузка и компиляция шейдеров
|
||||
bulbShader.load(GL_VERTEX_SHADER, "shaders/bulb.vert");
|
||||
bulbShader.load(GL_FRAGMENT_SHADER, "shaders/bulb.frag");
|
||||
bulbShader.link();
|
||||
|
||||
// Вершины для скайбокса
|
||||
glm::vec3 skybox_verticies[] = {
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
{-1.0f, -1.0f, -1.0f},
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
{ 1.0f, 1.0f, -1.0f},
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
{-1.0f, -1.0f, -1.0f},
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
{-1.0f, 1.0f, 1.0f},
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
{ 1.0f, -1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, -1.0f},
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
{-1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, -1.0f, 1.0f},
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
{ 1.0f, 1.0f, -1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{ 1.0f, 1.0f, 1.0f},
|
||||
{-1.0f, 1.0f, 1.0f},
|
||||
{-1.0f, 1.0f, -1.0f},
|
||||
|
||||
{-1.0f, -1.0f, -1.0f},
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
{ 1.0f, -1.0f, -1.0f},
|
||||
{-1.0f, -1.0f, 1.0f},
|
||||
{ 1.0f, -1.0f, 1.0f}
|
||||
};
|
||||
|
||||
// Модель скайбокса
|
||||
Model skybox;
|
||||
skybox.load_verteces(skybox_verticies, sizeof(skybox_verticies)/sizeof(glm::vec3));
|
||||
TextureCube skybox_texture(TEX_ALBEDO, { "../resources/textures/skybox/px.jpg"
|
||||
, "../resources/textures/skybox/nx.jpg"
|
||||
, "../resources/textures/skybox/py.jpg"
|
||||
, "../resources/textures/skybox/ny.jpg"
|
||||
, "../resources/textures/skybox/pz.jpg"
|
||||
, "../resources/textures/skybox/nz.jpg"
|
||||
});
|
||||
|
||||
// Шейдер для скайбокса
|
||||
ShaderProgram skyboxShader;
|
||||
// Загрузим шейдеры
|
||||
skyboxShader.load(GL_VERTEX_SHADER, "shaders/skybox.vert");
|
||||
skyboxShader.load(GL_FRAGMENT_SHADER, "shaders/skybox.frag");
|
||||
skyboxShader.link();
|
||||
// Привязка текстуры скайбокса
|
||||
const char* skybox_shader_names[] = {"skybox"};
|
||||
skyboxShader.bindTextures(skybox_shader_names, sizeof(skybox_shader_names)/sizeof(const char*));
|
||||
|
||||
// Значение гамма-коррекции
|
||||
UBO gamma(&inv_gamma, sizeof(inv_gamma), 4);
|
||||
|
||||
ID selected; // Выбранная модель
|
||||
|
||||
// Шейдер для инструментов
|
||||
ShaderProgram toolsShader;
|
||||
// Загрузим шейдеры
|
||||
toolsShader.load(GL_VERTEX_SHADER, "shaders/gshader.vert");
|
||||
toolsShader.load(GL_FRAGMENT_SHADER, "shaders/tools.frag");
|
||||
toolsShader.link();
|
||||
|
||||
// Инструменты
|
||||
Transform transform;
|
||||
Rotate rotate;
|
||||
Scale scale;
|
||||
TRS& currentTool = transform;
|
||||
|
||||
// Текстура для отражений скайбокса
|
||||
TextureCube reflections_texture(skybox_texture);
|
||||
reflections_texture.setType(9);
|
||||
|
||||
// Пока не произойдет событие запроса закрытия окна
|
||||
while(!glfwWindowShouldClose(window))
|
||||
{
|
||||
// Загрузка данных о камере
|
||||
cameraUB.loadSub(&Camera::current().getData(), sizeof(CameraData));
|
||||
// Загрузим информацию об источниках света
|
||||
Light::upload(light_data);
|
||||
// Загружаем информацию о направленном источнике
|
||||
Sun::upload(sun_data);
|
||||
|
||||
// Активируем G-кадра
|
||||
gbuffer.use();
|
||||
// Используем шейдер с освещением
|
||||
gShader.use();
|
||||
// Очистка буфера цвета и глубины
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Тут производится рендер
|
||||
scene.render(gShader, material_data);
|
||||
rectangle.render(gShader, material_data);
|
||||
|
||||
// Отрисовка отладочных лампочек со специальным шейдером
|
||||
bulbShader.use();
|
||||
Light::render(bulbShader, material_data);
|
||||
|
||||
// Используем шейдер для инструментов
|
||||
toolsShader.use();
|
||||
// Рендерим инструменты для выбранного объекта
|
||||
currentTool.render(selected.value, toolsShader, material_data);
|
||||
|
||||
// Выбор объекта
|
||||
if (mouse.left == 0100000)
|
||||
{
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT4);
|
||||
glReadPixels(mouse.x, WINDOW_HEIGHT-mouse.y, 1, 1, GL_RGB_INTEGER, GL_UNSIGNED_INT, &selected);
|
||||
std::cout << (void*) selected.value << ' ' << selected.etc << '\n';
|
||||
}
|
||||
|
||||
// Активируем буфер SSAO
|
||||
ssaoBuffer.use();
|
||||
// Используем шейдер для расчета SSAO
|
||||
ssaoShader.use();
|
||||
// Очистка буфера цвета
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// Подключаем текстуры G-буфера
|
||||
gPosition.use();
|
||||
gNormal.use();
|
||||
// Подключаем текстуру шума для SSAO
|
||||
noiseTexture.use();
|
||||
// Рендерим прямоугольник
|
||||
quadModel.render();
|
||||
|
||||
// Активируем буфер размытия SSAO
|
||||
ssaoBlurBuffer.use();
|
||||
// Используем шейдер для размытия SSAO
|
||||
ssaoBlurShader.use();
|
||||
// Очистка буфера цвета
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// Подключаем текстуру сырого SSAO
|
||||
ssaoTexture_raw.use();
|
||||
// Рендерим прямоугольник
|
||||
quadModel.render();
|
||||
|
||||
// Изменим размер вывода для тени
|
||||
glViewport(0, 0, sunShadow_resolution, sunShadow_resolution);
|
||||
// Активируем буфер кадра для теней от солнца
|
||||
sunShadowBuffer.use();
|
||||
// Подключим шейдер для расчета теней
|
||||
sunShadowShader.use();
|
||||
// Очистка буфера глубины
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
// Рендерим геометрию в буфер глубины
|
||||
scene.render(sunShadowShader, material_data);
|
||||
rectangle.render(sunShadowShader, material_data);
|
||||
|
||||
// Изменим размер вывода для стороны кубической карты точечного источника
|
||||
glViewport(0, 0, pointShadow_resolution, pointShadow_resolution);
|
||||
// Активируем буфер кадра для теней от солнца
|
||||
pointShadowBuffer.use();
|
||||
// Подключим шейдер для расчета теней
|
||||
pointShadowShader.use();
|
||||
// Очистка буфера глубины
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
// Для каждого источника вызывается рендер сцены
|
||||
for (int i = 0; i < Light::getCount(); i++)
|
||||
{
|
||||
glUniform1i(pointShadowShader.getUniformLoc("light_i"), i);
|
||||
// Рендерим геометрию в буфер глубины
|
||||
scene.render(pointShadowShader, material_data);
|
||||
rectangle.render(pointShadowShader, material_data);
|
||||
}
|
||||
|
||||
// Изменим размер вывода для окна
|
||||
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
// Активируем базовый буфер кадра
|
||||
FBO::useDefault();
|
||||
// Подключаем шейдер для прямоугольника
|
||||
lightShader.use();
|
||||
// Очистка буфера цвета и глубины
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
// Подключаем текстуры G-буфера
|
||||
gPosition.use();
|
||||
gNormal.use();
|
||||
gBaseColor.use();
|
||||
gRMS.use();
|
||||
gID.use();
|
||||
gEmittedColor.use();
|
||||
reflections_texture.use();
|
||||
// Идентификатор выбранного объекта для обводки
|
||||
glUniform3uiv(lightShader.getUniformLoc("selectedID"), 1, (GLuint*) &selected);
|
||||
// Подключаем текстуры теней
|
||||
sunShadowDepth.use();
|
||||
pointShadowDepth.use();
|
||||
// Подключим текстуру SSAO
|
||||
ssaoTexture.use();
|
||||
// Рендерим прямоугольник с расчетом освещения
|
||||
quadModel.render();
|
||||
|
||||
// Перенос буфера глубины
|
||||
FBO::useDefault(GL_DRAW_FRAMEBUFFER); // Базовый в режиме записи
|
||||
gbuffer.use(GL_READ_FRAMEBUFFER); // Буфер геометрии в режиме чтения
|
||||
// Копирование значений глубины
|
||||
glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
FBO::useDefault(); // Использование базового буфера для дальнейших работ
|
||||
|
||||
// Отрисовка скайбокса без записи глубины
|
||||
glDepthMask(GL_FALSE);
|
||||
// Используем шейдер для скайбокса
|
||||
skyboxShader.use();
|
||||
// Подключаем текстуру скайбокса
|
||||
skybox_texture.use();
|
||||
// Рендерим куб
|
||||
skybox.render();
|
||||
// Возвращаем запись глубины
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
// Дополнительная обработка мыши
|
||||
process_mouse_button(mouse.left);
|
||||
process_mouse_button(mouse.right);
|
||||
mouse.prev_x = mouse.x;
|
||||
mouse.prev_y = mouse.y;
|
||||
|
||||
// Представление содержимого буфера цепочки показа на окно
|
||||
glfwSwapBuffers(window);
|
||||
// Обработка системных событий
|
||||
glfwPollEvents();
|
||||
|
||||
// Поворот камеры
|
||||
if (mouse.right & 0100000
|
||||
&& mouse.x != mouse.prev_x
|
||||
&& mouse.y != mouse.prev_y)
|
||||
Camera::current().rotate(glm::vec2(mouse.x - mouse.prev_x, mouse.prev_y - mouse.y));
|
||||
|
||||
// Взаимодействие с инструментом при зажатой левой кнопке
|
||||
if (mouse.left > 0100000)
|
||||
if (selected.etc)
|
||||
currentTool.process(selected.value, selected.etc, glm::transpose(Camera::current().getVP()) * glm::vec4(mouse.x - mouse.prev_x, mouse.prev_y - mouse.y, 0, 1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user