PDA

View Full Version : Efficient Smoke



Fezziwig
03-17-2012, 04:43 AM
Hello,
I have made a smoke grenade in a top-down shooter and I just started on smoke grenades. My current way of rendering smoke is to use a png file of a grey smoke like cloud and then have around 100 of them spin out of the grenade. It looks really good but it does lag quite a lot and I was wandering if there was a way to speed up my code.

This is the smoke class:


class Smoke
{
private:
double x,y,alpha,size,speed,direction, rotation;
public:
void Move();
void Draw();
Smoke(double, double);
};

std::vector<Smoke> Smoke_Vector;

void Smoke::Move(void)
{
x += cos(direction) * speed;
y += sin(direction) * speed;
size += 0.1;
alpha -= alpha*0.001;
rotation += 0.1;
if (alpha < 0.05)
{
for (std::vector<Smoke>::iterator it = Smoke_Vector.begin(); it != Smoke_Vector.end(); ++it)
{
if (&amp;*it == this)
{
Smoke_Vector.erase(it);
break;
}
}
}
}

void Smoke::Draw(void)
{
glColor4d(1, 1, 1, alpha);
Draw_Texture(x, y, size, size, rotation, Tex_Smoke);
}

Smoke::Smoke(double ax, double ay)
{
x = ax;
y = ay;
size = 0;
direction = Random(0, 360, current_time);
rotation = Random(0, 360, current_time * direction);
speed = 0.05;
alpha = 20;
}

This is how the texture is loaded:

Tex_Smoke = SOIL_load_OGL_texture("Data/Smoke.png",SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_MIPMA PS | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);

This is how it's drawn:


void Draw_Texture(double x, double y, double width, double height, double Angle, GLuint texture)
{
glPushMatrix();
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_NEAREST = no smoothing
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D,texture);
glTranslated(x, y, 0);
glRotated(Angle, 0, 0, 1);
glBegin(GL_QUADS);
double x_centre = width/2;
double y_centre = height/2;
glTexCoord2d(0.0,0.0); glVertex2d(-x_centre, -y_centre);
glTexCoord2d(1.0,0.0); glVertex2d(x_centre, -y_centre);
glTexCoord2d(1.0,1.0); glVertex2d(x_centre, y_centre);
glTexCoord2d(0.0,1.0); glVertex2d(-x_centre, y_centre);
glEnd();
glPopMatrix();
}

And this is how they are rendered in the display funtion:


for( int i = 0 ; i < Smoke_Vector.size() ; ++i )
{
Smoke_Vector[i].Draw();
Smoke_Vector[i].Move();
}

Any help is much appreciated.
Thanks,
Rowan

BionicBytes
03-17-2012, 06:32 AM
A few pointers really.
Although nothing to do with your issue you should try and use time deltas when calculating the animation rather than a fixed step size like 0.001.
That way even fast computers always render/ animate the same.

A possible speed up is to decouple the smoke update/move from the rendering.
That way you can delegate the task to a separate thread.
This saves CPU time - if that is where your system currently has the bottleneck.

If your game is suffering from fill rate then there's not much one can do as alpha blending can be expensive.

I do notice you are using doubles instead of singles. Why?
Fixed function GL only uses singles anyway and will have to convert to singles ultimately regardless.

One final thing is that you set the texture min and mag filters before you bind the texture which is not right.

Fezziwig
03-17-2012, 07:22 AM
Thanks your advice really helped but what do you mean by doubles and singles? And can you give me an example of how to use time deltas.
Thanks.

Fezziwig
03-17-2012, 08:29 AM
Also there's no lag when you throw it at first and the smoke starts to appear but as the smoke spreads the performance gets worse.

V-man
03-18-2012, 10:23 AM
Thanks your advice really helped but what do you mean by doubles and singles? And can you give me an example of how to use time deltas.
Thanks.

A double is a 64 bit floating point number.
A single is a 32 bit floating point number.
http://www.opengl.org/wiki/Common_Mistakes#GL_DOUBLE

As for performance when you use textures,
http://www.opengl.org/wiki/Performance#Texture_Mipmaps

and when you enable blending, yes, it is normal that it drags down the performance. Why is it normal for it to drag down the performance?

tanzanite
03-18-2012, 04:46 PM
Hello,
This is how it's drawn:


void Draw_Texture(double x, double y, double width, double height, double Angle, GLuint texture)
{
glPushMatrix();
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_NEAREST = no smoothing
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D,texture);
glTranslated(x, y, 0);
glRotated(Angle, 0, 0, 1);
glBegin(GL_QUADS);
double x_centre = width/2;
double y_centre = height/2;
glTexCoord2d(0.0,0.0); glVertex2d(-x_centre, -y_centre);
glTexCoord2d(1.0,0.0); glVertex2d(x_centre, -y_centre);
glTexCoord2d(1.0,1.0); glVertex2d(x_centre, y_centre);
glTexCoord2d(0.0,1.0); glVertex2d(-x_centre, y_centre);
glEnd();
glPopMatrix();
}

This is bad.
* group your smoke particles per texture / state and draw them all without constantly re-setting the state (depending on driver - might not help much with speed).
* glTexParameter stuff is part of texture object state - why are you resetting it all the time (also, they are in the wrong place as the relevant texture gets bound AFTER filtering parameters - ie. you are changing the settings of some random/semi-random texture that was bound before instead)?
* get rid of the push matrix / translate / rotate / pop matrix stuff - as you are using immediate mode and the calculations you do look fairly simple it would be better if you calculated them yourself ... which enables you to:
* ... draw all the particles (with matching state - in your case: "texture") in one begin / end block.

Did not look at the rest of the code.