diff --git a/.all-contributorsrc b/.all-contributorsrc index 02245a2..7b00059 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -48,6 +48,28 @@ "bug", "test" ] + }, + { + "login": "Dead-Deus", + "name": "Dead-Deus", + "avatar_url": "https://avatars.githubusercontent.com/u/89898198?v=4", + "profile": "https://github.com/Dead-Deus", + "contributions": [ + "infra", + "bug" + ] + }, + { + "login": "JonathSpirit", + "name": "GuillaumeG.", + "avatar_url": "https://avatars.githubusercontent.com/u/48102745?v=4", + "profile": "https://github.com/JonathSpirit", + "contributions": [ + "bug", + "test", + "code", + "ideas" + ] } ], "projectName": "Candle", diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1f2113 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Ignoring build dir +build/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dc14e3..593e7dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,15 +13,27 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY lib) # might need to set manually SFML_ROOT to the install location of SFML for Windows users # set(SFML_ROOT "") +if(SFML_ROOT STREQUAL "") + find_package(SFML 2.5 REQUIRED COMPONENTS graphics) +endif() # if the user wants to use static SFML libs # set(SFML_STATIC_LIBRARIES TRUE) -find_package(SFML 2.5 REQUIRED COMPONENTS graphics) - +set(CANDLE_HEADERS + include/Candle/LightingArea.hpp + include/Candle/LightSource.hpp + include/Candle/RadialLight.hpp + include/Candle/DirectedLight.hpp + include/Candle/geometry/Line.hpp + include/Candle/geometry/Polygon.hpp + include/Candle/geometry/Vector2.hpp + include/Candle/graphics/Color.hpp + include/Candle/graphics/VertexArray.hpp + include/Candle/Constants.hpp +) set(CANDLE_SRC - src/LightingArea.cpp src/LightSource.cpp src/RadialLight.cpp @@ -31,11 +43,10 @@ set(CANDLE_SRC src/Color.cpp src/VertexArray.cpp src/Constants.cpp - ) # Static library target -add_library(Candle-s STATIC ${CANDLE_SRC}) +add_library(Candle-s STATIC ${CANDLE_SRC} ${CANDLE_HEADERS}) target_include_directories(Candle-s PUBLIC include) target_link_libraries(Candle-s sfml-graphics) diff --git a/README.md b/README.md index 478eb1d..dc4b20a 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,9 @@ Thanks to the people that have contributed to this project: ([emoji key](https:/
Modar Nasser

πŸ› πŸš‡ πŸ“–
nightroy99

🌍 πŸ‘€
Lukas DΓΌrrenberger

πŸ› -
Tim Stoddard

πŸ’»πŸ› ⚠️ +
Tim Stoddard

πŸ’» πŸ› ⚠️ +
Dead-Deus

πŸš‡ πŸ› +
GuillaumeG.

