#include "Lights.h" #include "Scene.h" // Для отладочного вывода лампочек #include 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; }