Explosion Particles Tutorial
Introduction
This tutorial will show you how to produce some simple
particle effects for explosions, as seen in mods such as Q3F and
CQA, and in the original Quake. Most of the code you'll need to
produce these effects is already in place in the Q3A source, so
there isn't much work to do. This tutorial assumes you are
familiar with C programming and the Q3A source. You'll need to
produce a texture and shader for your particles, or sparks, or you
can download my example here.
|
The particle explosion effect
in action in my CQA mod. (Click to see a larger
image) |
Preparatory
coding
The first thing to do is to put our
shader as a member of the cgs.media structure, and to register the
shader so it is precached and we can use it in the code. So, in cg_local.h at the bottom of the cgMedia_t, beneath the line sfxHandle_t wstbactvSound;, put the following code:
qhandle_t sparkShader;
To register the shader, put the
following line at the bottom of the CG_RegisterGraphics function in cg_main.c:
cgs.media.sparkShader =
trap_R_RegisterShader("spark");
This assumes your
particle shader is called "spark", which it will be if you've used
my example shader and graphic. If you've called your particle
shader something else, change the argument to trap_R_RegisterShader accordingly.
The Particle Launch
Function
Most of the work in producing the particles is done
in CG_ExplosionParticles, a new funciton which
you should add in cg_weapons.c anywhere above
the existing function CG_MissileHitWall. The
function is defined as follows:
void CG_ExplosionParticles( int
weapon, vec3_t origin ) {
int number; // number of particles
int jump; //
amount to nudge the particles trajectory vector up by
int
speed; // speed of particles
int light; // amount of light
for each particle
vec4_t lColor; // color of light for each
particle
qhandle_t shader; // shader to use for the
particles
int index;
vec3_t randVec, tempVec;
// set defaults
number = 32;
jump = 50;
speed = 300;
light = 50;
lColor[0] =
1.0f;
lColor[1] = 1.0f;
lColor[2] = 1.0f;
lColor[3]
= 1.0f; // alpha
switch( weapon )
{
case
WP_ROCKET_LAUNCHER:
number = 128;
jump =
70;
light = 100;
lColor[0] = 1.0f;
lColor[1] =
0.56f;
lColor[2] = 0.0f;
shader =
cgs.media.sparkShader;
break;
case WP_GRENADE_LAUNCHER:
number = 64;
jump = 60;
light = 100;
lColor[0] = 1.0f;
lColor[1] = 0.56f;
lColor[2] = 0.0f;
shader = cgs.media.sparkShader;
break;
default:
return;
for( index = 0; index < number;
index++ ) {
localEntity_t *le;
refEntity_t
*re;
le = CG_AllocLocalEntity(); //allocate a
local entity
re = &le->refEntity;
le->leFlags
= LEF_PUFF_DONT_SCALE; //don't change the particle size
le->leType = LE_MOVE_SCALE_FADE; // particle should
fade over time
le->startTime = cg.time; // set the start
time of the particle to the current time
le->endTime =
cg.time + 3000 + random() * 250; //set the end
time
le->lifeRate = 1.0 / ( le->endTime -
le->startTime );
re = &le->refEntity;
re->shaderTime = cg.time / 1000.0f;
re->reType =
RT_SPRITE;
re->rotation = 0;
re->radius = 3;
re->customShader = shader;
re->shaderRGBA[0] =
0xff;
re->shaderRGBA[1] = 0xff;
re->shaderRGBA[2]
= 0xff;
re->shaderRGBA[3] = 0xff;
le->light =
light;
VectorCopy( lColor, le->lightColor );
le->color[3] = 1.0;
le->pos.trType = TR_GRAVITY;
// moves in a gravity affected arc
le->pos.trTime =
cg.time;
VectorCopy( origin, le->pos.trBase );
tempVec[0] = crandom(); //between 1 and -1
tempVec[1] = crandom();
tempVec[2] = crandom();
VectorNormalize(tempVec);
VectorScale(tempVec, speed,
randVec);
randVec[2] += jump; //nudge the particles up a
bit
VectorCopy( randVec, le->pos.trDelta );
}
}
This arguments to this function are the weapon
number, for example WP_ROCKET_LAUNCHER and the 3
dimensional vector representing the position of the explosion.
First of all a number of variables representing various
characteristics of the particles are declared, for example the
speed of the particles, the shader to use etc. In the following
switch statement, these values are changed for specific weapons.
In this example only the rocket launcher and grenade launcher have
particle explosions, and the other weapons will cause the default
case to be used, and the function returns.
The for loop then creates the actual particles. The
variable number holds the number of particles to
create, and the code is looped until the correct number of
particles have been created. The code inside the loop first
allocates a space on the local entity list for the particle, then
sets up the values in the local entity that define how the
particle will move, how long it will appear for etc.
The lines at the bottom
of the loop, starting with tempVec[0] = crandom(); generate a random vector which will act as the movement
vector for the particle. This vector is then scaled so the
particle is moving at the correct speed, and the upwards component
of the movement vector is increased by a small amount to launch
the particles move vertically and give a better affect. This
vector is then copied to the particle's localentity's pos.trDelta vector, the vector which represents
the movent of that particle.
The final step is to call this function
by putting the following line at the bottom of CG_MissileHitWall, the function responsible for
creating the effects for an explosion:
CG_ExplosionParticles( weapon, origin );
You should now be able to compile your code and see
the particle effect produced when a rocket or grenade explodes. I
hope this tutorial hasn't been too hard to follow :) Please note
that the code shown in this tutorial isn't perfect, it's just a
relatively simple and effective method of producing explosion
particles. There is plenty of room for improvment to create a more
powerful system, but this should provide a base for you to work
from. If you have any questions/comments/flaming/mad ramblings
about this tutorial, please email me here.