#include "Light.h" void Light::draw(sf::RenderWindow& window) { if (hasFlags(Flags::visibility)) { window.draw(m_light.data(), m_light.size(), sf::PrimitiveType::TriangleFan); } if (hasFlags(Flags::drawRays)) { sf::VertexArray tempRay(sf::PrimitiveType::Lines, 2); tempRay[start].position = m_origin; tempRay[start].color = m_rayColor; tempRay[end].color = m_rayColor; for (auto& ray : m_rays) { tempRay[end].position = ray.position; window.draw(tempRay); } } if (hasFlags(Flags::drawEndPoints)) { sf::CircleShape endPoint(5.f, 20u); endPoint.setOrigin({ 5, 5 }); endPoint.setFillColor(m_endPointColor); for (auto& ray : m_rays) { endPoint.setPosition(ray.position); window.draw(endPoint); } } } void Light::processRays(const std::vector& polygons) { m_light[0].position = m_origin; if (m_center and m_offset) { constructPartialLight(polygons); } else { constructFullLight(polygons); } } void Light::constructFullLight(const std::vector& polygons) { createAndSortRays(polygons); calculateIntersects(polygons); for (auto& ray : m_rays) { m_light.emplace_back(ray); } m_light.emplace_back(m_rays.front());//fully connect the triangle fan } void Light::constructPartialLight(const std::vector& polygons) { createAndSortRays(polygons, m_center.value() - m_offset.value(), m_center.value() + m_offset.value()); calculateIntersects(polygons); for (auto& ray : m_rays) { m_light.emplace_back(ray); } } void Light::createAndSortRays(const std::vector& polygons) { if (not hasFlags(Flags::infiniteLength)) { if (m_rayCount > 0) { float angleIncrement = 360.f / (float)m_rayCount; for (int i = 0; i < m_rayCount; i++) m_rays.emplace_back(sf::Vertex{ constructVector( m_origin, sf::degrees(angleIncrement * i), m_rayLength ), m_lightColor }); } } sf::Angle angle; for (auto& poly : polygons) { for (std::size_t i = 0; i < poly.getVertexCount() - 1; i++) { if (hasFlags(Flags::infiniteLength) ? false : distanceBetween(m_origin, poly[i].position) >= m_rayLength) continue; angle = sf::radians(std::atan2f(poly[i].position.y - m_origin.y, poly[i].position.x - m_origin.x)); m_rays.emplace_back(sf::Vertex{ poly[i].position, m_lightColor }); m_rays.emplace_back(sf::Vertex{ constructVector( m_origin, angle + sf::radians(.00001f), m_rayLength + (5000.f * hasFlags(Flags::infiniteLength)) ), m_lightColor }); m_rays.emplace_back(sf::Vertex{ constructVector( m_origin, angle - sf::radians(.00001f), m_rayLength + (5000.f * hasFlags(Flags::infiniteLength)) ), m_lightColor }); } } std::sort(m_rays.begin(), m_rays.end(), [&](const auto& lhs, const auto& rhs) { return (lhs.position - m_origin).angle().wrapUnsigned().asRadians() < (rhs.position - m_origin).angle().wrapUnsigned().asRadians(); }); } // overload for bounded light void Light::createAndSortRays(const std::vector& polygons, sf::Angle lowerAngleBounds, sf::Angle upperAngleBounds) { sf::Vertex lowerBound{ constructVector( m_origin, lowerAngleBounds, m_rayLength + (5000.f * hasFlags(Flags::infiniteLength)) ), m_lightColor }; sf::Vertex upperBound{ constructVector( m_origin, upperAngleBounds, m_rayLength + (5000.f * hasFlags(Flags::infiniteLength)) ), m_lightColor }; auto lowerBoundVector = lowerBound.position - m_origin; auto upperBoundVector = upperBound.position - m_origin; if (not hasFlags(Flags::infiniteLength)) { sf::Vertex temp; float angleIncrement = 360.f / (float)m_rayCount; for (int i = 0; i < m_rayCount; i++) { temp = sf::Vertex{ constructVector(m_origin, sf::degrees(angleIncrement * i), m_rayLength), m_lightColor }; if (withinBounds(temp.position - m_origin, lowerBoundVector, upperBoundVector)) m_rays.emplace_back(temp); } } constexpr float infiniteLength = 5000.f; sf::Angle angle; for (auto& poly : polygons) { for (std::size_t i = 0; i < poly.getVertexCount() - 1; i++) { if (poly[i].position == m_origin) continue;// cannot pass zero vector to sf::Vector2::angleTo() if (hasFlags(Flags::infiniteLength) ? false : distanceBetween(m_origin, poly[i].position) >= m_rayLength) continue; if (not withinBounds(poly[i].position - m_origin, lowerBoundVector, upperBoundVector)) continue; angle = sf::radians(std::atan2f(poly[i].position.y - m_origin.y, poly[i].position.x - m_origin.x)); m_rays.emplace_back(sf::Vertex{ poly[i].position, m_lightColor }); m_rays.emplace_back(sf::Vertex{ constructVector( m_origin, angle + sf::radians(.00001f), m_rayLength + (infiniteLength * hasFlags(Flags::infiniteLength)) ), m_lightColor }); m_rays.emplace_back(sf::Vertex{ constructVector( m_origin, angle - sf::radians(.00001f), m_rayLength + (infiniteLength * hasFlags(Flags::infiniteLength)) ), m_lightColor }); } } m_rays.emplace_back(lowerBound); m_rays.emplace_back(upperBound); std::sort(m_rays.begin(), m_rays.end(), [&](const auto& lhs, const auto& rhs) { return (lhs.position - m_origin).angleTo(upperBoundVector).wrapUnsigned().asRadians() < (rhs.position - m_origin).angleTo(upperBoundVector).wrapUnsigned().asRadians(); }); } void Light::calculateIntersects(const std::vector& polygons) { std::optional currentIntersect{}; for (auto& ray : m_rays) { sf::Vector2f closestIntersection = ray.position; currentIntersect.reset(); for (auto& poly : polygons) { for (std::size_t i = 1; i <= poly.getVertexCount() - 1; i++) { currentIntersect = lineIntersect({ m_origin, ray.position }, { poly[i - 1].position , poly[i].position }); if (not currentIntersect) continue; if ((closestIntersection - m_origin).length() < (currentIntersect.value() - m_origin).length()) continue; closestIntersection = currentIntersect.value(); } } ray.position = closestIntersection; } } bool Light::withinBounds(sf::Vector2f point, sf::Vector2f lower, sf::Vector2f upper) { return point.angleTo(upper).wrapUnsigned().asRadians() < (lower).angleTo(upper).wrapUnsigned().asRadians(); }