ray-casting-demo/Light.cpp

194 lines
6.0 KiB
C++

#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<sf::VertexArray>& polygons)
{
m_light[0].position = m_origin;
if (m_center and m_offset)
{
constructPartialLight(polygons);
}
else
{
constructFullLight(polygons);
}
}
void Light::constructFullLight(const std::vector<sf::VertexArray>& 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<sf::VertexArray>& 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<sf::VertexArray>& 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<sf::VertexArray>& 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<sf::VertexArray>& polygons)
{
std::optional<sf::Vector2f> 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();
}