10/src/Lights.cpp

218 lines
7.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Lights.h"
#include "Scene.h" // Для отладочного вывода лампочек
#include <stdexcept>
GLuint Light::count = 0; // количество используемых источников (должно быть <= MAX_LIGHTS)
LightData Light::data[MAX_LIGHTS]; // Массив данных по источникам света
Light Light::lights[MAX_LIGHTS]; // Массив источников-узлов сцены
// возвращает размер буфера в байтах
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(); // Проверка на работу с корректным индексом
data[index].position = glm::vec3(result_transform[3]); // Позиция из матрицы трансформации
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]; // Квадратичный коэф. угасания
}
}
// Возвращает ссылку на новый источник света
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)
{
}
// Оператор присваивания
Light& Light::operator=(const Light& other)
{
// Проверка на самоприсваивание
if (this != &other)
{
index = other.index; // Переносим индекс
uploadReq = other.uploadReq; // Необходимость загрузки
color = other.color;
radius = other.radius;
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);
// Цикл по источникам света
for (int i = 0; i < count; i++)
{
// Сдвиг на позицию источника
bulb.root.e_position() = data[i].position;
sphere.e_scale() = glm::vec3(data[i].attenuation.r); // Масштабирование сферы
// Задание цвета
bulb.models.begin()->material.ka = sphere.material.ka = data[i].color;
// Вызов отрисовки
bulb.render(shaderProgram, material_buffer);
// Рисование сферы покрытия источника в режиме линий
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;
}