πŸ› ⚠️ πŸ’» πŸ€” diff --git a/include/Candle/RadialLight.hpp b/include/Candle/RadialLight.hpp index e7ab17b..f3384ce 100644 --- a/include/Candle/RadialLight.hpp +++ b/include/Candle/RadialLight.hpp @@ -13,15 +13,15 @@ namespace candle{ /** * @brief LightSource that emits light from a single point * @details - * + * * A RadialLight is defined, mainly, by the position, the orientation, the * range of the light and the beam angle. To manipulate the * position and the orientation of the light, you can change the position * and rotation of the object as you would do with any sf::Transformable. * The range can be manipulated as with other LightSources, with - * @ref LightSource::setRange, and the angle of the beam with + * @ref LightSource::setRange, and the angle of the beam with * @ref setBeamAngle. - * + * * * * @@ -33,23 +33,23 @@ namespace candle{ private: static int s_instanceCount; float m_beamAngle; - + void draw(sf::RenderTarget& t, sf::RenderStates st) const override; void resetColor() override; - + public: /** * @brief Constructor */ RadialLight(); - + /** * @brief Destructor */ virtual ~RadialLight(); - + void castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end) override; - + /** * @brief Set the range for which rays may be casted. * @details The angle shall be specified in degrees. The angle in which the rays will be casted will be @@ -58,14 +58,26 @@ namespace candle{ * @see getBeamAngle */ void setBeamAngle(float angle); - + /** * @brief Get the range for which rays may be casted. - * @details It defaults to 360ΒΊ. + * @details It defaults to 360ΒΊ. * @see setBeamAngle */ - float getBeamAngle() const; - + float getBeamAngle() const; + + /** + * @brief Get the local bounding rectangle of the light. + * @returns The local bounding rectangle in float. + */ + sf::FloatRect getLocalBounds() const; + + /** + * @brief Get the global bounding rectangle of the light. + * @returns The global bounding rectangle in float. + */ + sf::FloatRect getGlobalBounds() const; + }; } diff --git a/include/Candle/geometry/Line.hpp b/include/Candle/geometry/Line.hpp index b4eaaf9..daeb2c7 100644 --- a/include/Candle/geometry/Line.hpp +++ b/include/Candle/geometry/Line.hpp @@ -10,6 +10,7 @@ #include #include +#include #include "Candle/geometry/Vector2.hpp" #include "Candle/Constants.hpp" @@ -29,7 +30,7 @@ namespace sfu{ }; sf::Vector2f m_origin; ///< Origin point of the line. sf::Vector2f m_direction; ///< Direction vector (not necessarily normalized) - + /** * @brief Construct a line that passes through @p p1 and @p p2 * @details The direction is interpreted as p2 - p1. @@ -37,7 +38,7 @@ namespace sfu{ * @param p2 Second point */ Line(const sf::Vector2f& p1, const sf::Vector2f& p2); - + /** * @brief Construct a line defined by a point and an angle. * @details The direction is interpreted as {cos(angle), sin(angle)}. @@ -45,7 +46,16 @@ namespace sfu{ * @param angle Angle defining the line */ Line(const sf::Vector2f& p, float angle); - + + /** + * @brief Get the global bounding rectangle of the line. + * @details The returned rectangle is in global coordinates, which + * means that it takes into account the transformations (translation, + * rotation, scale, ...) (see SFML). + * @returns Global bounding rectangle in float + */ + sf::FloatRect getGlobalBounds() const; + /** * @brief Return the relative position of a point to the line. * @details If the point is to the right of the direction vector, the @@ -55,21 +65,21 @@ namespace sfu{ * @returns -1, 0 or 1 */ int relativePosition(const sf::Vector2f& point) const; - + /** * @brief Return the minimum distance of a point to the line. * @param point * @returns The minimum distance of a point to the line. */ float distance(const sf::Vector2f& point) const; - + /** * @brief Returns the relative position of one line to another. * @param line * @returns The relative position of the two lines. */ LineRelativePosition intersection(const Line& line) const; - + /** * @brief Returns the relative position of one line to another. * @details If the relative position is SECANT, the output argument @@ -81,12 +91,12 @@ namespace sfu{ * @see point */ LineRelativePosition intersection(const Line& line, float& t) const; - + /** * @brief Returns the relative position of one line to another. * @details If the relative position is SECANT, the output argument * @p t1 contains the parameter required to get the intersection - * point with **this** line and @p t2, the parameter required to + * point with **this** line and @p t2, the parameter required to * get it with @p line. * @param line * @param t1 (Output argument) @@ -95,7 +105,7 @@ namespace sfu{ * @see point */ LineRelativePosition intersection(const Line& line, float& t1, float& t2) const; - + /** * @brief Get a point of the line. * @details The point is obtained is m_origin + param * m_direction @@ -103,20 +113,20 @@ namespace sfu{ * @returns A point of the line */ sf::Vector2f point(float param) const; - + }; - + /** * @brief Cast a ray against a set of segments. - * @details Use a line as a ray, casted from its - * [origin](@ref sfu::Line::m_origin) in its + * @details Use a line as a ray, casted from its + * [origin](@ref sfu::Line::m_origin) in its * [direction](@ref sfu::Line::m_direction). It is intersected with * a set * of segments, represented as [Lines](sfu::Line) too, and the one closest * to the cast point is returned. - * + * * Segments are interpreted to be delimited by the @p ray.m_origin and * @p ray.point(1). - * + * * @param begin Iterator to the first ray. * @param end Iterator to the last ray. * @param ray @@ -126,7 +136,7 @@ namespace sfu{ template sf::Vector2f castRay(const Iterator& begin, const Iterator& end, - Line ray, + Line ray, float maxRange=std::numeric_limits::infinity()){ float minRange = maxRange; ray.m_direction = sfu::normalize(ray.m_direction); diff --git a/src/Line.cpp b/src/Line.cpp index 70a6d5c..a9f22ce 100644 --- a/src/Line.cpp +++ b/src/Line.cpp @@ -7,7 +7,7 @@ namespace sfu{ Line::Line(const sf::Vector2f& p1, const sf::Vector2f& p2): m_origin(p1), m_direction(p2 - p1){} - + Line::Line(const sf::Vector2f& p, float angle): m_origin(p) { @@ -17,13 +17,26 @@ namespace sfu{ ang -= sfu::PI; m_direction = {std::cos(ang), std::sin(ang)}; } - - + + sf::FloatRect Line::getGlobalBounds() const{ + const sf::Vector2f& point1 = m_origin; + sf::Vector2f point2 = m_direction + m_origin; + + //Make sure that the rectangle begin from the upper left corner + sf::FloatRect rect; + rect.left = (point1.x < point2.x) ? point1.x : point2.x; + rect.top = (point1.y < point2.y) ? point1.y : point2.y; + rect.width = std::abs(m_direction.x) + 1.0f; //The +1 is here to avoid having a width of zero + rect.height = std::abs(m_direction.y) + 1.0f; //(SFML doesn't like 0 in rect) + + return rect; + } + int Line::relativePosition(const sf::Vector2f& point) const{ float f = (point.x-m_origin.x) / m_direction.x - (point.y-m_origin.y) / m_direction.y; return (0.f < f) - (f < 0.f); } - + float Line::distance(const sf::Vector2f& point) const{ float d; if(m_direction.x == 0){ @@ -38,7 +51,7 @@ namespace sfu{ } return d; } - + Line::LineRelativePosition Line::intersection(const Line& l) const{ float t1, t2; return intersection(l,t1,t2); @@ -52,7 +65,7 @@ namespace sfu{ auto& v = m_direction; auto& b = l.m_origin; auto& w = l.m_direction; - + float th = angle(v, w); if(th < 0.001f || th > 359.99f){ if(relativePosition(a) == 0){ @@ -61,7 +74,7 @@ namespace sfu{ return PARALLEL; } } - + if(std::abs(w.y) < 0.001f){ t1 = (b.y-a.y) / v.y; t2 = (a.x + t1*v.x - b.x) / w.x; @@ -69,7 +82,7 @@ namespace sfu{ t1 = (w.y * (b.x-a.x) + w.x * (a.y-b.y)) / (v.x*w.y - v.y*w.x); t2 = (t1*v.y + a.y - b.y) / w.y; } - + return SECANT; } sf::Vector2f Line::point(float param) const{ diff --git a/src/RadialLight.cpp b/src/RadialLight.cpp index 9cfa737..bbefad6 100644 --- a/src/RadialLight.cpp +++ b/src/RadialLight.cpp @@ -17,18 +17,18 @@ namespace candle{ bool l_texturesReady(false); std::unique_ptr l_lightTextureFade; std::unique_ptr l_lightTexturePlain; - + void initializeTextures(){ #ifdef CANDLE_DEBUG std::cout << "RadialLight: InitializeTextures" << std::endl; #endif int points = 100; - + l_lightTextureFade.reset(new sf::RenderTexture); l_lightTexturePlain.reset(new sf::RenderTexture); l_lightTextureFade->create(BASE_RADIUS*2 + 2, BASE_RADIUS*2 + 2); l_lightTexturePlain->create(BASE_RADIUS*2 + 2, BASE_RADIUS*2 + 2); - + sf::VertexArray lightShape(sf::TriangleFan, points+2); float step = sfu::PI*2.f/points; lightShape[0].position = {BASE_RADIUS + 1, BASE_RADIUS + 1}; @@ -43,20 +43,20 @@ namespace candle{ l_lightTextureFade->draw(lightShape); l_lightTextureFade->display(); l_lightTextureFade->setSmooth(true); - + sfu::setColor(lightShape, sf::Color::White); l_lightTexturePlain->clear(sf::Color::Transparent); l_lightTexturePlain->draw(lightShape); l_lightTexturePlain->display(); l_lightTexturePlain->setSmooth(true); } - + float module360(float x){ x = (float)fmod(x,360.f); if(x < 0.f) x += 360.f; return x; } - + RadialLight::RadialLight() : LightSource() { @@ -67,17 +67,17 @@ namespace candle{ } m_polygon.setPrimitiveType(sf::TriangleFan); m_polygon.resize(6); - m_polygon[0].position = + m_polygon[0].position = m_polygon[0].texCoords = {BASE_RADIUS+1, BASE_RADIUS+1}; - m_polygon[1].position = + m_polygon[1].position = m_polygon[5].position = - m_polygon[1].texCoords = + m_polygon[1].texCoords = m_polygon[5].texCoords = {0.f, 0.f}; - m_polygon[2].position = + m_polygon[2].position = m_polygon[2].texCoords = {BASE_RADIUS*2 + 2, 0.f}; - m_polygon[3].position = + m_polygon[3].position = m_polygon[3].texCoords = {BASE_RADIUS*2 + 2, BASE_RADIUS*2 + 2}; - m_polygon[4].position = + m_polygon[4].position = m_polygon[4].texCoords = {0.f, BASE_RADIUS*2 + 2}; Transformable::setOrigin(BASE_RADIUS, BASE_RADIUS); setRange(1.0f); @@ -85,7 +85,7 @@ namespace candle{ // castLight(); s_instanceCount++; } - + RadialLight::~RadialLight(){ s_instanceCount--; #ifdef RADIAL_LIGHT_FIX @@ -102,7 +102,7 @@ namespace candle{ } #endif } - + void RadialLight::draw(sf::RenderTarget& t, sf::RenderStates s) const{ sf::Transform trm = Transformable::getTransform(); trm.scale(m_range/BASE_RADIUS, m_range/BASE_RADIUS, BASE_RADIUS, BASE_RADIUS); @@ -121,49 +121,59 @@ namespace candle{ void RadialLight::resetColor(){ sfu::setColor(m_polygon, m_color); } - + void RadialLight::setBeamAngle(float r){ m_beamAngle = module360(r); } - + float RadialLight::getBeamAngle() const{ return m_beamAngle; + } + + sf::FloatRect RadialLight::getLocalBounds() const{ + return sf::FloatRect(0.0f, 0.0f, m_range*4, m_range*4); + } + + sf::FloatRect RadialLight::getGlobalBounds() const{ + float scaledRange = m_range / BASE_RADIUS; + sf::Transform trm = Transformable::getTransform(); + trm.scale(scaledRange, scaledRange, BASE_RADIUS, BASE_RADIUS); + return trm.transformRect( getLocalBounds() ); } - + void RadialLight::castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end){ float scaledRange = m_range / BASE_RADIUS; sf::Transform trm = Transformable::getTransform(); trm.scale(scaledRange, scaledRange, BASE_RADIUS, BASE_RADIUS); std::vector rays; - + rays.reserve(2 + std::distance(begin, end) * 2 * 3); // 2: beam angle, 4: corners, 2: pnts/sgmnt, 3 rays/pnt - + // Start casting float bl1 = module360(getRotation() - m_beamAngle/2); float bl2 = module360(getRotation() + m_beamAngle/2); bool beamAngleBigEnough = m_beamAngle < 0.1f; auto castPoint = Transformable::getPosition(); float off = .001f; - + auto angleInBeam = [&](float a)-> bool { - return beamAngleBigEnough + return beamAngleBigEnough ||(bl1 < bl2 && a > bl1 && a < bl2) ||(bl1 > bl2 && (a > bl1 || a < bl2)); }; - + for(float a = 45.f; a < 360.f; a += 90.f){ if(beamAngleBigEnough || angleInBeam(a)){ rays.emplace_back(castPoint, a); } } - + + sf::FloatRect lightBounds = getGlobalBounds(); for(auto it = begin; it != end; it++){ auto& s = *it; - float d1 = s.distance(castPoint); - // float d2 = sfu::magnitude(s.m_origin - castPoint); - // float d3 = sfu::magnitude(s.point(1.f) - castPoint); - // if(std::max(std::min(d2, d3), d1) <= m_range*std::sqrt(2)){ - if(d1 <= m_range){ + + //Only cast a ray if the line is in range + if( lightBounds.intersects( s.getGlobalBounds() ) ){ sfu::Line r1(castPoint, s.m_origin); sfu::Line r2(castPoint, s.point(1.f)); float a1 = sfu::angle(r1.m_direction); @@ -180,7 +190,7 @@ namespace candle{ } } } - + if(bl1 > bl2){ std::sort( rays.begin(), @@ -198,7 +208,7 @@ namespace candle{ rays.begin(), rays.end(), [bl1] (sfu::Line& r1, sfu::Line& r2){ - return + return sfu::angle(r1.m_direction) < sfu::angle(r2.m_direction); } ); @@ -207,7 +217,7 @@ namespace candle{ rays.emplace(rays.begin(), castPoint, bl1); rays.emplace_back(castPoint, bl2); } - + sf::Transform tr_i = trm.getInverse(); // keep only the ones within the area std::vector points; @@ -225,7 +235,7 @@ namespace candle{ sf::Vector2f al2(std::cos(bl2rad), std::sin(bl2rad)); int d_n = points.size()*2 + 4; m_debug.resize(d_n); - m_debug[d_n-1].color = m_debug[d_n-2].color = sf::Color::Cyan; + m_debug[d_n-1].color = m_debug[d_n-2].color = sf::Color::Cyan; m_debug[d_n-3].color = m_debug[d_n-4].color = sf::Color::Yellow; m_debug[d_n-1].position = m_debug[d_n-3].position = m_polygon[0].position; m_debug[d_n-2].position = tr_i.transformPoint(castPoint + m_range * al1); @@ -240,7 +250,7 @@ namespace candle{ m_debug[i*2].position = m_polygon[0].position; m_debug[i*2+1].position = p; m_debug[i*2].color = m_debug[i*2+1].color = sf::Color::Magenta; -#endif +#endif } if(beamAngleBigEnough){ m_polygon[points.size()+1] = m_polygon[1];

Variables schema