Smoke Particle

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Smoke Particle

      Hi guys,

      I need some help with my smoke particle system. I'm trying to get my smoke system to look like this (See "Smoke Stack" ) Its written in Delphi (?). Also, the .exe and source is included.

      This code renders each of the particles.

      Source Code

      1. for (int i = 0; i < MAX_SMOKE_PARTICLES; i++ ) // loop through 500 particles.
      2. {
      3. if ( Particle[i].GetActiveState() ) // If a particular particle is alive...
      4. {
      5. // glColor4f( Particle[i].m_vColor.Red, Particle[i].m_vColor.Blue, Particle[i].m_vColor.Green, Particle[i].m_fEnergy );
      6. glColor4f( 0.25f, 0.25f, 0.25f, 0.050f );
      7. glBegin(GL_TRIANGLE_STRIP);
      8. ... // draw the smoke pic.
      9. glEnd();
      10. Particle[i].m_vPosition += Particle[i].m_vVelocity * 0.0050f;
      11. Particle[i].m_vVelocity += Particle[i].m_vGravity;
      12. Particle[i].m_fSize += 0.01f; // As smoke rises, it expands, so increase the size of the particle.
      13. Particle[i].m_fEnergy -= Particle[i].m_fFade;
      14. if ( Particle[i].IsDead() ) // Is our particle dead?
      15. {
      16. ReSetParticle(i);
      17. }
      18. }
      19. }
      20. ....
      Display All


      This code resets a dead particle.

      Source Code

      1. inline void CSmokeEffect::ReSetParticle( unsigned int uPart )
      2. {
      3. Particle[uPart].SetLifeTime( 1.00f );
      4. Particle[uPart].SetFadeTime( Randf( 0.00f, 1.00f ) ); // Get a number from 0.0 to 1.0.
      5. Particle[uPart].SetPosition( 0.00f, -4.90f, 0.00f );
      6. Particle[uPart].SetVelocity( 0.00f, 1.00f, 0.00f );
      7. Particle[uPart].SetColor( 0.25f, 0.25f, 0.25f, 1.00f );
      8. Particle[uPart].m_fSize = 1.00f;
      9. }


      Question: When rendering each particle in my loop, is there something wrong in what I am donig?

      Note the link I provided above uses Delphi, if anyone knows this language can you translate the important stuff?
      Here a link to a movie demonstrating my smoke. (SLOW download!)

      Thanks again :)
      Sabrina
    • RE: Smoke Particle

      Gosh I hardly know where to begin - Sabrina needs better smoke, and by golly she came to the right place - my web site.

      I think I hear the FBI knocking down my door already.

      Ok so straight to the meat of it - your implementation as it stands doesn't include the complexity of the original implementation - which was written in (omigod) Pascal. Although you have a good start, you need some addtional components to make, ahem, better smoke.

      Here is the original update function for each particle:

      Note that anything past // characters are comments...


      procedure Particle.reset;
      begin
      F := 0;
      PreDelay := random(100);
      SubFrame := 0;
      X := (Random(1000)-500)/10000;
      Y := (Random(1000)-500)/10000+1;
      Z := (Random(1000)-500)/10000;
      //RiseRate := (Random(250+round(MaxVel))+20)/4000;
      Vx := (Random(round(250+MaxVel))-(175+MaxVel/2))/100000;
      Vz := (Random(round(250+MaxVel))-(175+MaxVel/2))/100000;
      Vy := (Random(250)+45+MaxVel)/40000;
      Al := (Random(100))/100*Alp;
      Rate := (Random(200)+10)/5000;
      RiseRate := ((Random(200)/5000)+Rate*2)/3;
      Size := Random(2000)/1600;
      Frame := 0;
      NextFrame := 1;
      Col := ((Random * 20)+80)/100;
      Dec(UsedP);
      Used := false;
      end;



      Procedure Particle.Tick;
      begin
      if F < Al Then F := F + 0.001*T;
      X := X + Vx*T;
      Y := Y + Vy*T;
      Z := Z + Vz*T;
      Vx := Vx *(0.997-(0.001*T));//+Sin(ElapsedTime/9000*Random)*0.0002-Random*Sin(ElapsedTime/5000)*0.0001;
      Vy := Vy *(0.997-(0.001*T));//+Sin(ElapsedTime/10000*Random)*0.0002-Random*Sin(ElapsedTime/4000)*0.0001;
      Vz := Vz *(0.997-(0.001*T));//+Sin(ElapsedTime/8000*Random)*0.0002-Random*Sin(ElapsedTime/6000)*0.0001;
      //R := (R+(Random(1000))/50)/2;
      Vy := Vy + RiseRate/900*T;

      if RiseRate > 0.00 then
      RiseRate := RiseRate-(VY*0.01*T*(1-RiseRate));
      if RiseRate < 0 then RiseRate := RiseRate + 0.01;

      SubFrame := SubFrame+ Rate*T;
      Size := Size+0.01*((1-Rate)*0.07)*T;
      if SubFrame >= 1 then
      begin
      inc(Frame);
      NextFrame := Frame+1;
      if Frame >= maxframes-1 then begin Frame := MaxFrames; {Size := 1;} reset; end;
      if NextFrame > maxframes then begin NextFrame := 0; {Size := 1;} end;
      SubFrame := Subframe-1;
      end;
      end;


      Here's some comments I have about what the original programmer did that you haven't done (yet):
      1. Used an actual elapsed time component instead of incrementing the movement based on loop count
      2. Added some clamps to make sure the random components didn't make smoke particles sink to the ground
      3. Added some random components to the original data, such as velocity, position, rise rate, and size
        [/list=1]

        But, basically you've got it - just add some random components to the mix and you should have some great smoke any time you want it.

        Now - knowing what I know about the people that read these posts, I'll be completely amazed if this thread stays on topic.
      Files
      • smoke.bmp

        (470.31 kB, downloaded 699 times, last: )
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • RE: Smoke Particle

      The one thing i never understood about particle physics in games is why doesnt it kill your frame rate? You are doing complex math on a large number of objects that has to recur on every loop. What keeps this from taking all your processor time?
      .Code
      push you ; haha!
    • RE: Smoke Particle

      Well - patricles CAN kill your framerate - just like everything else.

      Particles aren't usually physically present, and they tend to share many of the same attributes (such as orientation, texture, etc.). This attribute sharing means you can simplify the systems that process their behavior - in a way they are treated as a single, complex object rather than multitudes of generic objects.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • RE: Smoke Particle

      Hi guys,

      I was actually expecting to be laughted at and even mocked for every letting you guys see what I have so far, but hey, (LOL). Thank God for guys like Mr.Mike to show us the way, right!?

      Serously, my smoke needs work, (lots of work). The "Smoke Stack" link I provided above is very nice looking. One thing I did notice about the program is the author uses a 'sprite' (I think thats the right word). Would this be something that I should add to make it more realistic? The way I see it, smoke morphs into another shape just as the authors smoke attempts to do. Would this be something to consider? Thoughts?

      THANKS MrMike! Your my hero!!

      I can almost hear you say: "Oh yea, that sure does suck". (ROFL!)

      Delphi = Pascal, gottch.

      I did see the "procedure Particle.reset;" and "Procedure Particle.Tick;" and though it was the 'main/important' part of the program.

      Used an actual elapsed time component instead of incrementing the movement based on loop count

      I am assuming here that means 'frame rate independent', right? Would there be an advantage using std::vector or std::list here instead of simple array? Also, how about the cost of using a list of pointers i.g. shared_ptr. The first thing that comes to mind when using pointers, it the cost of creation and destruction time. Thoughts? EDIT: took out dumb question.

      Added some clamps to make sure the random components didn't make smoke particles sink to the ground

      Source Code

      1. //Clamps random return values between "fMin" and "fMax".
      2. inline float Randf( float fMin, float fMax )
      3. {
      4. float fNum = (float)rand() / RAND_MAX;
      5. return fMin + (fMax - fMin) * fNum;
      6. }

      I think I'm ok with what you said about clamping values down to a certain range. This may or may not be correct, but if its not let me know :)


      Added some random components to the original data, such as velocity, position, rise rate, and size

      Ok, will do :)


      I do have a side question on this. Would you guys think that adding "p = vt + 1/2at2 " to a particle system is over-board? I'm thinking that adding some physics to a system like this might give the impression of looking more realistic. However, as DarkPenguin pointed out, it very well could drop frame rates. Thoughts?

      Humm.. what else..? Thats it for now. THANKS! :)

      Sabrina

      The post was edited 2 times, last by Sabrina ().

    • Trust me i'm not 3d expert i cant even get a stupid UI to show up on my screen yet without the resourcemanager blowing up in my face or something...

      Used an actual elapsed time component instead of incrementing the movement based on loop count

      When i read this it made me think about the fTime and fElapsedTime which are passed into your Render callback function. I could be wrong.

      and as far as
      p = vt + 1/2at2

      position, velocity, rise rate

      theres your position, velocity and acceleration (rise rate). Also you now have the newly added component of time to help you compute it. :)
      - Brian Wight
    • Hi,


      ...i cant even get a stupid UI to show up on my screen yet without the resourcemanager blowing up in my face or something

      I have to laugh at what you said about the resource mgr becuase I too am at the same point now. (Actually, the way you phrased is funny.) My resource mgr really is pitifull, and I'm not very happy about it. I feel your pain.


      Some more thoughts:

      As far as the physics thingy thought (p = vt + 1/2at2), I picked a newspaper and was reading the weather section when it hit me that I could use some physics functions to give a realistic look to my particles. So I got a physics book from a friend and started thumbing through it and came across a few functions.

      1) a = f/m (F = m*a) .... This gives me acceleration.

      2) V = v+a * tdelta .... This gives me velocity

      3) (Subsitute your own reality of physics here..)

      Much better smoke now. Can it be improved, Hummmmmm

      Sabrina

      The post was edited 1 time, last by Sabrina ().

    • Oh - one more thing!

      Put all of these crazy hardcoded numbers in a text file, and be able to re-read them and alter your simulation by pressing the space bar or something.

      Particles are a tweakfest - and the best way to dial them in is to make your parameters data driven. If you don't have to compile each time you change something, you'll get a lot more work done!
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • Mr.Mike, did you get a chance to review the new update? I think it looks better, but need some input as to how it looks.

      If it still looks bad, plz let me know and provide some guidance guys. I won't be a good programmer if I don't get "peer-reviews". (suggestions, comments, helpful hints, etc.) :)

      I do have a "Quake" like console and will be able to 'ReLoadScriptFile(...)' settings from a file. 8)

      Me.
    • Well i downloaded it yesterday but since then its been removed i dont remember exactly how it looked but i'll give you my opinion from what i can remember.

      First thing that comes to mind is that its very predictable. It seems to me like its got a loop it runs through or something. I noticed that every 15-20 seconds or something it would flair up and make a cute little smoke puff, which i might add is a nice feature. However, i shouldnt be able to notice a pattern like that.

      If you eventually want to add a flame beneath the smoke is it set up where the smoke will go with the flow of the tips of the flame. Or is it still going to be a column of smoke comming from the base of the flame. If not, then you need to think of a way to make your smoke where its base doesnt have to be a circle and it doesnt have to be on a flat plane. Also the area that is generating the smoke would normally not exist, something burning would generate smoke not the ground.

      Well thats all i can think of for now, hope it gives you some ideas on stuff you can change.
      - Brian Wight
    • Hi,

      If not, then you need to think of a way to make your smoke where its base doesnt have to be a circle and it doesnt have to be on a flat plane.


      That's a good point and one that I thought about. I think I'll have to hide the point where the smoke is generated (point where it starts) so that it looks better. Right now I'm not sure how to get that fixed. One thought came to mind is to have the smoke start off with its alpha set to 0.0, then as the smoke rises (get older) gradually increment the alpha (0.0 to 1.0) I think this is what really happens to smoke. Smoke does not occur 'in the fire', it occurs just above the flame. Hope all that makes sense. Thoughts?

      Thanks for the input! :) Keep'em comming.

      I'm still waiting on Kain and Rez's thoughts. Not sure where these boys are. ?(


      Sabrina.
    • Hi Sabrina,

      I'm having trouble viewing your movie. I think the whole internet is downloading it, right now.... plus Quicktime is complaining about something, and I don't know how to use the internets to fix it.

      I don't really have much to add that hasn't already been said. Particle are one of the most expensive graphical components, so if you had to cut anything for performance reasons, cut acceleration. You can pretty much assume that smoke rises at a constant velocity making (pNew = pOld + v*deltaT) a perfectly valid way to go.

      I'm also assuming that all of your particles are sharing the same instance of texture data, yes?

      Along the lines of performance, stick with the raw array, no vector, list, or smart pointers. Those are to be used when you're going to be passing around dynamic things a lot, but they cost an extra CPU instruction to access. Since you want your particles working as speedy as possible, and since your particles are going to be a self-contained system with a well-defined problem space, you're probably safe from yourself with a simple raw array.

      Assuming 'v' is a three-dimensional vector, the minor random factor is a great idea. Particles that randomly change direction can be visually awkward, so try incorporating either sine or cosine into your random fluctuations so that your particles have a wavy behavior to them. The random parts of each particle's behavior can come in the form of randomizing the amplitude of the motion variation (clamped between reasonable values), period of the wave (clamped between reason), and starting angle between 0 and 2*pi that gets passed into the sine or cosine function. This oughta be a fun math exercise for ya. And it doesn't have to really be random as in the use of rand(). You can apply some kind of deterministic algorithm that guarantees a sensible spread of particles so that you can spawn less particles and still get a fuller effect.

      Hmmm... alpha upon spawning? I think it would be better to just have a particle size of zero upon spawning. As the lifetime increases, size increases and alpha becomes more translucent... that seems natural to me. Apologies in advance if you are already doing this. Have you considered posting your movies on a googlepage or something... or maybe YouTube? Anything but Myspace.
    • How-do Sir!

      Hey, thanks for the input! :)) I appreciate you taking the time to look at my project.

      I will make the changes that you mentioned, spawning particle size and not alpha values and using the trig fuctions.

      Also, (THIS IS COOL) remember sometime ago I was asking about scripts? I decided to write my own script. I got my "scanner" to! (Scanner = make tokens) Now I'm working on my parser (parser = make sence of the tokens)! Now my particle system and other thingy's will be data driven. Is'nt that cool!? (I think MrMike handed it out for an assignment.)


      Sabrina
    • Originally posted by Sabrina
      (I think MrMike handed it out for an assignment.)


      You should make him grade it! Very cool on the scanner/parser. Did you use anything like Lex and Yacc, or did you write it all from scratch?

      -Rez
    • Eesh, I can't imagine not using Lex and Yacc. Well, maybe Antler or something, but still. Writing it from scratch is just . . . painful. Either way, you get mad props, Sabrina.
      Feel you safe and secure in the protection of your pants . . . but one day, one day there shall be a No Pants Day and that shall be the harbinger of your undoing . . .
    • I did this purly from scratch, yes from scratch! Its not as hard as I thought it would be. With some research ("Backus Naur Form" and "Finite State Machine") at my local library and some help from a friend, I was able to come up with this: (Note is this just a fancy ".ini" file and it does not create an executable file.)

      Particles.vms/ (VMS = "Virutal State Script", sounds impressive right?)

      Source Code

      1. Particles
      2. {
      3. Size = 1.0;
      4. LifeTime = 1.0;
      5. Velocity = 0.5;
      6. Image = "Particle.tga";
      7. .
      8. .
      9. }

      Once I get it scanned it becomes:

      Source Code

      1. Token Count 1
      2. TokenType: 34 (ScriptType)
      3. TokenName: Particles
      4. Token Count 2
      5. TokenType: 19 (Block Begin)
      6. TokenName: {
      7. Token Count 3
      8. TokenType: 19 (Variable)
      9. TokenName: Size
      10. Token Count 4
      11. TokenType: 26 (Assignment)
      12. TokenName: =
      13. Token Count 5
      14. TokenType: 36 (Float)
      15. TokenName: 1.0
      16. Token Count 6
      17. TokenType: 19 (Variable)
      18. TokenName: LifeTime
      19. Token Count 7
      20. TokenType: 26 (Assignment)
      21. TokenName: =
      22. Token Count 8
      23. TokenType: 36 (Float)
      24. TokenName: 1.0
      25. Token Count 9
      26. TokenType: 19 (Variable)
      27. TokenName: Velocity
      28. Token Count 10
      29. TokenType: 26 (Assignment)
      30. TokenName: =
      31. Token Count 11
      32. TokenType: 36 (Float)
      33. TokenName: 0.5
      34. Token Count 12
      35. TokenType: 19 (Variable)
      36. TokenName: Image
      37. Token Count 13
      38. TokenType: 26 (Assignment)
      39. TokenName: =
      40. Token Count 14
      41. TokenType: 36 (String)
      42. TokenName: Particle.tga
      43. .
      44. .
      Display All


      Now the tricky part is the parser. I have to make some meaning to all of this. I have a few ideas of what to do next, but sure would like to hear what you guys think so far.

      I did look at Yac and Lex.... Ummm.... no, besides I need the experiance of the "Ear Wax Expolsion" phenomenon. Yac and Lex look to confusing for me.

      Hummm have MrMike grade.... guess I better get lots of apples.

      Me.
    • Sabrina congrats, I love doing this kind of thing, creating mini languages and so forth. It's harder for me to come up with excuses to do anymore now that LUA and embedded Python (yuck).
      are around. Are you using a yacc parser to do this? Personally I think it's overkill for a syntax as simple as this? a switch block would handle this short a state set simply enough.
      What does the parser return to your system a vector of maps?

      cool stuff

      gb
    • Hi guys

      Garbob
      No Yacc (or Yack's) was (were) used in the making of this script. I do use "switch" statments to control transitional state changes. (I still am having trouble speaking "geek"!). :P

      Another question or perhaps some advice or thoughts from you guys. Garbob hit on this topic. I have a class called "Tokens" my tokens consist of "Literals" (e.g. "ParticleSize") which have a value along with its type ("Literal"). My token class may also "Identifiers: (e.g. "MyImage.tga") which also have a value and type (string). With me so far? whew, ok.

      Once I have a token, I use the "std::vector" to store all those tokens. Here's the problem. If I were to have (theorically) have hundreds and hundreds of tokens in my vector list, I would have to perform a look-up in order to get the right token. From what I've read this can be very slow (at most O(n) operation)! Is there a reasonable solution to this problem, do I just live with it?

      Here is one possible solution (?) Create a "hash" (or a "Handle") for each token and inserting the token and hash into a "std::map". Now when I need to perform a look-up, I simply use a hash function to retrieve the token. Thoughts?? Garbob brought up this idea, do I use a vector or map.

      Sabrina,
      Oh yea, do I still get credit from the teacher (MrMike) for asking help or will two apples on his deak be enought?

      The post was edited 1 time, last by Sabrina ().

    • Sabrina,

      When i was talking about the vector or maps i was thinking more about the output of the configuration file module to the particle system.

      You don't lex the entire file and then parse it
      I see a top do down parsing structure.

      C++ ish psuedo code:

      in this getToken returns one token at a time
      and can signal EOF and disk i/o errors

      while (true)
      {
      lookForStartOfDefinition()
      if (found start of definition)
      {
      if (opening of particle definition found)
      {
      parseParticleDefinition()
      }
      else
      {
      error message - start of particle definition expected
      }
      }
      else
      {
      break out of parse loop end of file
      }
      }

      parseParticleDefinition()
      {
      do
      {
      getToken()
      if (EOF)
      error message - unexpected end of file

      if (token is close-brace)
      return - end of definition found

      if (token is ident)
      {
      getToken()
      if (token is asign-op)
      getLiteral and do assignment
      else
      error message unexpected token
      }
      else
      error message - identifer expected
      } while ()
      }

      lookForStartOfDefinition()
      {
      getToken()
      if (EOF)
      do something to signal done to exit the master parse loop

      if (token is ident)
      {
      getToken();
      if (token is open-brace)
      signal found a definition start
      else
      error message - unexpected token found
      }
      else
      error message - identifer expected
      }

      Hope this helps a more than it hurts

      gb