diff --git a/include/Candle/DirectedLight.hpp b/include/Candle/DirectedLight.hpp index b8ec030..c08951a 100644 --- a/include/Candle/DirectedLight.hpp +++ b/include/Candle/DirectedLight.hpp @@ -12,51 +12,54 @@ namespace candle{ /** * @brief LightSource that emits light in a single direction. - * @details - * - * A DirectedLight is defined, mainly, by the direction of the rays, the + * @details + * + * A DirectedLight is defined, mainly, by the direction of the rays, the * position of the source, the beam width and the range of the light. You - * can manipulate the first two changing the rotation and + * can manipulate the first two changing the rotation and * position as you would with any sf::Transformable. The range can * be manipulated as with other LightSources, with * @ref LightSource::setRange, and the beam width with @ref setBeamWidth. - * + * * * * * * *

Variables diagram

Demo example
- * + * */ class DirectedLight: public LightSource{ private: - float m_beamWidth; - + float m_beamWidth; + sf::Texture* m_texture; + void draw(sf::RenderTarget& t, sf::RenderStates st) const override; void resetColor() override; public: DirectedLight(); - - void castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end) override; - + + void castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end) override; + + void setTexture(sf::Texture* texture) override; + /** * @brief Set the width of the beam. - * @details The width specifies the maximum distance allowed from the + * @details The width specifies the maximum distance allowed from the * center of the segment to cast a ray, along a segment normal to the * light direction. * @param width Width of the beam. * @see getBeamWidth */ void setBeamWidth(float width); - + /** * @brief Get the width of the beam. * @returns Width of the beam * @see setBeamWidth */ float getBeamWidth() const; - + }; } diff --git a/include/Candle/LightSource.hpp b/include/Candle/LightSource.hpp index 7d0008f..b202dd8 100644 --- a/include/Candle/LightSource.hpp +++ b/include/Candle/LightSource.hpp @@ -20,37 +20,42 @@ namespace candle{ * @brief Typedef with mere semantic purposes. */ typedef sfu::Line Edge; - + /** * @typedef EdgeVector * @brief Typedef to shorten the use of vectors as edge pools */ typedef std::vector EdgeVector; - + /** * @brief This function initializes the Texture used for the RadialLights. * @details This function is called the first time a RadialLight is created - * , so the user shouldn't need to do it. Anyways, it could be - * necessary to do it explicitly if you declare a RadialLight that, for + * , so the user shouldn't need to do it. Anyways, it could be + * necessary to do it explicitly if you declare a RadialLight that, for * some reason, is global or static RadialLight and is not constructed in * a normal order. */ - void initializeTextures(); - + void initializeTextures(); + + /** + * @brief This function cleanup texture created by RadialLights. + */ + void cleanupTexture(sf::Texture* texture); + /** * @brief Interface for objects that emit light * @details - * + * * LightSources use raycasting algorithms to compute the polygon - * illuminated by the light. The main difference between the + * illuminated by the light. The main difference between the * implementations, @ref RadialLight and @ref DirectedLight, is whether * the constant is the origin or the direction of the rays. - * + * * LightSources manage their colour separating the alpha value from the RGB - * . This is convenient to manipulate color of the light (interpreted as - * the RGB value) and intensity (interpreted as the alpha value) + * . This is convenient to manipulate color of the light (interpreted as + * the RGB value) and intensity (interpreted as the alpha value) * separately. - * + * * By default, they use a sf::BlendAdd mode. This means that you can * specify any other blend mode you want, except sf::BlendAlpha, that * will be changed to the additive mode. @@ -61,7 +66,7 @@ namespace candle{ * @brief Draw the object to a target */ virtual void draw(sf::RenderTarget& t, sf::RenderStates st) const = 0; - + protected: sf::Color m_color; sf::VertexArray m_polygon; @@ -69,50 +74,50 @@ namespace candle{ float m_intensity; // only for fog bool m_fade; -#ifdef CANDLE_DEBUG +#ifdef CANDLE_DEBUG sf::VertexArray m_debug; #endif - + virtual void resetColor() = 0; - + public: /** * @brief Constructor */ LightSource(); - + /** * @brief Set the light intensity. - * @details The @p intensity of the light determines two things: - * how much fog opacity it reduces when drawn in a LightingArea * in + * @details The @p intensity of the light determines two things: + * how much fog opacity it reduces when drawn in a LightingArea * in * FOG mode, and how much presence its color has when drawn normally. - * + * * The default value is 1. - * + * * @param intensity Value from 0 to 1. At 0 the light is * invisible. * @see getIntensity */ void setIntensity(float intensity); - + /** * @brief Get the intensity of the light. * @returns The light intensity. * @see setIntensity */ float getIntensity() const; - + /** * @brief Set the light color. * @details The light color refers only to the RGB values. - * + * * The default value is sf::Color::White - * + * * @param color New color of the light. The alpha value is ignored. * @see getColor */ void setColor(const sf::Color& color); - + /** * @brief Get the plain color of the light. * @details The light color refers only to the RGB values. @@ -120,27 +125,33 @@ namespace candle{ * @see setColor */ sf::Color getColor() const; - + /** * @brief Set the value of the _fade_ flag. * @details when the @p fade flag is set, the light will lose intensity * in the limits of its range. Otherwise, the intensity will remain * constant. - * + * * The default value is true. - * + * * @param fade Value to set the flag. * @see getFade */ virtual void setFade(bool fade); - + /** * @brief Check if the light fades or not. * @returns The value of the _fade_ flag. * @see setFade */ - virtual bool getFade() const; - + virtual bool getFade() const; + + /** + * @brief Set the light texture (nullptr = default texture). + * @details The default texture is generated internally. + */ + virtual void setTexture(sf::Texture* texture) = 0; + /** * @brief Set the range of the illuminated area. * @details The range of the light indicates the how far a light ray @@ -149,21 +160,21 @@ namespace candle{ * @see getRange, setFade */ void setRange(float range); - + /** * @brief Get the range of the illuminated area. * @returns The range of the illuminated area. * @see setRange */ float getRange() const; - + /** - * @brief Modify the polygon of the illuminated area with a + * @brief Modify the polygon of the illuminated area with a * raycasting algorithm. - * @details The algorithm needs to know which edges to use to cast + * @details The algorithm needs to know which edges to use to cast * shadows. They are specified within a range of two iterators of a * vector of edges of type @ref sfu::Line. - * @param begin Iterator to the first sfu::Line of the vector to take + * @param begin Iterator to the first sfu::Line of the vector to take * into account. * @param end Iterator to the first sfu::Line of the vector not to be * taken into account. diff --git a/include/Candle/RadialLight.hpp b/include/Candle/RadialLight.hpp index f3384ce..34208d5 100644 --- a/include/Candle/RadialLight.hpp +++ b/include/Candle/RadialLight.hpp @@ -32,7 +32,11 @@ namespace candle{ class RadialLight: public LightSource{ private: static int s_instanceCount; - float m_beamAngle; + float m_beamAngle; + unsigned int m_localRadius; + + sf::Texture* m_lightTextureFade; + sf::Texture* m_lightTexturePlain; void draw(sf::RenderTarget& t, sf::RenderStates st) const override; void resetColor() override; @@ -48,7 +52,9 @@ namespace candle{ */ virtual ~RadialLight(); - void castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end) override; + void castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end) override; + + void setTexture(sf::Texture* texture) override; /** * @brief Set the range for which rays may be casted. @@ -66,6 +72,20 @@ namespace candle{ */ float getBeamAngle() const; + /** + * @brief Get the light fade texture. + * @returns The actual light fade texture. + * @see setLightFadeTexture + */ + sf::Texture* getLightFadeTexture(); + + /** + * @brief Get the light plain texture. + * @returns The actual light plain texture. + * @see setLightPlainTexture + */ + sf::Texture* getLightPlainTexture(); + /** * @brief Get the local bounding rectangle of the light. * @returns The local bounding rectangle in float. diff --git a/src/DirectedLight.cpp b/src/DirectedLight.cpp index 54f0faa..2108829 100644 --- a/src/DirectedLight.cpp +++ b/src/DirectedLight.cpp @@ -11,7 +11,8 @@ namespace candle{ st.transform *= Transformable::getTransform(); if(st.blendMode == sf::BlendAlpha){ // the default st.blendMode = sf::BlendAdd; - } + } + st.texture = m_texture; t.draw(m_polygon, st); #ifdef CANDLE_DEBUG sf::RenderStates deb_s; @@ -44,7 +45,8 @@ namespace candle{ DirectedLight::DirectedLight(){ m_polygon.setPrimitiveType(sf::Quads); m_polygon.resize(2); - setBeamWidth(10.f); + setBeamWidth(10.f); + m_texture = nullptr; // castLight(); } @@ -54,6 +56,10 @@ namespace candle{ float DirectedLight::getBeamWidth() const{ return m_beamWidth; + } + + void DirectedLight::setTexture(sf::Texture* texture){ + m_texture = texture; } struct LineParam: public sfu::Line{ diff --git a/src/LightSource.cpp b/src/LightSource.cpp index d5c604f..c795077 100644 --- a/src/LightSource.cpp +++ b/src/LightSource.cpp @@ -15,41 +15,41 @@ namespace candle{ , m_debug(sf::Lines, 0) #endif {} - + void LightSource::setIntensity(float intensity){ m_color.a = 255 * intensity; resetColor(); } - + float LightSource::getIntensity() const{ return (float)m_color.a/255.f; } - + void LightSource::setColor(const sf::Color& c){ m_color = {c.r, c.g, c.b, m_color.a}; resetColor(); } - + sf::Color LightSource::getColor() const{ const sf::Color &c = m_color; return {c.r, c.g, c.b, 255}; } - + void LightSource::setFade(bool fade){ m_fade = fade; resetColor(); } - + bool LightSource::getFade() const{ return m_fade; } - + void LightSource::setRange(float r){ m_range = r; } - + float LightSource::getRange() const{ return m_range; } - + } diff --git a/src/RadialLight.cpp b/src/RadialLight.cpp index ff8ea4a..e587cf3 100644 --- a/src/RadialLight.cpp +++ b/src/RadialLight.cpp @@ -1,7 +1,8 @@ #ifdef CANDLE_DEBUG #include #endif - + +#include #include #include "Candle/RadialLight.hpp" @@ -13,42 +14,63 @@ namespace candle{ int RadialLight::s_instanceCount = 0; - const float BASE_RADIUS = 400.0f; - bool l_texturesReady(false); - std::unique_ptr l_lightTextureFade; - std::unique_ptr l_lightTexturePlain; + const unsigned int BASE_RADIUS = 400; + bool l_texturesReady(false); + std::map, std::unique_ptr >> l_lightTexturesCache; + 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}; - for(int i = 1; i < points+2; i++){ - lightShape[i].position = { - (std::sin(step*(i)) + 1) * BASE_RADIUS + 1, - (std::cos(step*(i)) + 1) * BASE_RADIUS + 1 - }; - lightShape[i].color.a = 0; - } - l_lightTextureFade->clear(sf::Color::Transparent); - 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(); + + sf::Image lightImageFade; + sf::Image lightImagePlain; + + //We create an image of the radius*2 + an 2 more pixels + lightImageFade.create(BASE_RADIUS*2+2, BASE_RADIUS*2+2, sf::Color(255,255,255,0)); + lightImagePlain.create(BASE_RADIUS*2+2, BASE_RADIUS*2+2, sf::Color(255,255,255,0)); + + sf::Vector2f center = {BASE_RADIUS, BASE_RADIUS}; + + for (unsigned int y=1; y(255.0f*distanceFromCenter))); + lightImagePlain.setPixel(x,y, sf::Color(255,255,255,255)); + } + } + } + + l_lightTextureFade.reset( new sf::Texture() ); + l_lightTextureFade->loadFromImage(lightImageFade); + l_lightTextureFade->setSmooth(true); + + l_lightTexturePlain.reset( new sf::Texture() ); + l_lightTexturePlain->loadFromImage(lightImagePlain); l_lightTexturePlain->setSmooth(true); + } + + void cleanupTexture(sf::Texture* texture){ + if (texture != nullptr){ + //Custom texture + auto cachedTextureIter = l_lightTexturesCache.find(texture); + + if (cachedTextureIter != l_lightTexturesCache.end()){ + l_lightTexturesCache.erase(cachedTextureIter); + } + }else{ + //Every texture + l_lightTexturesCache.clear(); + } } float module360(float x){ @@ -64,26 +86,16 @@ namespace candle{ // The first time we create a RadialLight, we must create the textures initializeTextures(); l_texturesReady = true; - } - m_polygon.setPrimitiveType(sf::TriangleFan); - m_polygon.resize(6); - m_polygon[0].position = - m_polygon[0].texCoords = {BASE_RADIUS+1, BASE_RADIUS+1}; - m_polygon[1].position = - m_polygon[5].position = - m_polygon[1].texCoords = - m_polygon[5].texCoords = {0.f, 0.f}; - m_polygon[2].position = - m_polygon[2].texCoords = {BASE_RADIUS*2 + 2, 0.f}; - m_polygon[3].position = - m_polygon[3].texCoords = {BASE_RADIUS*2 + 2, BASE_RADIUS*2 + 2}; - m_polygon[4].position = - m_polygon[4].texCoords = {0.f, BASE_RADIUS*2 + 2}; - Transformable::setOrigin(BASE_RADIUS, BASE_RADIUS); + } + + setTexture(nullptr); + setRange(1.0f); setBeamAngle(360.f); // castLight(); - s_instanceCount++; + s_instanceCount++; + + setColor(sf::Color(255,255,255,255)); } RadialLight::~RadialLight(){ @@ -105,9 +117,9 @@ namespace candle{ 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); + trm.scale(m_range/m_localRadius, m_range/m_localRadius, m_localRadius, m_localRadius); s.transform *= trm; - s.texture = m_fade ? &l_lightTextureFade->getTexture() : &l_lightTexturePlain->getTexture(); + s.texture = m_fade ? m_lightTextureFade : m_lightTexturePlain; if(s.blendMode == sf::BlendAlpha){ s.blendMode = sf::BlendAdd; } @@ -130,21 +142,108 @@ namespace candle{ return m_beamAngle; } + void RadialLight::setTexture(sf::Texture* texture){ + if (texture != nullptr){ + //Custom texture + m_lightTexturePlain = texture; + auto cachedTextureIter = l_lightTexturesCache.find(texture); + + if (cachedTextureIter != l_lightTexturesCache.end()){ + m_lightTexturePlain = cachedTextureIter->second.first.get(); + m_lightTextureFade = cachedTextureIter->second.second.get(); + }else{ + sf::Image userTexture = texture->copyToImage(); + + unsigned int diameter = ((userTexture.getSize().x > userTexture.getSize().y) ? userTexture.getSize().x : userTexture.getSize().y) + 2; + + //Put 1px transparent border to avoid bad clamp + sf::Image plainImage; + sf::Image fadeImage; + fadeImage.create(diameter, diameter, sf::Color(255,255,255,0)); + plainImage.create(diameter, diameter, sf::Color(255,255,255,0)); + + sf::Vector2f center = sf::Vector2f(diameter/2.0f, diameter/2.0f); + + for (unsigned int y=0; y(255.0f*distanceFromCenter); + fadeImage.setPixel(x+1,y+1, pixel); + } + } + } + + sf::Texture* plainTexture = new sf::Texture(); + sf::Texture* fadeTexture = new sf::Texture(); + plainTexture->loadFromImage(plainImage); + fadeTexture->loadFromImage(fadeImage); + plainTexture->setSmooth(true); + fadeTexture->setSmooth(true); + + l_lightTexturesCache[texture] = std::make_pair(std::unique_ptr(plainTexture), std::unique_ptr(fadeTexture)); + + m_lightTexturePlain = plainTexture; + m_lightTextureFade = fadeTexture; + } + }else{ + //Default texture + m_lightTexturePlain = l_lightTexturePlain.get(); + m_lightTextureFade = l_lightTextureFade.get(); + } + + m_localRadius = m_lightTexturePlain->getSize().x/2+1; + + m_polygon.setPrimitiveType(sf::TriangleFan); + m_polygon.resize(6); + m_polygon[0].position = + m_polygon[0].texCoords = {static_cast(m_localRadius), static_cast(m_localRadius)}; + m_polygon[1].position = + m_polygon[5].position = + m_polygon[1].texCoords = + m_polygon[5].texCoords = {0.f, 0.f}; + m_polygon[2].position = + m_polygon[2].texCoords = {static_cast(m_localRadius)*2, 0.f}; + m_polygon[3].position = + m_polygon[3].texCoords = {static_cast(m_localRadius)*2, static_cast(m_localRadius)*2}; + m_polygon[4].position = + m_polygon[4].texCoords = {0.f, static_cast(m_localRadius)*2}; + Transformable::setOrigin(m_localRadius, m_localRadius); + } + + sf::Texture* RadialLight::getLightFadeTexture(){ + return m_lightTextureFade; + } + + sf::Texture* RadialLight::getLightPlainTexture(){ + return m_lightTexturePlain; + } + sf::FloatRect RadialLight::getLocalBounds() const{ - return sf::FloatRect(0.0f, 0.0f, BASE_RADIUS*2, BASE_RADIUS*2); + return sf::FloatRect(0.0f, 0.0f, m_localRadius*2, m_localRadius*2); } sf::FloatRect RadialLight::getGlobalBounds() const{ - float scaledRange = m_range / BASE_RADIUS; + float scaledRange = m_range / m_localRadius; sf::Transform trm = Transformable::getTransform(); - trm.scale(scaledRange, scaledRange, BASE_RADIUS, BASE_RADIUS); + trm.scale(scaledRange, scaledRange, m_localRadius, m_localRadius); return trm.transformRect( getLocalBounds() ); } void RadialLight::castLight(const EdgeVector::iterator& begin, const EdgeVector::iterator& end){ - float scaledRange = m_range / BASE_RADIUS; + float scaledRange = m_range / m_localRadius; sf::Transform trm = Transformable::getTransform(); - trm.scale(scaledRange, scaledRange, BASE_RADIUS, BASE_RADIUS); + trm.scale(scaledRange, scaledRange, m_localRadius, m_localRadius); std::vector rays; rays.reserve(2 + std::distance(begin, end) * 2 * 3); // 2: beam angle, 4: corners, 2: pnts/sgmnt, 3 rays/pnt @@ -166,7 +265,7 @@ namespace candle{ if(beamAngleBigEnough || angleInBeam(a)){ rays.emplace_back(castPoint, a); } - } + } sf::FloatRect lightBounds = getGlobalBounds(); for(auto it = begin; it != end; it++){ @@ -223,7 +322,7 @@ namespace candle{ std::vector points; points.reserve(rays.size()); for (auto& r: rays){ - points.push_back(tr_i.transformPoint(castRay(begin, end, r, m_range*m_range))); + points.push_back(tr_i.transformPoint(castRay(begin, end, r, m_range*2))); } m_polygon.resize(points.size() + 1 + beamAngleBigEnough); // + center and last m_polygon[0].color = m_color;