Major code rewrite

-class Light has been completely decoupled from class Ray
-class Ray is still in the project for reference, but is not used at all
-Light has been decoupled from the imgui variables, and instead has it's own member variables that are used to configure the object
-all of Light's bool variables are represented by a bit flag variable with appropriate functions to modify it
-the creation and sorting of end points for the Light object has been moved into the class itself
-extra functions and local variables have been created to help with readability of code
- Bounded light can now be greater than 180 degrees
-more imgui variables added to configure the light object
This commit is contained in:
Joseph Aquino 2025-04-15 02:02:17 -04:00
parent a3672012f6
commit 3941a59b0d
6 changed files with 312 additions and 176 deletions

204
Light.cpp
View File

@ -2,93 +2,191 @@
void Light::draw(sf::RenderWindow& window)
{
if (not m_visible) return;
changeColor(lightColor.asSfColor());
window.draw(m_light.data(), m_light.size(), sf::PrimitiveType::TriangleFan);
if (not drawEndPoints) return;
for (auto& point : m_light)
if (hasFlags(Flags::visibility))
{
window.draw(m_light.data(), m_light.size(), sf::PrimitiveType::TriangleFan);
}
if (hasFlags(Flags::drawRays))
{
for (auto& ray : m_rays)
{
sf::VertexArray tempRay(sf::PrimitiveType::Lines, 2);
tempRay[start].position = m_origin;
tempRay[start].color = m_rayColor;
tempRay[end].position = ray.position;
tempRay[end].color = m_rayColor;
window.draw(tempRay);
}
}
if (hasFlags(Flags::drawEndPoints))
{
for (auto& ray : m_rays)
{
point.color = sf::Color::Black;
sf::CircleShape endPoint(5.f, 20u);
endPoint.setOrigin({ 5, 5 });
endPoint.setFillColor(rayColor.asSfColor());
endPoint.setPosition(point.position);
endPoint.setFillColor(m_endPointColor);
endPoint.setPosition(ray.position);
window.draw(endPoint);
}
}
}
void Light::addRay(Ray ray)
{
m_rays.emplace_back(ray);
}
void Light::changeColor(sf::Color color)
{
for (auto& points : m_light)
points.color = color;
for (auto& ray : m_rays)
ray.changeColor(color);
}
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();
constructFullLight(polygons);
}
}
void Light::constructFullLight()
void Light::constructFullLight(const std::vector<sf::VertexArray>& polygons)
{
std::sort(m_rays.begin(), m_rays.end(), [](const auto& lhs, const auto& rhs) { return lhs.angle().wrapUnsigned().asRadians() < rhs.angle().wrapUnsigned().asRadians(); });
createAndSortRays(polygons);
calculateIntersects(polygons);
for (auto& ray : m_rays)
{
m_light.emplace_back(ray.points()[end]);
m_light.emplace_back(ray);
}
m_light.emplace_back(m_rays.front().points()[end]);//fully connect the triangle fan
m_light.emplace_back(m_rays.front());//fully connect the triangle fan
}
void Light::constructPartialLight(const std::vector<sf::VertexArray>& polygons)
{
auto upperAngleBound = m_center.value() + m_offset.value();
auto lowerAngleBound = m_center.value() - m_offset.value();
Ray upperBound(m_light[0].position, rayLength, upperAngleBound, lightColor.asSfColor());
Ray lowerBound(m_light[0].position, rayLength, lowerAngleBound, lightColor.asSfColor());
createAndSortRays(polygons, m_center.value() - m_offset.value(), m_center.value() + m_offset.value());
m_rays.emplace_back(upperBound);
m_rays.emplace_back(lowerBound);
for (auto& ray : m_rays)
{
ray.calculateIntersect(polygons);
}
calculateIntersects(polygons);
if ((m_center.value().wrapUnsigned().asDegrees() - m_offset.value().wrapUnsigned().asDegrees() < 0.f) or (m_center.value().wrapUnsigned().asDegrees() + m_offset.value().wrapUnsigned().asDegrees() > 360.f))
{
std::sort(m_rays.begin(), m_rays.end(), [](const auto& lhs, const auto& rhs) { return lhs.angle().wrapSigned().asRadians() < rhs.angle().wrapSigned().asRadians(); });
for (auto& ray : m_rays)
if (ray.angle().wrapSigned() >= lowerAngleBound.wrapSigned() and ray.angle().wrapSigned() <= upperAngleBound.wrapSigned())
{
m_light.emplace_back(ray.points()[end]);
}
}
else
{
std::sort(m_rays.begin(), m_rays.end(), [](const auto& lhs, const auto& rhs) { return lhs.angle().wrapUnsigned().asRadians() < rhs.angle().wrapUnsigned().asRadians(); });
for (auto& ray : m_rays)
if (ray.angle().wrapUnsigned() >= lowerAngleBound.wrapUnsigned() and ray.angle().wrapUnsigned() <= upperAngleBound.wrapUnsigned())
{
m_light.emplace_back(ray.points()[end]);
}
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 });
}
}
for (auto& poly : polygons)
{
for (std::size_t i = 0; i < poly.getVertexCount() - 1; i++)
{
if (distanceBetween(m_origin, poly[i].position) <= m_rayLength or hasFlags(Flags::infiniteLength))
{
sf::Angle 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))
{
if (m_rayCount > 0)
{
float angleIncrement = 360.f / (float)m_rayCount;
for (int i = 0; i < m_rayCount; i++)
{
sf::Vertex temp{ constructVector(m_origin, sf::degrees(angleIncrement * i), m_rayLength), m_lightColor };
if (withinBounds(temp.position - m_origin, lowerBoundVector, upperBoundVector))
{
m_rays.emplace_back(temp);
}
}
}
}
m_rays.emplace_back(lowerBound);
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 (distanceBetween(m_origin, poly[i].position) <= m_rayLength or hasFlags(Flags::infiniteLength))
{
if (withinBounds(poly[i].position - m_origin, lowerBoundVector, upperBoundVector))
{
sf::Angle 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 });
}
}
}
}
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)
{
for (auto& ray : m_rays)
{
sf::Vector2f closestIntersection = ray.position;
std::optional<sf::Vector2f> currentIntersect{};
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 (currentIntersect)
{
if ((closestIntersection - m_origin).length() > (currentIntersect.value() - m_origin).length())
{
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();
}

70
Light.h
View File

@ -17,18 +17,40 @@ class Light
public:
Light(bool visible = true)
: m_visible(visible)
{
m_light.push_back(sf::Vertex());
m_light.push_back(sf::Vertex{ {0.f,0.f}, m_lightColor});
if (visible)
{
setFlags(Flags::visibility);
}
else
{
unsetFlags(Flags::visibility);
}
}
Light(sf::Angle center, sf::Angle offset, bool visible = true)
: m_center(center)
, m_offset(offset)
, m_visible(visible)
{
m_light.push_back(sf::Vertex{});
if (visible)
{
setFlags(Flags::visibility);
}
else
{
unsetFlags(Flags::visibility);
}
}
enum class Flags
{
visibility = 1 << 0,
infiniteLength = 1 << 1,
drawRays = 1 << 2,
drawEndPoints = 1 << 3
};
void addAngleBounds(sf::Angle center, sf::Angle offset)
{
@ -42,8 +64,10 @@ public:
m_offset.reset();
}
void setVisibility(bool input) { m_visible = input; }
bool visible() const { return m_visible; }
void setFlags(Flags flags) { m_flags |= (int)flags; }
void unsetFlags(Flags flags) { m_flags &= ~(int)flags; }
void toggleFlags(Flags flags) { m_flags ^= (int)flags; }
bool hasFlags(Flags flags) const { return (m_flags & (int)flags) == (int)flags; }
void setCenterAngle(sf::Angle center) { m_center = center; }
std::optional<sf::Angle> getCenterAngle() const { return m_center; }
@ -51,27 +75,45 @@ public:
void setOffsetAngle(sf::Angle offset) { m_offset = offset; }
std::optional<sf::Angle> getOffsetAngle() const { return m_offset; }
sf::Vector2f getOrigin() const { return m_light[0].position; }
void setOrigin(sf::Vector2f center) { m_light[0].position = center; }
void setOrigin(sf::Vector2f point) { m_origin = point; }
sf::Vector2f getOrigin() const { return m_origin; }
void setRayCount(int input) { m_rayCount = input; }
int getRayCount() const { return m_rayCount; };
float getRayLength() { return m_rayLength; }
void setRayLength(float input) { m_rayLength = input; }
void draw(sf::RenderWindow& window);
void addRay(Ray ray);
void clearRays() { m_rays.clear(); m_light.clear(); m_light.push_back(sf::Vertex{ {0.f,0.f}, m_lightColor }); }
void clearRays() { m_rays.clear(); m_light.clear(); m_light.push_back(sf::Vertex{});}
void changeColor(sf::Color color);
void changeLightColor(sf::Color color) { m_lightColor = color; }
void changeEndPointColor(sf::Color color) { m_endPointColor = color; }
void changeRayColor(sf::Color color) { m_rayColor = color; }
void processRays(const std::vector<sf::VertexArray>& polygons);
private:
std::vector<sf::Vertex> m_light{};
RayVector m_rays{};
std::vector<sf::Vertex> m_rays{};
std::optional<sf::Angle> m_center{};
std::optional<sf::Angle> m_offset{};
sf::Color m_lightColor{ sf::Color::Yellow };
sf::Color m_endPointColor{ sf::Color::Magenta };
sf::Color m_rayColor{ sf::Color::Red };
sf::Vector2f m_origin{};
bool m_visible{};
float m_rayLength{ 500.f };
int m_rayCount{ 500 };
int m_flags{};
void constructFullLight();
private:
void constructFullLight(const std::vector<sf::VertexArray>& polygons);
void constructPartialLight(const std::vector<sf::VertexArray>& polygons);
void createAndSortRays(const std::vector<sf::VertexArray>& polygons);
void createAndSortRays(const std::vector<sf::VertexArray>& polygons, sf::Angle lowerAngleBounds, sf::Angle upperAngleBounds);// for bounded light
void calculateIntersects(const std::vector<sf::VertexArray>& polygons);
bool withinBounds(sf::Vector2f point, sf::Vector2f lower, sf::Vector2f upper);
};

15
Ray.h
View File

@ -46,21 +46,6 @@ public:
void calculateIntersect(const std::vector<sf::VertexArray>& polygons);
bool operator== (const Ray& rhs)
{
return m_points[start].position == rhs.points()[start].position and m_points[end].position == rhs.points()[end].position;
}
Ray operator+ (const Ray& rhs)
{
return Ray(this->m_points[start].position + rhs.m_points[start].position, this->m_points[end].position + rhs.m_points[end].position);
}
Ray operator- (const Ray& rhs)
{
return Ray(this->m_points[start].position - rhs.m_points[start].position, this->m_points[end].position - rhs.m_points[end].position);
}
private:
sf::VertexArray m_points{ sf::PrimitiveType::Lines, 2 };//points[0] = origin of ray, points[1] = end of ray
sf::Angle m_angle{};

View File

@ -133,20 +133,3 @@ inline float distanceBetween(sf::Vector2f startPoint, sf::Vector2f endPoint)
{
return sqrtf(powf(endPoint.x - startPoint.x, 2) + powf(endPoint.y - startPoint.y, 2));
}
//imGui Variables
inline sf::Vector2f mousePos{};
inline float polySize[2] = { 100.f, 100.f };
inline int numRays{ 500 };
inline int vertexBounds[2] = { 3u, 10u };
inline int averageSize{ 100u };
inline float rayLength{ 500.f };
inline imguiColor rayColor = constructImguiColor(sf::Color::Red);
inline imguiColor lightColor = constructImguiColor(sf::Color::Yellow);
inline bool drawRays{ false };
inline bool drawLight{ true };
inline bool drawEndPoints{ false };
inline bool boundedLight{ false };
inline int lightCenterAngle{0};
inline int lightOffsetAngle{45};

View File

@ -3,6 +3,6 @@ Pos=60,60
Size=400,400
[Window][Config]
Pos=44,11
Size=708,467
Pos=7,11
Size=670,442

182
main.cpp
View File

@ -15,18 +15,35 @@
auto window = sf::RenderWindow(sf::VideoMode({ 1920u, 1080u }), "Ray Casting Test");
Light light;
RayVector rays;
std::vector<sf::VertexArray> polygons;
Light light;
std::vector<sf::VertexArray> polygons;
sf::Vector2f mousePos{};
sf::Vector2f center{ 1920.f / 2.f, 1080.f / 2.f };
//imGui Variables
float polySize[2] = { 100.f, 100.f };
int numRays{ 500 };
int vertexBounds[2] = { 3u, 10u };
int averageSize{ 100u };
float rayLength{ 500.f };
imguiColor rayColor = constructImguiColor(sf::Color::Red);
imguiColor lightColor = constructImguiColor(sf::Color::Yellow);
imguiColor endPointColor = constructImguiColor(sf::Color::Magenta);
bool drawRays{ false };
bool drawLight{ true };
bool drawEndPoints{ false };
bool boundedLight{ false };
bool infiniteRayLength{ false };
int lightCenterAngle{ 0 };
int lightOffsetAngle{ 45 };
void GUI();
void userInput(std::vector<sf::VertexArray>& polygons);
void processRays();
void userInput();
void render();
int main()
{
sf::Vector2f center{ 1920.f / 2.f, 1080.f / 2.f };
//sfml setup
sf::Clock clock;
@ -37,24 +54,21 @@ int main()
mousePos = center;
polygons.emplace_back(constructRectangle(center, center));
//main game loop
while (window.isOpen())
{
ImGui::SFML::Update(window, clock.restart());
rays.clear();
light.clearRays();
userInput(polygons);
processRays();
light.setOrigin(mousePos);
for (auto& ray : rays) light.addRay(ray);
userInput();
GUI();
light.setOrigin(mousePos);
light.processRays(polygons);
render();
}
@ -72,28 +86,86 @@ void GUI()
ImGui::Text("C: clear all polygons");
ImGui::Text("Scroll wheel: increase/decrease light direction (if light is bounded)");
ImGui::Unindent();
ImGui::Checkbox("Render rays", &drawRays);
if (ImGui::Checkbox("Render rays", &drawRays))
{
if (drawRays)
{
light.setFlags(Light::Flags::drawRays);
}
else
{
light.unsetFlags(Light::Flags::drawRays);
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Render end points", &drawEndPoints))
{
if (drawEndPoints)
{
light.setFlags(Light::Flags::drawEndPoints);
}
else
{
light.unsetFlags(Light::Flags::drawEndPoints);
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Render light", &drawLight))
{
light.setVisibility(drawLight);
if (drawLight)
{
light.setFlags(Light::Flags::visibility);
}
if (ImGui::SliderInt("number of rays", &numRays, 5, 500))
else
{
rays.clear();
{
float i = 0.f, angleIncrement = 360.f / (float)numRays;
for (int i = 0; i < numRays; i++) rays.emplace_back(mousePos, rayLength, sf::degrees(angleIncrement * i), rayColor.asSfColor());
light.unsetFlags(Light::Flags::visibility);
}
}
}
ImGui::InputFloat("Ray Length", &rayLength);
if (ImGui::ColorEdit3("Ray color", &rayColor.r))
if (ImGui::Checkbox("Infinite ray length", &infiniteRayLength))
{
for (auto& ray : rays) ray.changeColor(rayColor.asSfColor());
if (infiniteRayLength)
{
light.setFlags(Light::Flags::infiniteLength);
}
ImGui::ColorEdit3("Light color", &lightColor.r);
else
{
light.unsetFlags(Light::Flags::infiniteLength);
}
}
if (not infiniteRayLength)
{
ImGui::SliderInt("number of rays", &numRays, 5, 500);
light.setRayCount(numRays);
if (ImGui::InputFloat("Ray Length", &rayLength))
{
rayLength = std::clamp(rayLength, 5.f, 10000.f);
}
light.setRayLength(rayLength);
}
if (drawRays)
{
ImGui::ColorEdit4("Ray color", &rayColor.r);
}
if (drawLight)
{
ImGui::ColorEdit4("Light color", &lightColor.r);
}
if (drawEndPoints)
{
ImGui::ColorEdit4("End point color", &endPointColor.r);
}
light.changeLightColor(lightColor.asSfColor());
light.changeEndPointColor(endPointColor.asSfColor());
light.changeRayColor(rayColor.asSfColor());
if(ImGui::Checkbox("Light is bounded", &boundedLight))
{
if (boundedLight)
@ -107,19 +179,18 @@ void GUI()
}
if (boundedLight)
{
ImGui::Checkbox("Draw end points", &drawEndPoints);
ImGui::SliderInt("Light direction", &lightCenterAngle, 0, 359);
light.setCenterAngle(sf::degrees((float)lightCenterAngle));
if (ImGui::InputInt("Offset size", &lightOffsetAngle))
{
if (lightOffsetAngle > 89)
if (lightOffsetAngle > 175)
{
lightOffsetAngle = 89;
lightOffsetAngle = 175;
}
if (lightOffsetAngle < 0)
if (lightOffsetAngle < 5)
{
lightOffsetAngle = 0;
lightOffsetAngle = 5;
}
}
light.setOffsetAngle(sf::degrees((float)lightOffsetAngle));
@ -146,7 +217,7 @@ void GUI()
ImGui::End();
}
void userInput(std::vector<sf::VertexArray>& polygons)
void userInput()
{
while (const std::optional event = window.pollEvent())
{
@ -163,6 +234,7 @@ void userInput(std::vector<sf::VertexArray>& polygons)
{
case sf::Keyboard::Scancode::C:
polygons.clear();
polygons.emplace_back(constructRectangle(center, center));
break;
case sf::Keyboard::Scancode::R:
@ -212,58 +284,14 @@ void userInput(std::vector<sf::VertexArray>& polygons)
}
}
void processRays()
{
if (numRays > 0)
{
float i = 0.f, angleIncrement = 360.f / (float)numRays;
for (int i = 0; i < numRays; i++) rays.emplace_back(Ray(mousePos, rayLength, sf::degrees(angleIncrement * i), rayColor.asSfColor()));
}
for (auto& poly : polygons)
{
for (std::size_t i = 0; i < poly.getVertexCount() - 1; i++)
{
if (distanceBetween(mousePos, poly[i].position) <= rayLength)
{
sf::Angle angle = sf::radians(std::atan2f(poly[i].position.y - mousePos.y, poly[i].position.x - mousePos.x));
rays.emplace_back(Ray(mousePos, poly[i].position, rayColor.asSfColor()));
rays.emplace_back(Ray(mousePos, rayLength, angle + sf::radians(.00001f), rayColor.asSfColor()));
rays.emplace_back(Ray(mousePos, rayLength, angle - sf::radians(.00001f), rayColor.asSfColor()));
}
}
}
for (auto& ray : rays)
{
ray.calculateIntersect(polygons);
}
}
void render()
{
window.clear(sf::Color::White);
light.processRays(polygons);
light.draw(window);
for (auto& poly : polygons) window.draw(poly);
if (drawRays)
{
for (auto& ray : rays)
{
auto& rayPoints = ray.points();
sf::CircleShape endPoint(5.f, 20u);
endPoint.setOrigin({ 5, 5 });
endPoint.setFillColor(rayColor.asSfColor());
endPoint.setPosition(rayPoints[end].position);
window.draw(rayPoints);
window.draw(endPoint);
}
}
ImGui::SFML::Render(window);
window.display();