For lack of a better topic this week, I thought I’d share what I’ve been working on for our game, Flux. Before I begin though, I’d like to again thank Professor Hogue for making the homework questions a lot more manageable. It’s because of this change that I finally managed to find some time to work on the game, and sure enough, I was fairly productive. But back to the main topic. Apart from just showing what I’ve done, I will also give a brief tutorial of how I did it. Hopefully someone somewhere will learn something from this, though not likely.
For the last couple days I’ve been getting back to work on particles. Last semester, we had a working system that ran pretty efficiently. Although we never got to texture any of them, which made the particles look very artificial and a lot less interesting. The code was also somewhat messy by the end. So this time around, I wanted to rewrite the entire particle class, clean it up, and add textures as well. I am happy to say all of these tasks have been achieved, though room for improvement still exists, as always. Not including the inferior systems we had last semester, 3 new ones have been created: fire, rain, and waterfalls. All of them are textured and in my opinion represent what they are suppose to be quite well. All new systems currently contain 2000 individual particles, though that number will surely need to be reduced later on for efficiency purposes.
Of course they all look better moving, but you can still get the general idea. The textures are also just placeholder. I await Master Ninja Alston to make some awesome textures.
Rain. This is the easiest system to make. Particles start at y=20 and fall until they hit 0.
Here is the fire system. The particles start at y=0 and increase in height. As they increase they get smaller and smaller until they disappear.
Waterfall. Particles start at y=10 and shoot out in the z briefly and then continue down until they hit the ground.
So that’s mostly what I’ve done in the last couple days. Making each system is actually very simple. Once you understand the pipeline, you can make new ones in less than an hour. I’m going to try and give a brief tutorial now of how I do it. It may not necessarily be the best way, in fact I’m certain it probably isn’t, but it works for me nonetheless. In fact, it’s very reminiscent of what Professor Hogue taught us last semester in Computer Animation.
I’ll start off with the header, let’s call it “ParticleManager.h”. The first thing you’re going to want to do is make a class. Inside the class you declare all your functions and variables and whatnot. The most important thing in this class is going to be the constructor, because this is where you will initialize each of your systems. The second thing you’re going to need is a structure declared outside the class. This structure will contain all the attributes that each individual particle will contain. Lastly you’ll need to make a vector of objects for the structure you created. Declare this vector in the private section of your particle class.
Particle structure. Containing description of each variable
Class constructor. Initializing the particle systems. You'll notice the variables being initialized are different than tat of the structure. There is a reason for this that I will explain shortly.
Vector of structure objects. Declared as private.
Now we move on the the “ParticleManager.cpp” file. Easy enough so far right? Well trust me, it’s easy all the way through. I’m going to use the Rain system for this example because it is the easiest one to make and understand.
The first thing you’re going to want to make is a function to create the particles. Let’s call it “createRainParticle(int)”. Remember to declare it first in your header file as well. Basically all this function does is initialize all the variables in the particle structure to the corresponding variables in the class constructor. When a particle dies, this function is called again to reset, or re-initialize the particle so that it can continue through the cycle again. This is why the variables in the constructor are separate from the ones in the particle structure. The ones in the constructor are essentially reset variables. You’ll also need to send in an integer value to determine which particle in the vector is being created.
Here's the createRainParticle function.
You’ll notice that the x, y, and z position are set using a random number function. This will be hard for me to explain, but essentially they are there so that the initial position is set within a certain boundary. I’ll leave it to you to figure out exactly what it’s doing. Come on… I can’t explain every last detail.
Next comes the update function. You can either have one function to handle the updates of all systems, or you can declare separate ones. It really doesn’t matter. For this example however, I chose to use a single update function with a switch statement inside to determine which system I want updated. Since rain is very simple, the only variables I am updating are the x, y, and z positions. You can also update color, life, and speed here should you need to do so.
Here's the updateParticles() function.
Alright, almost done. Still easy right? Well, now comes the drawing function. Hopefully you’re familiar with OpenGL and drawing. If not, well then I feel sorry for you. To draw the particles you use the standard glBegin and glEnd. Just specify the vertices using the x, y, and z positions and that’s it, the particles will be drawn. You’re also going to need an if statement here to determine when to reset the particle. Afterwards, just call the update function and the position of the particle will be updated for the next drawing iteration.
Here's the function. As you can see I'm using GL_TRIANGLE_STRIP instead of QUADS. I can't even tell you a legitimate reason for why I'm using this. All I can say is I saw it being used in an example a while back and I never changed it. I suppose the performance would be the same with QUADS.
The last function you’ll need is the setup. This is a very simple function that is called right after you declare a new particle system in main(or wherever else you decide to declare it). All it does is initialize the vector of objects described before, and then calls the create function for that position in the vector. It never gets called again anywhere down the pipeline. Similar to the update function, you can either use a single function to handle all setups, or a separate one for each system. I chose again to use a single function with a switch statement.
Here's the setupParticles(char) function.
That’s it! The rain particle system. All that’s left to do now is create a Particle object in main or wherever else you decide to declare it in your program. Create the object, call setup(ONCE!), then call the draw function within your OpenGL loop.
Hopefully this tutorial helped somewhat. I realize it’s probably not the absolute best way to approach particles but as I said, it works for me and my group. There are still improvements that need to be made for the future, some of which probably won’t get done this semester. For example, it would be nice to do the whole system using Cg and shaders, but unfortunately I do not possess the expertise to make that happen yet. I’m certain that doing this would enable us to have much more particles on the screen at once due to the boost in performance, but for now OpenGL will have to do. Currently, all 3 systems running at once at 2000 particles causes a noticeable drop in frame-rate, but the program is still quite stable. If you choose to not texture your particles you will we able to render significantly more, as seen last semester when we had 10’s of thousands processing at once before noticing a drop in frame-rate. If you do decide to texture them however, just make sure your images are of very low quality. The rain drop texture used is 14 x 20 pixels. If your image is too big, it will slow the program down significantly.
I hope you enjoyed this beginner’s guide to particles. I suppose if I helped at least one person then my job is complete. Enjoy and have fun!
All 3 systems running at once. Again, looks better in motion 🙂