From 3941a59b0df1e5336a9f9033421160feffb51f89 Mon Sep 17 00:00:00 2001 From: Joseph Aquino Date: Tue, 15 Apr 2025 02:02:17 -0400 Subject: [PATCH] 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 --- Light.cpp | 196 +++++++++++++++++++++++++++++++++++++++------------- Light.h | 70 +++++++++++++++---- Ray.h | 15 ---- Utility.hpp | 17 ----- imgui.ini | 4 +- main.cpp | 186 ++++++++++++++++++++++++++++--------------------- 6 files changed, 312 insertions(+), 176 deletions(-) diff --git a/Light.cpp b/Light.cpp index 08f667f..03ca58d 100644 --- a/Light.cpp +++ b/Light.cpp @@ -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)) { - point.color = sf::Color::Black; - sf::CircleShape endPoint(5.f, 20u); - endPoint.setOrigin({ 5, 5 }); - endPoint.setFillColor(rayColor.asSfColor()); - endPoint.setPosition(point.position); - window.draw(endPoint); + 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) + { + sf::CircleShape endPoint(5.f, 20u); + endPoint.setOrigin({ 5, 5 }); + 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& 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& 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& 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()); + + calculateIntersects(polygons); - m_rays.emplace_back(upperBound); - m_rays.emplace_back(lowerBound); for (auto& ray : m_rays) { - ray.calculateIntersect(polygons); + m_light.emplace_back(ray); } - 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)) +} + + +void Light::createAndSortRays(const std::vector& polygons) +{ + + if (not hasFlags(Flags::infiniteLength)) { - 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()) + if (m_rayCount > 0) { - m_light.emplace_back(ray.points()[end]); + 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 }); } } - else + + for (auto& poly : polygons) { - 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()) + for (std::size_t i = 0; i < poly.getVertexCount() - 1; i++) + { + if (distanceBetween(m_origin, poly[i].position) <= m_rayLength or hasFlags(Flags::infiniteLength)) { - m_light.emplace_back(ray.points()[end]); + 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& 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& polygons) +{ + + for (auto& ray : m_rays) + { + sf::Vector2f closestIntersection = ray.position; + std::optional 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(); } \ No newline at end of file diff --git a/Light.h b/Light.h index 7223f9e..abeb318 100644 --- a/Light.h +++ b/Light.h @@ -17,19 +17,41 @@ 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) { m_center = center; @@ -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 getCenterAngle() const { return m_center; } @@ -51,27 +75,45 @@ public: void setOffsetAngle(sf::Angle offset) { m_offset = offset; } std::optional 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& polygons); + private: std::vector m_light{}; - RayVector m_rays{}; + std::vector m_rays{}; std::optional m_center{}; std::optional 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& polygons); void constructPartialLight(const std::vector& polygons); + void createAndSortRays(const std::vector& polygons); + void createAndSortRays(const std::vector& polygons, sf::Angle lowerAngleBounds, sf::Angle upperAngleBounds);// for bounded light + void calculateIntersects(const std::vector& polygons); + bool withinBounds(sf::Vector2f point, sf::Vector2f lower, sf::Vector2f upper); }; diff --git a/Ray.h b/Ray.h index fb402bb..a5c16a8 100644 --- a/Ray.h +++ b/Ray.h @@ -46,21 +46,6 @@ public: void calculateIntersect(const std::vector& 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{}; diff --git a/Utility.hpp b/Utility.hpp index 6d6f4a3..7f6f782 100644 --- a/Utility.hpp +++ b/Utility.hpp @@ -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}; \ No newline at end of file diff --git a/imgui.ini b/imgui.ini index 1467fe0..98c0119 100644 --- a/imgui.ini +++ b/imgui.ini @@ -3,6 +3,6 @@ Pos=60,60 Size=400,400 [Window][Config] -Pos=44,11 -Size=708,467 +Pos=7,11 +Size=670,442 diff --git a/main.cpp b/main.cpp index f7964ba..829013f 100644 --- a/main.cpp +++ b/main.cpp @@ -15,18 +15,35 @@ auto window = sf::RenderWindow(sf::VideoMode({ 1920u, 1080u }), "Ray Casting Test"); - Light light; - RayVector rays; - std::vector polygons; +Light light; +std::vector 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& 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 (ImGui::SliderInt("number of rays", &numRays, 5, 500)) - { - rays.clear(); - + if (drawLight) { - 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.setFlags(Light::Flags::visibility); } + else + { + 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); + } + else + { + light.unsetFlags(Light::Flags::infiniteLength); + } } - ImGui::ColorEdit3("Light color", &lightColor.r); + + 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& polygons) +void userInput() { while (const std::optional event = window.pollEvent()) { @@ -162,7 +233,8 @@ void userInput(std::vector& polygons) switch (buttonPressed->scancode) { case sf::Keyboard::Scancode::C: - polygons.clear(); + polygons.clear(); + polygons.emplace_back(constructRectangle(center, center)); break; case sf::Keyboard::Scancode::R: @@ -212,58 +284,14 @@ void userInput(std::vector& 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();