quakeforge/libs/video/renderer/glsl/glsl_particles.c
Bill Currie ef37ed39a9 Clean up global symbols for the glsl renderer.
Where possible, symbols have been made static, prefixed with glsl_/GLSL_ or
moved into the code shared by all renderers. This will make doing plugins
easier but done now for link testing. The moving was done via the gl
commit.
2012-02-18 11:48:52 +09:00

1883 lines
48 KiB
C

/*
gl_dyn_part.c
OpenGL particle system.
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] = "$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/image.h"
#include "QF/qargs.h"
#include "QF/quakefs.h"
#include "QF/render.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/GLSL/defines.h"
#include "QF/GLSL/funcs.h"
//#include "QF/GL/qf_explosions.h"
#include "QF/GLSL/qf_particles.h"
#include "QF/GLSL/qf_textures.h"
#include "QF/GLSL/qf_vid.h"
#include "r_cvar.h"
#include "r_dynamic.h"
#include "r_local.h"
#include "r_shared.h"
//FIXME not part of GLES, but needed for GL
#ifndef GL_VERTEX_PROGRAM_POINT_SIZE
# define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
#endif
//FIXME should not be here
static int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 };
//static int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 };
static int ramp3[8] = { 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
static vec3_t vertex_normals[NUMVERTEXNORMALS] = {
#include "anorms.h"
};
static GLushort *pVAindices;
static partvert_t *particleVertexArray;
static GLuint part_tex;
static const char quakepoint_vert[] =
#include "quakepnt.vc"
;
static const char quakepoint_frag[] =
#include "quakepnt.fc"
;
static const char quakepart_vert[] =
#include "quakepar.vc"
;
static const char quakepart_frag[] =
#include "quakepar.fc"
;
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t vertex;
shaderparam_t palette;
shaderparam_t color;
shaderparam_t fog;
} quake_point = {
0,
{"mvp_mat", 1},
{"vertex", 0},
{"palette", 1},
{"vcolor", 0},
{"fog", 1},
};
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t st;
shaderparam_t vertex;
shaderparam_t color;
shaderparam_t texture;
shaderparam_t fog;
} quake_part = {
0,
{"mvp_mat", 1},
{"vst", 0},
{"vertex", 0},
{"vcolor", 0},
{"texture", 1},
{"fog", 1},
};
inline static void
particle_new (ptype_t type, int texnum, const vec3_t org, float scale,
const vec3_t vel, float die, int color, float alpha, float ramp)
{
particle_t *part;
/*
// Uncomment this for particle debugging!
if (numparticles >= r_maxparticles) {
Sys_Error ("FAILED PARTICLE ALLOC!");
return NULL;
}
*/
part = &particles[numparticles++];
VectorCopy (org, part->org);
part->color = color;
part->tex = texnum;
part->scale = scale;
part->alpha = alpha;
VectorCopy (vel, part->vel);
part->type = type;
part->die = die;
part->ramp = ramp;
part->phys = R_ParticlePhysics (type);
}
/*
particle_new_random
note that org_fuzz & vel_fuzz should be ints greater than 0 if you are
going to bother using this function.
*/
inline static void
particle_new_random (ptype_t type, int texnum, const vec3_t org, int org_fuzz,
float scale, int vel_fuzz, float die, int color,
float alpha, float ramp)
{
float o_fuzz = org_fuzz, v_fuzz = vel_fuzz;
int rnd;
vec3_t porg, pvel;
rnd = rand ();
porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0];
porg[1] = o_fuzz * (((rnd >> 5) & 63) - 31.5) / 63.0 + org[1];
porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2];
rnd = rand ();
pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0;
pvel[1] = v_fuzz * (((rnd >> 5) & 63) - 31.5) / 63.0;
pvel[2] = v_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0;
particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp);
}
/*
inline static void
particle_new_veryrandom (ptype_t type, int texnum, const vec3_t org,
int org_fuzz, float scale, int vel_fuzz, float die,
int color, float alpha, float ramp)
{
vec3_t porg, pvel;
porg[0] = qfrandom (org_fuzz * 2) - org_fuzz + org[0];
porg[1] = qfrandom (org_fuzz * 2) - org_fuzz + org[1];
porg[2] = qfrandom (org_fuzz * 2) - org_fuzz + org[2];
pvel[0] = qfrandom (vel_fuzz * 2) - vel_fuzz;
pvel[1] = qfrandom (vel_fuzz * 2) - vel_fuzz;
pvel[2] = qfrandom (vel_fuzz * 2) - vel_fuzz;
particle_new (type, texnum, porg, scale, pvel, die, color, alpha, ramp);
}
*/
VISIBLE void
R_ClearParticles (void)
{
numparticles = 0;
}
void
R_InitParticles (void)
{
unsigned i;
int vert;
int frag;
float v[2] = {0, 0};
byte data[64][64][2];
tex_t *tex;
qfglEnable (GL_VERTEX_PROGRAM_POINT_SIZE);
qfglGetFloatv (GL_ALIASED_POINT_SIZE_RANGE, v);
Sys_MaskPrintf (SYS_GLSL, "point size: %g - %g\n", v[0], v[1]);
vert = GLSL_CompileShader ("quakepnt.vert", quakepoint_vert,
GL_VERTEX_SHADER);
frag = GLSL_CompileShader ("quakepnt.frag", quakepoint_frag,
GL_FRAGMENT_SHADER);
quake_point.program = GLSL_LinkProgram ("quakepoint", vert, frag);
GLSL_ResolveShaderParam (quake_point.program, &quake_point.mvp_matrix);
GLSL_ResolveShaderParam (quake_point.program, &quake_point.vertex);
GLSL_ResolveShaderParam (quake_point.program, &quake_point.palette);
GLSL_ResolveShaderParam (quake_point.program, &quake_point.color);
GLSL_ResolveShaderParam (quake_point.program, &quake_point.fog);
vert = GLSL_CompileShader ("quakepar.vert", quakepart_vert,
GL_VERTEX_SHADER);
frag = GLSL_CompileShader ("quakepar.frag", quakepart_frag,
GL_FRAGMENT_SHADER);
quake_part.program = GLSL_LinkProgram ("quakepart", vert, frag);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.mvp_matrix);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.st);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.vertex);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.color);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.texture);
GLSL_ResolveShaderParam (quake_part.program, &quake_part.fog);
memset (data, 0, sizeof (data));
qfglGenTextures (1, &part_tex);
qfglBindTexture (GL_TEXTURE_2D, part_tex);
qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qfglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
qfglTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 64, 64, 0,
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
tex = R_DotParticleTexture ();
qfglTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 32, 32, GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE, tex->data);
free (tex);
tex = R_SparkParticleTexture ();
qfglTexSubImage2D (GL_TEXTURE_2D, 0, 32, 0, 32, 32, GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE, tex->data);
free (tex);
tex = R_SmokeParticleTexture ();
qfglTexSubImage2D (GL_TEXTURE_2D, 0, 0, 32, 32, 32, GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE, tex->data);
free (tex);
if (particleVertexArray)
free (particleVertexArray);
particleVertexArray = calloc (r_maxparticles * 4, sizeof (partvert_t));
if (pVAindices)
free (pVAindices);
pVAindices = calloc (r_maxparticles * 6, sizeof (GLushort));
for (i = 0; i < r_maxparticles; i++) {
pVAindices[i * 6 + 0] = i * 4 + 0;
pVAindices[i * 6 + 1] = i * 4 + 1;
pVAindices[i * 6 + 2] = i * 4 + 2;
pVAindices[i * 6 + 3] = i * 4 + 0;
pVAindices[i * 6 + 4] = i * 4 + 2;
pVAindices[i * 6 + 5] = i * 4 + 3;
}
}
void
R_ReadPointFile_f (void)
{
const char *name;
char *mapname;
int c, r;
vec3_t org;
QFile *f;
mapname = strdup (r_worldentity.model->name);
if (!mapname)
Sys_Error ("Can't duplicate mapname!");
QFS_StripExtension (mapname, mapname);
name = va ("%s.pts", mapname);
free (mapname);
QFS_FOpenFile (name, &f);
if (!f) {
Sys_Printf ("couldn't open %s\n", name);
return;
}
Sys_MaskPrintf (SYS_DEV, "Reading %s...\n", name);
c = 0;
for (;;) {
char buf[64];
Qgets (f, buf, sizeof (buf));
r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]);
if (r != 3)
break;
c++;
if (numparticles >= r_maxparticles) {
Sys_MaskPrintf (SYS_DEV, "Not enough free particles\n");
break;
} else {
particle_new (pt_static, part_tex_dot, org, 1.5, vec3_origin,
99999, (-c) & 15, 1.0, 0.0);
}
}
Qclose (f);
Sys_MaskPrintf (SYS_DEV, "%i points read\n", c);
}
static void
R_ParticleExplosion_QF (const vec3_t org)
{
// R_NewExplosion (org);
if (numparticles >= r_maxparticles)
return;
particle_new_random (pt_smokecloud, part_tex_smoke, org, 4, 30, 8,
r_realtime + 5.0, (rand () & 7) + 8,
0.5 + qfrandom (0.25), 0.0);
}
static void
R_ParticleExplosion2_QF (const vec3_t org, int colorStart, int colorLength)
{
unsigned int i, j = 512;
if (numparticles >= r_maxparticles)
return;
else if (numparticles + j >= r_maxparticles)
j = r_maxparticles - numparticles;
for (i = 0; i < j; i++) {
particle_new_random (pt_blob, part_tex_dot, org, 16, 2, 256,
r_realtime + 0.3,
colorStart + (i % colorLength), 1.0, 0.0);
}
}
static void
R_BlobExplosion_QF (const vec3_t org)
{
unsigned int i;
unsigned int j = 1024;
if (numparticles >= r_maxparticles)
return;
else if (numparticles + j >= r_maxparticles)
j = r_maxparticles - numparticles;
for (i = 0; i < j >> 1; i++) {
particle_new_random (pt_blob, part_tex_dot, org, 12, 2, 256,
r_realtime + 1.0 + (rand () & 7) * 0.05,
66 + i % 6, 1.0, 0.0);
}
for (i = 0; i < j / 2; i++) {
particle_new_random (pt_blob2, part_tex_dot, org, 12, 2, 256,
r_realtime + 1.0 + (rand () & 7) * 0.05,
150 + i % 6, 1.0, 0.0);
}
}
static inline void
R_RunSparkEffect_QF (const vec3_t org, int count, int ofuzz)
{
if (numparticles >= r_maxparticles)
return;
particle_new (pt_smokecloud, part_tex_smoke, org, ofuzz * 0.08,
vec3_origin, r_realtime + 9, 12 + (rand () & 3),
0.25 + qfrandom (0.125), 0.0);
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
if (count) {
int orgfuzz = ofuzz * 3 / 4;
if (orgfuzz < 1)
orgfuzz = 1;
while (count--) {
int color = rand () & 7;
particle_new_random (pt_fallfadespark, part_tex_dot, org, orgfuzz,
0.7, 96, r_realtime + 5.0, ramp1[color], 1.0,
color);
}
}
}
static inline void
R_BloodPuff_QF (const vec3_t org, int count)
{
if (numparticles >= r_maxparticles)
return;
particle_new (pt_bloodcloud, part_tex_smoke, org, count / 5, vec3_origin,
r_realtime + 99.0, 70 + (rand () & 3), 0.5, 0.0);
}
static void
R_BloodPuffEffect_QF (const vec3_t org, int count)
{
R_BloodPuff_QF (org, count);
}
static void
R_GunshotEffect_QF (const vec3_t org, int count)
{
int scale = 16;
scale += count / 15;
R_RunSparkEffect_QF (org, count >> 1, scale);
}
static void
R_LightningBloodEffect_QF (const vec3_t org)
{
int count = 7;
R_BloodPuff_QF (org, 50);
if (numparticles >= r_maxparticles)
return;
particle_new (pt_smokecloud, part_tex_smoke, org, 3.0, vec3_origin,
r_realtime + 9.0, 12 + (rand () & 3),
0.25 + qfrandom (0.125), 0.0);
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
while (count--)
particle_new_random (pt_fallfade, part_tex_spark, org, 12, 2.0, 128,
r_realtime + 5.0, 244 + (count % 3), 1.0, 0.0);
}
static void
R_RunParticleEffect_QF (const vec3_t org, const vec3_t dir, int color,
int count)
{
float scale;
int i;
vec3_t porg;
if (numparticles >= r_maxparticles)
return;
scale = pow (count, 0.23); // calculate scale before clipping to part. max
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
for (i = 0; i < count; i++) {
int rnd = rand ();
porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 7.5);
porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 7.5);
porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 7.5);
// Note that ParseParticleEffect handles (dir * 15)
particle_new (pt_grav, part_tex_dot, porg, 1.5, dir,
r_realtime + 0.1 * (i % 5),
(color & ~7) + (rnd & 7), 1.0, 0.0);
}
}
static void
R_SpikeEffect_QF (const vec3_t org)
{
R_RunSparkEffect_QF (org, 5, 8);
}
static void
R_SuperSpikeEffect_QF (const vec3_t org)
{
R_RunSparkEffect_QF (org, 10, 8);
}
static void
R_KnightSpikeEffect_QF (const vec3_t org)
{
unsigned int count = 10;
if (numparticles >= r_maxparticles)
return;
particle_new (pt_smokecloud, part_tex_smoke, org, 1.0, vec3_origin,
r_realtime + 9.0, 234, 0.25 + qfrandom (0.125), 0.0);
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
while (count--)
particle_new_random (pt_fallfade, part_tex_dot, org, 6, 0.7, 96,
r_realtime + 5.0, 234, 1.0, 0.0);
}
static void
R_WizSpikeEffect_QF (const vec3_t org)
{
unsigned int count = 15;
if (numparticles >= r_maxparticles)
return;
particle_new (pt_smokecloud, part_tex_smoke, org, 2.0, vec3_origin,
r_realtime + 9.0, 63, 0.25 + qfrandom (0.125), 0.0);
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
while (count--)
particle_new_random (pt_fallfade, part_tex_dot, org, 12, 0.7, 96,
r_realtime + 5.0, 63, 1.0, 0.0);
}
static void
R_LavaSplash_QF (const vec3_t org)
{
float vel;
int rnd, i, j;
int k = 256;
vec3_t dir, porg, pvel;
if (numparticles + k >= r_maxparticles) {
return;
} // else if (numparticles + k >= r_maxparticles) {
// k = r_maxparticles - numparticles;
// }
dir[2] = 256;
for (i = -128; i < 128; i += 16) {
for (j = -128; j < 128; j += 16) {
rnd = rand ();
dir[0] = j + (rnd & 7);
dir[1] = i + ((rnd >> 6) & 7);
porg[0] = org[0] + dir[0];
porg[1] = org[1] + dir[1];
porg[2] = org[2] + ((rnd >> 9) & 63);
VectorNormalize (dir);
rnd = rand ();
vel = 50.0 + 0.5 * (float) (rnd & 127);
VectorScale (dir, vel, pvel);
particle_new (pt_grav, part_tex_dot, porg, 3, pvel,
r_realtime + 2.0 + ((rnd >> 7) & 31) * 0.02,
224 + ((rnd >> 12) & 7), 0.75, 0.0);
}
}
}
static void
R_TeleportSplash_QF (const vec3_t org)
{
float vel;
int rnd, i, j, k;
int l = 896;
vec3_t dir, pdir, porg, pvel;
if (numparticles + l >= r_maxparticles) {
return;
} // else if (numparticles + l >= r_maxparticles) {
// l = r_maxparticles - numparticles;
// }
for (k = -24; k < 32; k += 4) {
dir[2] = k * 8;
for (i = -16; i < 16; i += 4) {
dir[1] = i * 8;
for (j = -16; j < 16; j += 4) {
dir[0] = j * 8;
VectorCopy (dir, pdir);
VectorNormalize (pdir);
rnd = rand ();
porg[0] = org[0] + i + (rnd & 3);
porg[1] = org[1] + j + ((rnd >> 2) & 3);
porg[2] = org[2] + k + ((rnd >> 4) & 3);
vel = 50 + ((rnd >> 6) & 63);
VectorScale (pdir, vel, pvel);
particle_new (pt_grav, part_tex_spark, porg, 0.6, pvel,
(r_realtime + 0.2 + (rand () & 15) * 0.01),
(7 + ((rnd >> 12) & 7)), 1.0, 0.0);
}
}
}
}
static void
R_RocketTrail_QF (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
vec3_t old_origin, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 1.5 + qfrandom (1.5);
while (len < maxlen) {
pscalenext = 1.5 + qfrandom (1.5);
dist = (pscale + pscalenext) * 3.0;
percent = len * origlen;
particle_new (pt_smoke, part_tex_smoke, old_origin,
pscale + percent * 4.0, vec3_origin,
r_realtime + 2.0 - percent * 2.0,
12 + (rand () & 3),
0.5 + qfrandom (0.125) - percent * 0.40, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorMultAdd (old_origin, len, vec, old_origin);
pscale = pscalenext;
}
}
static void
R_GrenadeTrail_QF (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
vec3_t old_origin, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 6.0 + qfrandom (7.0);
while (len < maxlen) {
pscalenext = 6.0 + qfrandom (7.0);
dist = (pscale + pscalenext) * 2.0;
percent = len * origlen;
particle_new (pt_smoke, part_tex_smoke, old_origin,
pscale + percent * 4.0, vec3_origin,
r_realtime + 2.0 - percent * 2.0,
1 + (rand () & 3),
0.625 + qfrandom (0.125) - percent * 0.40, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorMultAdd (old_origin, len, vec, old_origin);
pscale = pscalenext;
}
}
static void
R_BloodTrail_QF (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
int j;
vec3_t old_origin, porg, pvel, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 5.0 + qfrandom (10.0);
while (len < maxlen) {
pscalenext = 5.0 + qfrandom (10.0);
dist = (pscale + pscalenext) * 1.5;
for (j = 0; j < 3; j++) {
pvel[j] = qfrandom (24.0) - 12.0;
porg[j] = old_origin[j] + qfrandom (3.0) - 1.5;
}
percent = len * origlen;
pvel[2] -= percent * 40.0;
particle_new (pt_grav, part_tex_smoke, porg, pscale, pvel,
r_realtime + 2.0 - percent * 2.0, 68 + (rand () & 3),
1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorMultAdd (old_origin, len, vec, old_origin);
pscale = pscalenext;
}
}
static void
R_SlightBloodTrail_QF (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
int j;
vec3_t old_origin, porg, pvel, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 1.5 + qfrandom (7.5);
while (len < maxlen) {
pscalenext = 1.5 + qfrandom (7.5);
dist = (pscale + pscalenext) * 1.5;
for (j = 0; j < 3; j++) {
pvel[j] = (qfrandom (12.0) - 6.0);
porg[j] = old_origin[j] + qfrandom (3.0) - 1.5;
}
percent = len * origlen;
pvel[2] -= percent * 40;
particle_new (pt_grav, part_tex_smoke, porg, pscale, pvel,
r_realtime + 1.5 - percent * 1.5, 68 + (rand () & 3),
0.75, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorMultAdd (old_origin, len, vec, old_origin);
pscale = pscalenext;
}
}
static void
R_WizTrail_QF (const entity_t *ent)
{
float maxlen, origlen, percent;
float dist = 3.0, len = 0.0;
static int tracercount;
vec3_t old_origin, pvel, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
percent = len * origlen;
tracercount++;
if (tracercount & 1) {
pvel[0] = 30.0 * vec[1];
pvel[1] = 30.0 * -vec[0];
} else {
pvel[0] = 30.0 * -vec[1];
pvel[1] = 30.0 * vec[0];
}
pvel[2] = 0.0;
particle_new (pt_flame, part_tex_smoke, old_origin,
2.0 + qfrandom (1.0) - percent * 2.0, pvel,
r_realtime + 0.5 - percent * 0.5, 52 + (rand () & 4),
1.0 - percent * 0.125, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_FlameTrail_QF (const entity_t *ent)
{
float maxlen, origlen, percent;
float dist = 3.0, len = 0.0;
static int tracercount;
vec3_t old_origin, pvel, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
percent = len * origlen;
tracercount++;
if (tracercount & 1) {
pvel[0] = 30.0 * vec[1];
pvel[1] = 30.0 * -vec[0];
} else {
pvel[0] = 30.0 * -vec[1];
pvel[1] = 30.0 * vec[0];
}
pvel[2] = 0.0;
particle_new (pt_flame, part_tex_smoke, old_origin,
2.0 + qfrandom (1.0) - percent * 2.0, pvel,
r_realtime + 0.5 - percent * 0.5, 234,
1.0 - percent * 0.125, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_VoorTrail_QF (const entity_t *ent)
{
float maxlen, origlen, percent;
float dist = 3.0, len = 0.0;
int j;
vec3_t subtract, old_origin, porg, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
percent = len * origlen;
for (j = 0; j < 3; j++)
porg[j] = old_origin[j] + qfrandom (16.0) - 8.0;
particle_new (pt_static, part_tex_dot, porg, 1.0 + qfrandom (1.0),
vec3_origin, r_realtime + 0.3 - percent * 0.3,
9 * 16 + 8 + (rand () & 3), 1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_GlowTrail_QF (const entity_t *ent, int glow_color)
{
float maxlen, origlen, percent;
float dist = 3.0, len = 0.0;
int rnd;
vec3_t old_origin, org, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
VectorScale (vec, (maxlen - dist), subtract);
while (len < maxlen) {
percent = len * origlen;
rnd = rand ();
org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5;
org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5;
org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5;
particle_new (pt_smoke, part_tex_dot, org, 1.0, vec3_origin,
r_realtime + 2.0 - percent * 0.2, glow_color, 1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_ParticleExplosion_EE (const vec3_t org)
{
/*
R_NewExplosion (org);
*/
if (numparticles >= r_maxparticles)
return;
particle_new_random (pt_smokecloud, part_tex_smoke, org, 4, 30, 8,
r_realtime + 5.0, rand () & 255,
0.5 + qfrandom (0.25), 0.0);
}
static void
R_TeleportSplash_EE (const vec3_t org)
{
float vel;
int rnd, i, j, k;
int l = 896;
vec3_t dir, porg, pvel;
if (numparticles + l >= r_maxparticles) {
return;
} // else if (numparticles + l >= r_maxparticles) {
// l = r_maxparticles - numparticles;
// }
for (k = -24; k < 32; k += 4) {
dir[2] = k * 8;
for (i = -16; i < 16; i += 4) {
dir[1] = i * 8;
for (j = -16; j < 16; j += 4) {
dir[0] = j * 8;
rnd = rand ();
porg[0] = org[0] + i + (rnd & 3);
porg[1] = org[1] + j + ((rnd >> 2) & 3);
porg[2] = org[2] + k + ((rnd >> 4) & 3);
VectorNormalize (dir);
vel = 50 + ((rnd >> 6) & 63);
VectorScale (dir, vel, pvel);
particle_new (pt_grav, part_tex_spark, porg, 0.6, pvel,
(r_realtime + 0.2 + (rand () & 15) * 0.01),
qfrandom (1.0), 1.0, 0.0);
}
}
}
}
static void
R_RocketTrail_EE (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
vec3_t old_origin, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 1.5 + qfrandom (1.5);
while (len < maxlen) {
pscalenext = 1.5 + qfrandom (1.5);
dist = (pscale + pscalenext) * 3.0;
percent = len * origlen;
particle_new (pt_smoke, part_tex_smoke, old_origin,
pscale + percent * 4.0, vec3_origin,
r_realtime + 2.0 - percent * 2.0,
rand () & 255,
0.5 + qfrandom (0.125) - percent * 0.40, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorScale (vec, len, subtract);
VectorAdd (old_origin, subtract, old_origin);
pscale = pscalenext;
}
}
static void
R_GrenadeTrail_EE (const entity_t *ent)
{
float dist, maxlen, origlen, percent, pscale, pscalenext;
float len = 0.0;
vec3_t old_origin, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, ent->old_origin, vec);
maxlen = VectorNormalize (vec);
origlen = r_frametime / maxlen;
pscale = 6.0 + qfrandom (7.0);
while (len < maxlen) {
pscalenext = 6.0 + qfrandom (7.0);
dist = (pscale + pscalenext) * 2.0;
percent = len * origlen;
particle_new (pt_smoke, part_tex_smoke, old_origin,
pscale + percent * 4.0, vec3_origin,
r_realtime + 2.0 - percent * 2.0,
rand () & 255,
0.625 + qfrandom (0.125) - percent * 0.40, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorScale (vec, len, subtract);
VectorAdd (old_origin, subtract, old_origin);
pscale = pscalenext;
}
}
static void
R_ParticleExplosion_ID (const vec3_t org)
{
unsigned int i;
unsigned int j = 1024;
if (numparticles >= r_maxparticles)
return;
else if (numparticles + j >= r_maxparticles)
j = r_maxparticles - numparticles;
for (i = 0; i < j >> 1; i++) {
particle_new_random (pt_explode, part_tex_dot, org, 16, 1.0, 256,
r_realtime + 5.0, ramp1[0], 1.0, i & 3);
}
for (i = 0; i < j / 2; i++) {
particle_new_random (pt_explode2, part_tex_dot, org, 16, 1.0, 256,
r_realtime + 5.0, ramp1[0], 1.0, i & 3);
}
}
static void
R_BlobExplosion_ID (const vec3_t org)
{
unsigned int i;
unsigned int j = 1024;
if (numparticles >= r_maxparticles)
return;
else if (numparticles + j >= r_maxparticles)
j = r_maxparticles - numparticles;
for (i = 0; i < j >> 1; i++) {
particle_new_random (pt_blob, part_tex_dot, org, 12, 1.0, 256,
r_realtime + 1.0 + (rand () & 8) * 0.05,
66 + i % 6, 1.0, 0.0);
}
for (i = 0; i < j / 2; i++) {
particle_new_random (pt_blob2, part_tex_dot, org, 12, 1.0, 256,
r_realtime + 1.0 + (rand () & 8) * 0.05,
150 + i % 6, 1.0, 0.0);
}
}
static inline void // FIXME: inline?
R_RunParticleEffect_ID (const vec3_t org, const vec3_t dir, int color,
int count)
{
float scale;
int i;
vec3_t porg;
if (numparticles >= r_maxparticles)
return;
if (count > 130) // calculate scale before clipping to particle max
scale = 3.0;
else if (count > 20)
scale = 2.0;
else
scale = 1.0;
if (numparticles + count >= r_maxparticles)
count = r_maxparticles - numparticles;
for (i = 0; i < count; i++) {
int rnd = rand ();
porg[0] = org[0] + scale * (((rnd >> 3) & 15) - 8);
porg[1] = org[1] + scale * (((rnd >> 7) & 15) - 8);
porg[2] = org[2] + scale * (((rnd >> 11) & 15) - 8);
// Note that ParseParticleEffect handles (dir * 15)
particle_new (pt_grav, part_tex_dot, porg, 1.0, dir,
r_realtime + 0.1 * (i % 5),
(color & ~7) + (rnd & 7), 1.0, 0.0);
}
}
static void
R_BloodPuffEffect_ID (const vec3_t org, int count)
{
R_RunParticleEffect_ID (org, vec3_origin, 73, count);
}
static void
R_GunshotEffect_ID (const vec3_t org, int count)
{
R_RunParticleEffect_ID (org, vec3_origin, 0, count);
}
static void
R_LightningBloodEffect_ID (const vec3_t org)
{
R_RunParticleEffect_ID (org, vec3_origin, 225, 50);
}
static void
R_SpikeEffect_ID (const vec3_t org)
{
R_RunParticleEffect_ID (org, vec3_origin, 0, 10);
}
static void
R_SuperSpikeEffect_ID (const vec3_t org)
{
R_RunParticleEffect_ID (org, vec3_origin, 0, 20);
}
static void
R_KnightSpikeEffect_ID (const vec3_t org)
{
R_RunParticleEffect_ID (org, vec3_origin, 226, 20);
}
static void
R_WizSpikeEffect_ID (const vec3_t org)
{
R_RunParticleEffect_ID (org, vec3_origin, 20, 30);
}
static void
R_LavaSplash_ID (const vec3_t org)
{
float vel;
int rnd, i, j;
int k = 256;
vec3_t dir, porg, pvel;
if (numparticles + k >= r_maxparticles) {
return;
} // else if (numparticles + k >= r_maxparticles) {
// k = r_maxparticles - numparticles;
// }
dir[2] = 256;
for (i = -128; i < 128; i += 16) {
for (j = -128; j < 128; j += 16) {
rnd = rand ();
dir[0] = j + (rnd & 7);
dir[1] = i + ((rnd >> 6) & 7);
porg[0] = org[0] + dir[0];
porg[1] = org[1] + dir[1];
porg[2] = org[2] + ((rnd >> 9) & 63);
VectorNormalize (dir);
rnd = rand ();
vel = 50 + (rnd & 63);
VectorScale (dir, vel, pvel);
particle_new (pt_grav, part_tex_dot, porg, 1.0, pvel,
r_realtime + 2 + ((rnd >> 7) & 31) * 0.02,
224 + ((rnd >> 12) & 7), 1.0, 0.0);
}
}
}
static void
R_TeleportSplash_ID (const vec3_t org)
{
float vel;
int rnd, i, j, k;
int l = 896;
vec3_t dir, pdir, porg, pvel;
if (numparticles + l >= r_maxparticles) {
return;
} // else if (numparticles + l >= r_maxparticles) {
// l = r_maxparticles - numparticles;
// }
for (k = -24; k < 32; k += 4) {
dir[2] = k * 8;
for (i = -16; i < 16; i += 4) {
dir[1] = i * 8;
for (j = -16; j < 16; j += 4) {
dir[0] = j * 8;
VectorCopy (dir, pdir);
VectorNormalize (pdir);
rnd = rand ();
porg[0] = org[0] + i + (rnd & 3);
porg[1] = org[1] + j + ((rnd >> 2) & 3);
porg[2] = org[2] + k + ((rnd >> 4) & 3);
vel = 50 + ((rnd >> 6) & 63);
VectorScale (pdir, vel, pvel);
particle_new (pt_grav, part_tex_dot, porg, 1.0, pvel,
(r_realtime + 0.2 + (rand () & 7) * 0.02),
(7 + ((rnd >> 12) & 7)), 1.0, 0.0);
}
}
}
}
static void
R_DarkFieldParticles_ID (const entity_t *ent)
{
int i, j, k, l = 64;
unsigned int rnd;
float vel;
vec3_t dir, org, porg, pvel;
if (numparticles + l >= r_maxparticles) {
return;
} // else if (numparticles + l >= r_maxparticles) {
// l = r_maxparticles - numparticles;
// }
VectorCopy (ent->origin, org);
for (i = -16; i < 16; i += 8) {
dir [1] = i * 8;
for (j = -16; j < 16; j += 8) {
dir [0] = j * 8;
for (k = 0; k < 32; k += 8) {
dir [2] = k * 8;
rnd = rand ();
porg[0] = org[0] + i + ((rnd >> 3) & 3);
porg[1] = org[1] + j + ((rnd >> 5) & 3);
porg[2] = org[2] + k + ((rnd >> 7) & 3);
VectorNormalize (dir);
vel = 50 + ((rnd >> 9) & 63);
VectorScale (dir, vel, pvel);
particle_new (pt_slowgrav, part_tex_dot, porg, 1.5, pvel,
(r_realtime + 0.2 + (rnd & 7) * 0.02),
(150 + rand () % 6), 1.0, 0.0);
}
}
}
}
static vec3_t avelocities[NUMVERTEXNORMALS];
static void
R_EntityParticles_ID (const entity_t *ent)
{
int i, j = NUMVERTEXNORMALS;
float angle, sp, sy, cp, cy; // cr, sr
float beamlength = 16.0, dist = 64.0;
vec3_t forward, porg;
if (numparticles + j >= r_maxparticles) {
return;
} else if (numparticles + j >= r_maxparticles) {
j = r_maxparticles - numparticles;
}
if (!avelocities[0][0]) {
for (i = 0; i < NUMVERTEXNORMALS * 3; i++)
avelocities[0][i] = (rand () & 255) * 0.01;
}
for (i = 0; i < j; i++) {
angle = r_realtime * avelocities[i][0];
cy = cos (angle);
sy = sin (angle);
angle = r_realtime * avelocities[i][1];
cp = cos (angle);
sp = sin (angle);
// Next 3 lines results aren't currently used, may be in future. --Despair
// angle = r_realtime * avelocities[i][2];
// sr = sin (angle);
// cr = cos (angle);
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
porg[0] = ent->origin[0] + vertex_normals[i][0] * dist +
forward[0] * beamlength;
porg[1] = ent->origin[1] + vertex_normals[i][1] * dist +
forward[1] * beamlength;
porg[2] = ent->origin[2] + vertex_normals[i][2] * dist +
forward[2] * beamlength;
particle_new (pt_explode, part_tex_dot, porg, 1.0, vec3_origin,
r_realtime + 0.01, 0x6f, 1.0, 0);
}
}
static void
R_RocketTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
int ramp, rnd;
vec3_t old_origin, org, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, ent->old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, (maxlen - dist), subtract);
while (len < maxlen) {
rnd = rand ();
org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5;
org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5;
org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5;
ramp = rnd & 3;
particle_new (pt_fire, part_tex_dot, org, 1.0, vec3_origin,
r_realtime + 2.0, ramp3[ramp], 1.0, ramp);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_GrenadeTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
unsigned int ramp, rnd;
vec3_t old_origin, org, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, ent->old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
rnd = rand ();
org[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5;
org[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5;
org[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5;
ramp = (rnd & 3) + 2;
particle_new (pt_fire, part_tex_dot, org, 1.0, vec3_origin,
r_realtime + 2.0, ramp3[ramp], 1.0, ramp);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_BloodTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
unsigned int rnd;
vec3_t old_origin, subtract, vec, porg;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
rnd = rand ();
porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5;
porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5;
porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5;
particle_new (pt_grav, part_tex_dot, porg, 1.0, vec3_origin,
r_realtime + 2.0, 67 + (rnd & 3), 1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_SlightBloodTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 6.0, len = 0.0;
unsigned int rnd;
vec3_t old_origin, porg, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
rnd = rand ();
porg[0] = old_origin[0] + ((rnd >> 12) & 7) * (5.0/7.0) - 2.5;
porg[1] = old_origin[1] + ((rnd >> 9) & 7) * (5.0/7.0) - 2.5;
porg[2] = old_origin[2] + ((rnd >> 6) & 7) * (5.0/7.0) - 2.5;
particle_new (pt_grav, part_tex_dot, porg, 1.0, vec3_origin,
r_realtime + 1.5, 67 + (rnd & 3), 1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_WizTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
static int tracercount;
vec3_t old_origin, pvel, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
tracercount++;
if (tracercount & 1) {
pvel[0] = 30.0 * vec[1];
pvel[1] = 30.0 * -vec[0];
} else {
pvel[0] = 30.0 * -vec[1];
pvel[1] = 30.0 * vec[0];
}
pvel[2] = 0.0;
particle_new (pt_static, part_tex_dot, old_origin, 1.0, pvel,
r_realtime + 0.5, 52 + ((tracercount & 4) << 1), 1.0,
0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_FlameTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
static int tracercount;
vec3_t old_origin, pvel, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
tracercount++;
if (tracercount & 1) {
pvel[0] = 30.0 * vec[1];
pvel[1] = 30.0 * -vec[0];
} else {
pvel[0] = 30.0 * -vec[1];
pvel[1] = 30.0 * vec[0];
}
pvel[2] = 0.0;
particle_new (pt_static, part_tex_dot, old_origin, 1.0, pvel,
r_realtime + 0.5, 230 + ((tracercount & 4) << 1), 1.0,
0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
R_VoorTrail_ID (const entity_t *ent)
{
float maxlen;
float dist = 3.0, len = 0.0;
unsigned int rnd;
vec3_t old_origin, porg, subtract, vec;
if (numparticles >= r_maxparticles)
return;
VectorCopy (ent->old_origin, old_origin);
VectorSubtract (ent->origin, old_origin, vec);
maxlen = VectorNormalize (vec);
VectorScale (vec, maxlen - dist, subtract);
while (len < maxlen) {
rnd = rand ();
porg[0] = old_origin[0] + ((rnd >> 3) & 15) - 7.5;
porg[1] = old_origin[1] + ((rnd >> 7) & 15) - 7.5;
porg[2] = old_origin[2] + ((rnd >> 11) & 15) - 7.5;
particle_new (pt_static, part_tex_dot, porg, 1.0, vec3_origin,
r_realtime + 0.3, 9 * 16 + 8 + (rnd & 3), 1.0, 0.0);
if (numparticles >= r_maxparticles)
break;
len += dist;
VectorAdd (old_origin, subtract, old_origin);
}
}
static void
draw_qf_particles (void)
{
byte *at;
int activeparticles, maxparticle, j, vacount;
unsigned k;
float minparticledist, scale;
particle_t *part;
vec3_t up_scale, right_scale, up_right_scale, down_right_scale;
partvert_t *VA;
mat4_t vp_mat;
quat_t fog;
Mat4Mult (glsl_projection, glsl_view, vp_mat);
qfglDepthMask (GL_FALSE);
qfglUseProgram (quake_part.program);
qfglEnableVertexAttribArray (quake_part.vertex.location);
qfglEnableVertexAttribArray (quake_part.color.location);
qfglEnableVertexAttribArray (quake_part.st.location);
VectorCopy (Fog_GetColor (), fog);
fog[3] = Fog_GetDensity () / 64.0;
qfglUniform4fv (quake_part.fog.location, 1, fog);
qfglUniformMatrix4fv (quake_part.mvp_matrix.location, 1, false, vp_mat);
qfglUniform1i (quake_part.texture.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, part_tex);
// LordHavoc: particles should not affect zbuffer
qfglDepthMask (GL_FALSE);
minparticledist = DotProduct (r_refdef.vieworg, vpn) +
r_particles_nearclip->value;
activeparticles = 0;
vacount = 0;
VA = particleVertexArray;
maxparticle = -1;
j = 0;
for (k = 0, part = particles; k < numparticles; k++, part++) {
// Don't render particles too close to us.
// Note, we must still do physics and such on them.
if (!(DotProduct (part->org, vpn) < minparticledist)) {
at = (byte *) &d_8to24table[(byte) part->color];
VA[0].color[0] = at[0];
VA[0].color[1] = at[1];
VA[0].color[2] = at[2];
VA[0].color[3] = part->alpha * 255;
memcpy (VA[1].color, VA[0].color, sizeof (VA[0].color));
memcpy (VA[2].color, VA[0].color, sizeof (VA[0].color));
memcpy (VA[3].color, VA[0].color, sizeof (VA[0].color));
switch (part->tex) {
case part_tex_dot:
VA[0].texcoord[0] = 0.0;
VA[0].texcoord[1] = 0.0;
VA[1].texcoord[0] = 0.5;
VA[1].texcoord[1] = 0.0;
VA[2].texcoord[0] = 0.5;
VA[2].texcoord[1] = 0.5;
VA[3].texcoord[0] = 0.0;
VA[3].texcoord[1] = 0.5;
break;
case part_tex_spark:
VA[0].texcoord[0] = 0.5;
VA[0].texcoord[1] = 0.0;
VA[1].texcoord[0] = 1.0;
VA[1].texcoord[1] = 0.0;
VA[2].texcoord[0] = 1.0;
VA[2].texcoord[1] = 0.5;
VA[3].texcoord[0] = 0.5;
VA[3].texcoord[1] = 0.5;
break;
case part_tex_smoke:
VA[0].texcoord[0] = 0.0;
VA[0].texcoord[1] = 0.5;
VA[1].texcoord[0] = 0.5;
VA[1].texcoord[1] = 0.5;
VA[2].texcoord[0] = 0.5;
VA[2].texcoord[1] = 1.0;
VA[3].texcoord[0] = 0.0;
VA[3].texcoord[1] = 1.0;
break;
}
scale = part->scale;
VectorScale (vup, scale, up_scale);
VectorScale (vright, scale, right_scale);
VectorAdd (right_scale, up_scale, up_right_scale);
VectorSubtract (right_scale, up_scale, down_right_scale);
VectorAdd (part->org, down_right_scale, VA[0].vertex);
VectorSubtract (part->org, up_right_scale, VA[1].vertex);
VectorSubtract (part->org, down_right_scale, VA[2].vertex);
VectorAdd (part->org, up_right_scale, VA[3].vertex);
VA += 4;
vacount += 6;
}
part->phys (part);
// LordHavoc: immediate removal of unnecessary particles (must be done
// to ensure compactor below operates properly in all cases)
if (part->die < r_realtime) {
freeparticles[j++] = part;
} else {
maxparticle = k;
activeparticles++;
}
}
qfglVertexAttribPointer (quake_part.vertex.location, 3, GL_FLOAT,
0, sizeof (partvert_t),
&particleVertexArray[0].vertex);
qfglVertexAttribPointer (quake_part.color.location, 4, GL_UNSIGNED_BYTE,
1, sizeof (partvert_t),
&particleVertexArray[0].color);
qfglVertexAttribPointer (quake_part.st.location, 2, GL_FLOAT,
0, sizeof (partvert_t),
&particleVertexArray[0].texcoord);
qfglDrawElements (GL_TRIANGLES, vacount, GL_UNSIGNED_SHORT, pVAindices);
k = 0;
while (maxparticle >= activeparticles) {
*freeparticles[k++] = particles[maxparticle--];
while (maxparticle >= activeparticles &&
particles[maxparticle].die <= r_realtime)
maxparticle--;
}
numparticles = activeparticles;
qfglDepthMask (GL_TRUE);
qfglDisableVertexAttribArray (quake_part.vertex.location);
qfglDisableVertexAttribArray (quake_part.color.location);
qfglDisableVertexAttribArray (quake_part.st.location);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglDisable (GL_TEXTURE_2D);
}
static void
draw_id_particles (void)
{
int activeparticles, maxparticle, j, vacount;
unsigned k;
float minparticledist;
particle_t *part;
partvert_t *VA;
mat4_t vp_mat;
quat_t fog;
Mat4Mult (glsl_projection, glsl_view, vp_mat);
// LordHavoc: particles should not affect zbuffer
qfglDepthMask (GL_FALSE);
qfglUseProgram (quake_point.program);
qfglEnableVertexAttribArray (quake_point.vertex.location);
qfglEnableVertexAttribArray (quake_point.color.location);
qfglUniformMatrix4fv (quake_point.mvp_matrix.location, 1, false, vp_mat);
VectorCopy (Fog_GetColor (), fog);
fog[3] = Fog_GetDensity () / 64.0;
qfglUniform4fv (quake_point.fog.location, 1, fog);
qfglUniform1i (quake_point.palette.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, glsl_palette);
minparticledist = DotProduct (r_refdef.vieworg, vpn) +
r_particles_nearclip->value;
activeparticles = 0;
vacount = 0;
VA = particleVertexArray;
maxparticle = -1;
j = 0;
for (k = 0, part = particles; k < numparticles; k++, part++) {
// Don't render particles too close to us.
// Note, we must still do physics and such on them.
if (!(DotProduct (part->org, vpn) < minparticledist)) {
VA[0].color[0] = (byte) part->color;
VectorCopy (part->org, VA[0].vertex);
VA++;
vacount++;
}
part->phys (part);
// LordHavoc: immediate removal of unnecessary particles (must be done
// to ensure compactor below operates properly in all cases)
if (part->die < r_realtime) {
freeparticles[j++] = part;
} else {
maxparticle = k;
activeparticles++;
}
}
qfglVertexAttribPointer (quake_point.vertex.location, 3, GL_FLOAT,
0, sizeof (partvert_t),
&particleVertexArray[0].vertex);
qfglVertexAttribPointer (quake_point.color.location, 1, GL_UNSIGNED_BYTE,
1, sizeof (partvert_t),
&particleVertexArray[0].color);
qfglDrawArrays (GL_POINTS, 0, vacount);
k = 0;
while (maxparticle >= activeparticles) {
*freeparticles[k++] = particles[maxparticle--];
while (maxparticle >= activeparticles &&
particles[maxparticle].die <= r_realtime)
maxparticle--;
}
numparticles = activeparticles;
qfglDepthMask (GL_TRUE);
qfglDisableVertexAttribArray (quake_point.vertex.location);
qfglDisableVertexAttribArray (quake_point.color.location);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglDisable (GL_TEXTURE_2D);
}
void
R_DrawParticles (void)
{
if (!r_particles->int_val)
return;
if (r_particles_style->int_val) {
draw_qf_particles ();
} else {
draw_id_particles ();
}
}
void
r_easter_eggs_f (cvar_t *var)
{
if (easter_eggs) {
if (easter_eggs->int_val) {
R_ParticleExplosion = R_ParticleExplosion_EE;
R_TeleportSplash = R_TeleportSplash_EE;
R_RocketTrail = R_RocketTrail_EE;
R_GrenadeTrail = R_GrenadeTrail_EE;
} else if (r_particles_style) {
if (r_particles_style->int_val) {
R_ParticleExplosion = R_ParticleExplosion_QF;
R_TeleportSplash = R_TeleportSplash_QF;
R_RocketTrail = R_RocketTrail_QF;
R_GrenadeTrail = R_GrenadeTrail_QF;
} else {
R_ParticleExplosion = R_ParticleExplosion_ID;
R_TeleportSplash = R_TeleportSplash_ID;
R_RocketTrail = R_RocketTrail_ID;
R_GrenadeTrail = R_GrenadeTrail_ID;
}
}
}
}
void
r_particles_style_f (cvar_t *var)
{
if (r_particles_style) {
if (r_particles_style->int_val) {
R_BlobExplosion = R_BlobExplosion_QF;
R_LavaSplash = R_LavaSplash_QF;
R_BloodPuffEffect = R_BloodPuffEffect_QF;
R_GunshotEffect = R_GunshotEffect_QF;
R_LightningBloodEffect = R_LightningBloodEffect_QF;
R_RunParticleEffect = R_RunParticleEffect_QF;
R_SpikeEffect = R_SpikeEffect_QF;
R_SuperSpikeEffect = R_SuperSpikeEffect_QF;
R_KnightSpikeEffect = R_KnightSpikeEffect_QF;
R_WizSpikeEffect = R_WizSpikeEffect_QF;
R_BloodTrail = R_BloodTrail_QF;
R_SlightBloodTrail = R_SlightBloodTrail_QF;
R_WizTrail = R_WizTrail_QF;
R_FlameTrail = R_FlameTrail_QF;
R_VoorTrail = R_VoorTrail_QF;
} else {
R_BlobExplosion = R_BlobExplosion_ID;
R_LavaSplash = R_LavaSplash_ID;
R_BloodPuffEffect = R_BloodPuffEffect_ID;
R_GunshotEffect = R_GunshotEffect_ID;
R_LightningBloodEffect = R_LightningBloodEffect_ID;
R_RunParticleEffect = R_RunParticleEffect_ID;
R_SpikeEffect = R_SpikeEffect_ID;
R_SuperSpikeEffect = R_SuperSpikeEffect_ID;
R_KnightSpikeEffect = R_KnightSpikeEffect_ID;
R_WizSpikeEffect = R_WizSpikeEffect_ID;
R_BloodTrail = R_BloodTrail_ID;
R_SlightBloodTrail = R_SlightBloodTrail_ID;
R_WizTrail = R_WizTrail_ID;
R_FlameTrail = R_FlameTrail_ID;
R_VoorTrail = R_VoorTrail_ID;
}
}
R_DarkFieldParticles = R_DarkFieldParticles_ID;
R_EntityParticles = R_EntityParticles_ID;
R_ParticleExplosion2 = R_ParticleExplosion2_QF;
R_GlowTrail = R_GlowTrail_QF;
// Handle R_GrenadeTrail, R_RocketTrail, R_ParticleExplosion,
// R_TeleportSplash
r_easter_eggs_f (easter_eggs);
}
static void
R_ParticleFunctionInit (void)
{
r_particles_style_f (r_particles_style);
r_easter_eggs_f (easter_eggs);
}
VISIBLE void
R_Particles_Init_Cvars (void)
{
R_ParticleFunctionInit ();
}
VISIBLE void
R_Particle_New (ptype_t type, int texnum, const vec3_t org, float scale,
const vec3_t vel, float die, int color, float alpha, float ramp)
{
if (numparticles >= r_maxparticles)
return;
particle_new (type, texnum, org, scale, vel, die, color, alpha, ramp);
}
VISIBLE void
R_Particle_NewRandom (ptype_t type, int texnum, const vec3_t org, int org_fuzz,
float scale, int vel_fuzz, float die, int color,
float alpha, float ramp)
{
if (numparticles >= r_maxparticles)
return;
particle_new_random (type, texnum, org, org_fuzz, scale, vel_fuzz, die,
color, alpha, ramp);
}