Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Lightning  (Read 5504 times)

0 Members and 1 Guest are viewing this topic.

PeterWelzien

  • Newbie
  • *
  • Posts: 38
    • View Profile
Lightning
« on: September 13, 2010, 09:50:30 pm »
I'm going to need a force field in future project and since I suck at making graphics, I decided to make a simple class that draws lightning (not a lightning rod  :))

I thought I'd share the code incase someone else need some lightning.

Code: [Select]
#include <SFML/Graphics.hpp>
#include <vector>

class Lightning : public sf::Drawable
{
  public:
               Lightning(unsigned long noSegments, float thickness = 1.0f, sf::Color fadeColor = sf::Color(0, 0, 0))
                 : noSegments(noSegments), thickness(thickness), fadeColor(fadeColor)                                {segments.reserve(noSegments);}
  void         generate ();
  void         setSize  (float width, float height)                                                                  {size.x = width; size.y = height;}
  virtual void Render   (sf::RenderTarget& target, sf::Renderer& renderer) const;

  private:
  unsigned long      noSegments;
  float              thickness;
  sf::Color          fadeColor; // Outer color of the lightning
  std::vector<float> segments;  // Contains the x-position of each segment (-1.0 - +1.0), multiply with width to get final position
  sf::Vector2f       size;      // Size of the lightning
};

void Lightning::generate()
{
  sf::Vector2f start;
  sf::Vector2f end(0.0f, 0.0f);

  segments.clear();

  segments.push_back(0.0f); // The start point of the lightning is centered

  for(unsigned long c = 1; c < noSegments - 1; ++c)
    segments.push_back(sf::Randomizer::Random(-1.0f, 1.0f));

  segments.push_back(0.0f); // The end point of the lightning is also centered
}

void Lightning::Render(sf::RenderTarget& target, sf::Renderer& renderer) const
{
  sf::Vector2f start, end;
  sf::Color color, colorDifference, colorStep;

  // If the thickness is 1.0 then draw using only the main color. Otherwise calculate the amount to fade the fade color each iteration
  if(thickness == 1.0f)
    color = GetColor();
  else
  {
    color = fadeColor;
 
    // sf::Color doesn't provide the minus or division operators, so we'll have to make the calculations component by component
    colorDifference = sf::Color(GetColor().r - fadeColor.r,
                                GetColor().g - fadeColor.g,
                                GetColor().b - fadeColor.b,
                                GetColor().a - fadeColor.a);
    colorStep = sf::Color(colorDifference.r / static_cast<sf::Uint8> (thickness - 1.0f),
                          colorDifference.g / static_cast<sf::Uint8> (thickness - 1.0f),
                          colorDifference.b / static_cast<sf::Uint8> (thickness - 1.0f),
                          colorDifference.a / static_cast<sf::Uint8> (thickness - 1.0f)); // Increase the fade color by this each iteration.
  }

  float segmentHeight = size.y / (noSegments - 1.0f);
  float halfWidth = size.x / 2;
  float halfThickness = thickness / 2;

  // Draw multiple lightnings, decreasing the thinkness by 1.0 until we reach thickness 1.0 (or less)
  for(float t = thickness; t >= 1.0f; --t, color += colorStep)
    for(unsigned long c = 0; c < noSegments - 1; ++c)
    {
      start.x = segments[c]     * (halfWidth - halfThickness) + halfWidth ; start.y = segmentHeight * c;
        end.x = segments[c + 1] * (halfWidth - halfThickness)  + halfWidth;   end.y = segmentHeight * (c + 1);
      sf::Shape line = sf::Shape::Line(start, end, t, color);
      target.Draw(line);
    }
}

int main()
{
  sf::RenderWindow window(sf::VideoMode(800, 600), "Lightning test");
  sf::Event evnt;
  bool paused=false;
  Lightning lightning(15, 3.0f, sf::Color::Blue);
  lightning.SetPosition(0.0f, 0.0f);
  lightning.setSize(15.0f, 150.0f);
  lightning.SetColor(sf::Color::White);

  while(window.IsOpened())
  {
    while(window.GetEvent(evnt))
    {
      if(evnt.Type == sf::Event::Closed)
        window.Close();
      if(evnt.Type == sf::Event::KeyPressed && evnt.Key.Code == sf::Key::Escape)
        window.Close();
      if(evnt.Type == sf::Event::KeyPressed && evnt.Key.Code == sf::Key::Space)
        paused=true;
      if(evnt.Type == sf::Event::KeyReleased && evnt.Key.Code == sf::Key::Space)
        paused=false;
    }

    if(!paused)
    {
      window.Clear();

      lightning.generate();
      window.Draw(lightning);

      window.Display();
    }
  }
 
  return EXIT_SUCCESS;
}


I've included a simple main() to test it. Hold SPACE to pause, ESCAPE to quit.

The corners don't look good if the size and/or thinkness gets to large. The start and end points of the lightning are fixed. But it shouldn't be too hard to change that if you need eg. a lightning gun.

I've only tried it with SFML2.

edit: Fixed a small bug when calculating segmentHeight.
/Peter Welzien

Lupinius

  • Jr. Member
  • **
  • Posts: 85
    • View Profile
Lightning
« Reply #1 on: September 13, 2010, 10:23:23 pm »
Looks pretty nice. Also the code is organized and understandable. Good work!

kurayama

  • Newbie
  • *
  • Posts: 6
    • View Profile
Lightning
« Reply #2 on: September 14, 2010, 12:33:02 pm »
The code works in sfml 1.6, you just have to remove all references to  sf::Renderer& renderer, which from what I've read isn't doing anything..

The lightning looks nice :)

pierreyoda

  • Full Member
  • ***
  • Posts: 186
    • View Profile
    • http://pierreyoda.pagesperso-orange.fr/
Lightning
« Reply #3 on: September 14, 2010, 05:08:14 pm »
Hey, looks nice! :D

But couldn't we have a screenshot (I have SFML2 but not 1.x compiled...)  :?:
Projects:
- Open Rodent's Revenge (rewriting in progress)
- Open Advanced War (paused)

PeterWelzien

  • Newbie
  • *
  • Posts: 38
    • View Profile
Lightning
« Reply #4 on: September 14, 2010, 07:44:16 pm »
You can find a short video clip here: http://welzien.se/misc/lightning.avi
/Peter Welzien

 

anything