mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 12:52:46 +00:00
5f93c115ff
They're now an enum, and the flag part of the name is all lowercase, but now the flag definitions and names list will never get out of sync.
2044 lines
52 KiB
C
2044 lines
52 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
|
|
|
|
#define NH_DEFINE
|
|
#include "namehack.h"
|
|
|
|
#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/entity.h"
|
|
#include "QF/image.h"
|
|
#include "QF/mersenne.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_internal.h"
|
|
|
|
static mtstate_t mt; // private PRNG state
|
|
|
|
//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 *particle_point_vert_effects[] =
|
|
{
|
|
"QuakeForge.Vertex.particle.point",
|
|
0
|
|
};
|
|
|
|
static const char *particle_point_frag_effects[] =
|
|
{
|
|
"QuakeForge.Fragment.fog",
|
|
"QuakeForge.Fragment.palette",
|
|
"QuakeForge.Fragment.particle.point",
|
|
0
|
|
};
|
|
|
|
static const char *particle_textured_vert_effects[] =
|
|
{
|
|
"QuakeForge.Vertex.particle.textured",
|
|
0
|
|
};
|
|
|
|
static const char *particle_textured_frag_effects[] =
|
|
{
|
|
"QuakeForge.Fragment.fog",
|
|
"QuakeForge.Fragment.palette",
|
|
"QuakeForge.Fragment.particle.textured",
|
|
0
|
|
};
|
|
|
|
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 = mtwist_rand (&mt);
|
|
porg[0] = o_fuzz * ((rnd & 63) - 31.5) / 63.0 + org[0];
|
|
porg[1] = o_fuzz * (((rnd >> 6) & 63) - 31.5) / 63.0 + org[1];
|
|
porg[2] = o_fuzz * (((rnd >> 10) & 63) - 31.5) / 63.0 + org[2];
|
|
rnd = mtwist_rand (&mt);
|
|
pvel[0] = v_fuzz * ((rnd & 63) - 31.5) / 63.0;
|
|
pvel[1] = v_fuzz * (((rnd >> 6) & 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);
|
|
}
|
|
*/
|
|
|
|
void
|
|
glsl_R_ClearParticles (void)
|
|
{
|
|
numparticles = 0;
|
|
}
|
|
|
|
void
|
|
glsl_R_InitParticles (void)
|
|
{
|
|
shader_t *vert_shader, *frag_shader;
|
|
unsigned i;
|
|
int vert;
|
|
int frag;
|
|
float v[2] = {0, 0};
|
|
byte data[64][64][2];
|
|
tex_t *tex;
|
|
|
|
mtwist_seed (&mt, 0xdeadbeef);
|
|
|
|
qfeglEnable (GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
qfeglGetFloatv (GL_ALIASED_POINT_SIZE_RANGE, v);
|
|
Sys_MaskPrintf (SYS_glsl, "point size: %g - %g\n", v[0], v[1]);
|
|
|
|
vert_shader = GLSL_BuildShader (particle_point_vert_effects);
|
|
frag_shader = GLSL_BuildShader (particle_point_frag_effects);
|
|
vert = GLSL_CompileShader ("quakepnt.vert", vert_shader,
|
|
GL_VERTEX_SHADER);
|
|
frag = GLSL_CompileShader ("quakepnt.frag", frag_shader,
|
|
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);
|
|
GLSL_FreeShader (vert_shader);
|
|
GLSL_FreeShader (frag_shader);
|
|
|
|
vert_shader = GLSL_BuildShader (particle_textured_vert_effects);
|
|
frag_shader = GLSL_BuildShader (particle_textured_frag_effects);
|
|
vert = GLSL_CompileShader ("quakepar.vert", vert_shader,
|
|
GL_VERTEX_SHADER);
|
|
frag = GLSL_CompileShader ("quakepar.frag", frag_shader,
|
|
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);
|
|
GLSL_FreeShader (vert_shader);
|
|
GLSL_FreeShader (frag_shader);
|
|
|
|
memset (data, 0, sizeof (data));
|
|
qfeglGenTextures (1, &part_tex);
|
|
qfeglBindTexture (GL_TEXTURE_2D, part_tex);
|
|
qfeglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
qfeglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
qfeglTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 64, 64, 0,
|
|
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
|
|
tex = R_DotParticleTexture ();
|
|
qfeglTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 32, 32, GL_LUMINANCE_ALPHA,
|
|
GL_UNSIGNED_BYTE, tex->data);
|
|
free (tex);
|
|
tex = R_SparkParticleTexture ();
|
|
qfeglTexSubImage2D (GL_TEXTURE_2D, 0, 32, 0, 32, 32, GL_LUMINANCE_ALPHA,
|
|
GL_UNSIGNED_BYTE, tex->data);
|
|
free (tex);
|
|
tex = R_SmokeParticleTexture ();
|
|
qfeglTexSubImage2D (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
|
|
glsl_R_ReadPointFile_f (void)
|
|
{
|
|
const char *name;
|
|
char *mapname;
|
|
int c, r;
|
|
vec3_t org;
|
|
QFile *f;
|
|
|
|
mapname = strdup (r_worldentity.renderer.model->path);
|
|
if (!mapname)
|
|
Sys_Error ("Can't duplicate mapname!");
|
|
QFS_StripExtension (mapname, mapname);
|
|
|
|
name = va (0, "%s.pts", mapname);
|
|
free (mapname);
|
|
|
|
f = QFS_FOpenFile (name);
|
|
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,
|
|
vr_data.realtime + 5.0, (mtwist_rand (&mt) & 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,
|
|
vr_data.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,
|
|
vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 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,
|
|
vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 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, vr_data.realtime + 9, 12 + (mtwist_rand (&mt) & 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 = mtwist_rand (&mt) & 7;
|
|
|
|
particle_new_random (pt_fallfadespark, part_tex_dot, org, orgfuzz,
|
|
0.7, 96, vr_data.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,
|
|
vr_data.realtime + 99.0, 70 + (mtwist_rand (&mt) & 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,
|
|
vr_data.realtime + 9.0, 12 + (mtwist_rand (&mt) & 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,
|
|
vr_data.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 = mtwist_rand (&mt);
|
|
|
|
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,
|
|
vr_data.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,
|
|
vr_data.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,
|
|
vr_data.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,
|
|
vr_data.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,
|
|
vr_data.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 = mtwist_rand (&mt);
|
|
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 = mtwist_rand (&mt);
|
|
vel = 50.0 + 0.5 * (float) (rnd & 127);
|
|
VectorScale (dir, vel, pvel);
|
|
particle_new (pt_grav, part_tex_dot, porg, 3, pvel,
|
|
vr_data.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 = mtwist_rand (&mt);
|
|
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,
|
|
(vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 2.0 - percent * 2.0,
|
|
12 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 2.0 - percent * 2.0,
|
|
1 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 2.0 - percent * 2.0,
|
|
68 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 1.5 - percent * 1.5,
|
|
68 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 0.5 - percent * 0.5,
|
|
52 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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, vr_data.realtime + 0.3 - percent * 0.3,
|
|
9 * 16 + 8 + (mtwist_rand (&mt) & 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);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.frametime / maxlen;
|
|
VectorScale (vec, (maxlen - dist), subtract);
|
|
|
|
while (len < maxlen) {
|
|
percent = len * origlen;
|
|
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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,
|
|
vr_data.realtime + 5.0, mtwist_rand (&mt) & 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 = mtwist_rand (&mt);
|
|
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,
|
|
(vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 2.0 - percent * 2.0,
|
|
mtwist_rand (&mt) & 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, ent->old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
origlen = vr_data.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,
|
|
vr_data.realtime + 2.0 - percent * 2.0,
|
|
mtwist_rand (&mt) & 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,
|
|
vr_data.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,
|
|
vr_data.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,
|
|
vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 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,
|
|
vr_data.realtime + 1.0 + (mtwist_rand (&mt) & 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 = mtwist_rand (&mt);
|
|
|
|
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,
|
|
vr_data.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 = mtwist_rand (&mt);
|
|
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 = mtwist_rand (&mt);
|
|
vel = 50 + (rnd & 63);
|
|
VectorScale (dir, vel, pvel);
|
|
particle_new (pt_grav, part_tex_dot, porg, 1.0, pvel,
|
|
vr_data.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 = mtwist_rand (&mt);
|
|
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,
|
|
(vr_data.realtime + 0.2 + (mtwist_rand (&mt) & 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 (Transform_GetWorldPosition (ent->transform), 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 = mtwist_rand (&mt);
|
|
|
|
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,
|
|
(vr_data.realtime + 0.2 + (rnd & 7) * 0.02),
|
|
(150 + mtwist_rand (&mt) % 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;
|
|
vec3_t org;
|
|
|
|
if (numparticles + j >= r_maxparticles) {
|
|
return;
|
|
} else if (numparticles + j >= r_maxparticles) {
|
|
j = r_maxparticles - numparticles;
|
|
}
|
|
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
|
|
for (i = 0; i < NUMVERTEXNORMALS; i++) {
|
|
int k;
|
|
for (k = 0; k < 3; k++) {
|
|
avelocities[i][k] = (mtwist_rand (&mt) & 255) * 0.01;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < j; i++) {
|
|
angle = vr_data.realtime * avelocities[i][0];
|
|
cy = cos (angle);
|
|
sy = sin (angle);
|
|
angle = vr_data.realtime * avelocities[i][1];
|
|
cp = cos (angle);
|
|
sp = sin (angle);
|
|
// Next 3 lines results aren't currently used, may be in future. --Despair
|
|
// angle = vr_data.realtime * avelocities[i][2];
|
|
// sr = sin (angle);
|
|
// cr = cos (angle);
|
|
|
|
forward[0] = cp * cy;
|
|
forward[1] = cp * sy;
|
|
forward[2] = -sp;
|
|
|
|
porg[0] = org[0] + vertex_normals[i][0] * dist +
|
|
forward[0] * beamlength;
|
|
porg[1] = org[1] + vertex_normals[i][1] * dist +
|
|
forward[1] * beamlength;
|
|
porg[2] = org[2] + vertex_normals[i][2] * dist +
|
|
forward[2] * beamlength;
|
|
particle_new (pt_explode, part_tex_dot, porg, 1.0, vec3_origin,
|
|
vr_data.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);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, ent->old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
VectorScale (vec, (maxlen - dist), subtract);
|
|
|
|
while (len < maxlen) {
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, ent->old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
VectorScale (vec, maxlen - dist, subtract);
|
|
|
|
while (len < maxlen) {
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
VectorScale (vec, maxlen - dist, subtract);
|
|
|
|
while (len < maxlen) {
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
VectorScale (vec, maxlen - dist, subtract);
|
|
|
|
while (len < maxlen) {
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, 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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, 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,
|
|
vr_data.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;
|
|
vec3_t org;
|
|
|
|
if (numparticles >= r_maxparticles)
|
|
return;
|
|
|
|
VectorCopy (ent->old_origin, old_origin);
|
|
VectorCopy (Transform_GetWorldPosition (ent->transform), org);
|
|
VectorSubtract (org, old_origin, vec);
|
|
maxlen = VectorNormalize (vec);
|
|
VectorScale (vec, maxlen - dist, subtract);
|
|
|
|
while (len < maxlen) {
|
|
rnd = mtwist_rand (&mt);
|
|
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,
|
|
vr_data.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;
|
|
mat4f_t vp_mat;
|
|
quat_t fog;
|
|
|
|
mmulf (vp_mat, glsl_projection, glsl_view);
|
|
|
|
qfeglDepthMask (GL_FALSE);
|
|
qfeglUseProgram (quake_part.program);
|
|
qfeglEnableVertexAttribArray (quake_part.vertex.location);
|
|
qfeglEnableVertexAttribArray (quake_part.color.location);
|
|
qfeglEnableVertexAttribArray (quake_part.st.location);
|
|
|
|
glsl_Fog_GetColor (fog);
|
|
fog[3] = glsl_Fog_GetDensity () / 64.0;
|
|
qfeglUniform4fv (quake_part.fog.location, 1, fog);
|
|
|
|
qfeglUniformMatrix4fv (quake_part.mvp_matrix.location, 1, false,
|
|
&vp_mat[0][0]);
|
|
|
|
qfeglUniform1i (quake_part.texture.location, 0);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 0);
|
|
qfeglEnable (GL_TEXTURE_2D);
|
|
qfeglBindTexture (GL_TEXTURE_2D, part_tex);
|
|
|
|
// LordHavoc: particles should not affect zbuffer
|
|
qfeglDepthMask (GL_FALSE);
|
|
|
|
minparticledist = DotProduct (r_refdef.viewposition, 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 < vr_data.realtime) {
|
|
freeparticles[j++] = part;
|
|
} else {
|
|
maxparticle = k;
|
|
activeparticles++;
|
|
}
|
|
}
|
|
qfeglVertexAttribPointer (quake_part.vertex.location, 3, GL_FLOAT,
|
|
0, sizeof (partvert_t),
|
|
&particleVertexArray[0].vertex);
|
|
qfeglVertexAttribPointer (quake_part.color.location, 4, GL_UNSIGNED_BYTE,
|
|
1, sizeof (partvert_t),
|
|
&particleVertexArray[0].color);
|
|
qfeglVertexAttribPointer (quake_part.st.location, 2, GL_FLOAT,
|
|
0, sizeof (partvert_t),
|
|
&particleVertexArray[0].texcoord);
|
|
qfeglDrawElements (GL_TRIANGLES, vacount, GL_UNSIGNED_SHORT, pVAindices);
|
|
|
|
k = 0;
|
|
while (maxparticle >= activeparticles) {
|
|
*freeparticles[k++] = particles[maxparticle--];
|
|
while (maxparticle >= activeparticles &&
|
|
particles[maxparticle].die <= vr_data.realtime)
|
|
maxparticle--;
|
|
}
|
|
numparticles = activeparticles;
|
|
|
|
qfeglDepthMask (GL_TRUE);
|
|
qfeglDisableVertexAttribArray (quake_part.vertex.location);
|
|
qfeglDisableVertexAttribArray (quake_part.color.location);
|
|
qfeglDisableVertexAttribArray (quake_part.st.location);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 0);
|
|
qfeglDisable (GL_TEXTURE_2D);
|
|
}
|
|
|
|
static void
|
|
draw_id_particles (void)
|
|
{
|
|
int activeparticles, maxparticle, j, vacount;
|
|
unsigned k;
|
|
float minparticledist;
|
|
particle_t *part;
|
|
partvert_t *VA;
|
|
mat4f_t vp_mat;
|
|
quat_t fog;
|
|
|
|
mmulf (vp_mat, glsl_projection, glsl_view);
|
|
|
|
// LordHavoc: particles should not affect zbuffer
|
|
qfeglDepthMask (GL_FALSE);
|
|
qfeglUseProgram (quake_point.program);
|
|
qfeglEnableVertexAttribArray (quake_point.vertex.location);
|
|
qfeglEnableVertexAttribArray (quake_point.color.location);
|
|
|
|
qfeglUniformMatrix4fv (quake_point.mvp_matrix.location, 1, false,
|
|
&vp_mat[0][0]);
|
|
|
|
glsl_Fog_GetColor (fog);
|
|
fog[3] = glsl_Fog_GetDensity () / 64.0;
|
|
qfeglUniform4fv (quake_point.fog.location, 1, fog);
|
|
|
|
qfeglUniform1i (quake_point.palette.location, 0);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 0);
|
|
qfeglEnable (GL_TEXTURE_2D);
|
|
qfeglBindTexture (GL_TEXTURE_2D, glsl_palette);
|
|
|
|
minparticledist = DotProduct (r_refdef.viewposition, 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 < vr_data.realtime) {
|
|
freeparticles[j++] = part;
|
|
} else {
|
|
maxparticle = k;
|
|
activeparticles++;
|
|
}
|
|
}
|
|
qfeglVertexAttribPointer (quake_point.vertex.location, 3, GL_FLOAT,
|
|
0, sizeof (partvert_t),
|
|
&particleVertexArray[0].vertex);
|
|
qfeglVertexAttribPointer (quake_point.color.location, 1, GL_UNSIGNED_BYTE,
|
|
1, sizeof (partvert_t),
|
|
&particleVertexArray[0].color);
|
|
qfeglDrawArrays (GL_POINTS, 0, vacount);
|
|
|
|
k = 0;
|
|
while (maxparticle >= activeparticles) {
|
|
*freeparticles[k++] = particles[maxparticle--];
|
|
while (maxparticle >= activeparticles &&
|
|
particles[maxparticle].die <= vr_data.realtime)
|
|
maxparticle--;
|
|
}
|
|
numparticles = activeparticles;
|
|
|
|
qfeglDepthMask (GL_TRUE);
|
|
qfeglDisableVertexAttribArray (quake_point.vertex.location);
|
|
qfeglDisableVertexAttribArray (quake_point.color.location);
|
|
qfeglActiveTexture (GL_TEXTURE0 + 0);
|
|
qfeglDisable (GL_TEXTURE_2D);
|
|
}
|
|
|
|
void
|
|
glsl_R_DrawParticles (void)
|
|
{
|
|
if (!r_particles->int_val || !numparticles)
|
|
return;
|
|
if (r_particles_style->int_val) {
|
|
draw_qf_particles ();
|
|
} else {
|
|
draw_id_particles ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
glsl_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);
|
|
}
|
|
|
|
static void
|
|
glsl_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);
|
|
}
|
|
|
|
static vid_particle_funcs_t particles_QF = {
|
|
R_RocketTrail_QF,
|
|
R_GrenadeTrail_QF,
|
|
R_BloodTrail_QF,
|
|
R_SlightBloodTrail_QF,
|
|
R_WizTrail_QF,
|
|
R_FlameTrail_QF,
|
|
R_VoorTrail_QF,
|
|
R_GlowTrail_QF,
|
|
R_RunParticleEffect_QF,
|
|
R_BloodPuffEffect_QF,
|
|
R_GunshotEffect_QF,
|
|
R_LightningBloodEffect_QF,
|
|
R_SpikeEffect_QF,
|
|
R_KnightSpikeEffect_QF,
|
|
R_SuperSpikeEffect_QF,
|
|
R_WizSpikeEffect_QF,
|
|
R_BlobExplosion_QF,
|
|
R_ParticleExplosion_QF,
|
|
R_ParticleExplosion2_QF,
|
|
R_LavaSplash_QF,
|
|
R_TeleportSplash_QF,
|
|
R_DarkFieldParticles_ID,
|
|
R_EntityParticles_ID,
|
|
R_Particle_New,
|
|
R_Particle_NewRandom,
|
|
};
|
|
|
|
static vid_particle_funcs_t particles_ID = {
|
|
R_RocketTrail_ID,
|
|
R_GrenadeTrail_ID,
|
|
R_BloodTrail_ID,
|
|
R_SlightBloodTrail_ID,
|
|
R_WizTrail_ID,
|
|
R_FlameTrail_ID,
|
|
R_VoorTrail_ID,
|
|
R_GlowTrail_QF,
|
|
R_RunParticleEffect_ID,
|
|
R_BloodPuffEffect_ID,
|
|
R_GunshotEffect_ID,
|
|
R_LightningBloodEffect_ID,
|
|
R_SpikeEffect_ID,
|
|
R_KnightSpikeEffect_ID,
|
|
R_SuperSpikeEffect_ID,
|
|
R_WizSpikeEffect_ID,
|
|
R_BlobExplosion_ID,
|
|
R_ParticleExplosion_ID,
|
|
R_ParticleExplosion2_QF,
|
|
R_LavaSplash_ID,
|
|
R_TeleportSplash_ID,
|
|
R_DarkFieldParticles_ID,
|
|
R_EntityParticles_ID,
|
|
R_Particle_New,
|
|
R_Particle_NewRandom,
|
|
};
|
|
|
|
static vid_particle_funcs_t particles_QF_egg = {
|
|
R_RocketTrail_EE,
|
|
R_GrenadeTrail_EE,
|
|
R_BloodTrail_QF,
|
|
R_SlightBloodTrail_QF,
|
|
R_WizTrail_QF,
|
|
R_FlameTrail_QF,
|
|
R_VoorTrail_QF,
|
|
R_GlowTrail_QF,
|
|
R_RunParticleEffect_QF,
|
|
R_BloodPuffEffect_QF,
|
|
R_GunshotEffect_QF,
|
|
R_LightningBloodEffect_QF,
|
|
R_SpikeEffect_QF,
|
|
R_KnightSpikeEffect_QF,
|
|
R_SuperSpikeEffect_QF,
|
|
R_WizSpikeEffect_QF,
|
|
R_BlobExplosion_QF,
|
|
R_ParticleExplosion_EE,
|
|
R_ParticleExplosion2_QF,
|
|
R_LavaSplash_QF,
|
|
R_TeleportSplash_EE,
|
|
R_DarkFieldParticles_ID,
|
|
R_EntityParticles_ID,
|
|
R_Particle_New,
|
|
R_Particle_NewRandom,
|
|
};
|
|
|
|
static vid_particle_funcs_t particles_ID_egg = {
|
|
R_RocketTrail_EE,
|
|
R_GrenadeTrail_EE,
|
|
R_BloodTrail_ID,
|
|
R_SlightBloodTrail_ID,
|
|
R_WizTrail_ID,
|
|
R_FlameTrail_ID,
|
|
R_VoorTrail_ID,
|
|
R_GlowTrail_QF,
|
|
R_RunParticleEffect_ID,
|
|
R_BloodPuffEffect_ID,
|
|
R_GunshotEffect_ID,
|
|
R_LightningBloodEffect_ID,
|
|
R_SpikeEffect_ID,
|
|
R_KnightSpikeEffect_ID,
|
|
R_SuperSpikeEffect_ID,
|
|
R_WizSpikeEffect_ID,
|
|
R_BlobExplosion_ID,
|
|
R_ParticleExplosion_EE,
|
|
R_ParticleExplosion2_QF,
|
|
R_LavaSplash_ID,
|
|
R_TeleportSplash_EE,
|
|
R_DarkFieldParticles_ID,
|
|
R_EntityParticles_ID,
|
|
R_Particle_New,
|
|
R_Particle_NewRandom,
|
|
};
|
|
|
|
void
|
|
glsl_r_easter_eggs_f (cvar_t *var)
|
|
{
|
|
if (easter_eggs) {
|
|
if (easter_eggs->int_val) {
|
|
if (r_particles_style->int_val) {
|
|
glsl_vid_render_funcs.particles = &particles_QF_egg;
|
|
} else {
|
|
glsl_vid_render_funcs.particles = &particles_ID_egg;
|
|
}
|
|
} else if (r_particles_style) {
|
|
if (r_particles_style->int_val) {
|
|
glsl_vid_render_funcs.particles = &particles_QF;
|
|
} else {
|
|
glsl_vid_render_funcs.particles = &particles_ID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
glsl_r_particles_style_f (cvar_t *var)
|
|
{
|
|
glsl_r_easter_eggs_f (easter_eggs);
|
|
}
|
|
|
|
static void
|
|
R_ParticleFunctionInit (void)
|
|
{
|
|
glsl_r_particles_style_f (r_particles_style);
|
|
glsl_r_easter_eggs_f (easter_eggs);
|
|
}
|
|
|
|
static void
|
|
r_particles_nearclip_f (cvar_t *var)
|
|
{
|
|
Cvar_SetValue (r_particles_nearclip, bound (r_nearclip->value, var->value,
|
|
r_farclip->value));
|
|
}
|
|
|
|
static void
|
|
r_particles_f (cvar_t *var)
|
|
{
|
|
R_MaxParticlesCheck (var, r_particles_max);
|
|
}
|
|
|
|
static void
|
|
r_particles_max_f (cvar_t *var)
|
|
{
|
|
R_MaxParticlesCheck (r_particles, var);
|
|
}
|
|
|
|
void
|
|
glsl_R_Particles_Init_Cvars (void)
|
|
{
|
|
easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f,
|
|
"Enables easter eggs.");
|
|
r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f,
|
|
"Toggles drawing of particles.");
|
|
r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE,
|
|
r_particles_max_f, "Maximum amount of "
|
|
"particles to display. No maximum, minimum "
|
|
"is 0.");
|
|
r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32",
|
|
CVAR_ARCHIVE, r_particles_nearclip_f,
|
|
"Distance of the particle near clipping "
|
|
"plane from the player.");
|
|
r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE,
|
|
r_particles_style_f, "Sets particle style. "
|
|
"0 for Id, 1 for QF.");
|
|
R_ParticleFunctionInit ();
|
|
}
|