- if (!p->typeindex)
- {
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
-
- if (p->delayedspawn)
- {
- if (p->delayedspawn > cl.time)
- continue;
- p->delayedspawn = 0;
- }
-
- content = 0;
-
- p->size += p->sizeincrease * frametime;
- p->alpha -= p->alphafade * frametime;
-
- if (p->alpha <= 0 || p->die <= cl.time)
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
-
- if (particletype[p->typeindex].orientation != PARTICLE_BEAM && frametime > 0)
- {
- if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK))
- {
- if (p->typeindex == pt_blood)
- p->size += frametime * 8;
- else
- p->vel[2] -= p->gravity * gravity;
- f = 1.0f - min(p->liquidfriction * frametime, 1);
- VectorScale(p->vel, f, p->vel);
- }
- else
- {
- p->vel[2] -= p->gravity * gravity;
- if (p->airfriction)
- {
- f = 1.0f - min(p->airfriction * frametime, 1);
- VectorScale(p->vel, f, p->vel);
- }
- }
-
- VectorCopy(p->org, oldorg);
- VectorMA(p->org, frametime, p->vel, p->org);
- if (p->bounce && cl.time >= p->delayedcollisions)
- {
- trace = CL_Move(oldorg, vec3_origin, vec3_origin, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false);
- // if the trace started in or hit something of SUPERCONTENTS_NODROP
- // or if the trace hit something flagged as NOIMPACT
- // then remove the particle
- if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID))
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
- VectorCopy(trace.endpos, p->org);
- // react if the particle hit something
- if (trace.fraction < 1)
- {
- VectorCopy(trace.endpos, p->org);
- if (p->typeindex == pt_rain)
- {
- // raindrop - splash on solid/water/slime/lava
- int count;
- // convert from a raindrop particle to a rainsplash decal
- VectorCopy(trace.plane.normal, p->vel);
- VectorAdd(p->org, p->vel, p->org);
- p->typeindex = pt_raindecal;
- p->texnum = tex_rainsplash;
- p->time2 = cl.time;
- p->alphafade = p->alpha / 0.4;
- p->bounce = 0;
- p->airfriction = 0;
- p->liquidfriction = 0;
- p->gravity = 0;
- p->size *= 1.0f;
- p->sizeincrease = p->size * 20;
- count = (int)lhrandom(1, 10);
- while(count--)
- CL_NewParticle(pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, 0, lhrandom(64, 255), 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, cl.movevars_gravity * 0.04 + p->vel[2]*16, 0, 0, 0, 32);
- continue;
- }
- else if (p->typeindex == pt_blood)
- {
- // blood - splash on solid
- if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
- {
- p->typeindex = 0;
- continue;
- }
- if (cl_stainmaps.integer)
- R_Stain(p->org, 32, 32, 16, 16, (int)(p->alpha * p->size * (1.0f / 40.0f)), 192, 48, 48, (int)(p->alpha * p->size * (1.0f / 40.0f)));
- if (!cl_decals.integer)
- {
- p->typeindex = 0;
- continue;
- }
- // create a decal for the blood splat
- CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha);
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
- else if (p->bounce < 0)
- {
- // bounce -1 means remove on impact
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
- else
- {
- // anything else - bounce off solid
- dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
- VectorMA(p->vel, dist, trace.plane.normal, p->vel);
- if (DotProduct(p->vel, p->vel) < 0.03)
- VectorClear(p->vel);
- }
- }
- }
- }
-
- if (p->typeindex != pt_static)
- {
- switch (p->typeindex)
- {
- case pt_entityparticle:
- // particle that removes itself after one rendered frame
- if (p->time2)
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- }
- else
- p->time2 = 1;
- break;
- case pt_blood:
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- }
- break;
- case pt_bubble:
- a = CL_PointSuperContents(p->org);
- if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- }
- break;
- case pt_rain:
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- }
- break;
- case pt_snow:
- if (cl.time > p->time2)
- {
- // snow flutter
- p->time2 = cl.time + (rand() & 3) * 0.1;
- p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
- p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
- }
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
- {
- p->typeindex = 0;
- if (cl.free_particle > i)
- cl.free_particle = i;
- }
- break;
- default:
- break;
- }
- }
- }
-
- // reduce cl.num_particles if possible
- while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
- cl.num_particles--;
-}
-
-#define MAX_PARTICLETEXTURES 64
-// particletexture_t is a rectangle in the particlefonttexture
-typedef struct particletexture_s
-{
- rtexture_t *texture;
- float s1, t1, s2, t2;
-}
-particletexture_t;
-
-static rtexturepool_t *particletexturepool;
-static rtexture_t *particlefonttexture;
-static particletexture_t particletexture[MAX_PARTICLETEXTURES];
-
-static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
-static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
-
-#define PARTICLETEXTURESIZE 64
-#define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
-
-static unsigned char shadebubble(float dx, float dy, vec3_t light)
-{
- float dz, f, dot;
- vec3_t normal;
- dz = 1 - (dx*dx+dy*dy);
- if (dz > 0) // it does hit the sphere
- {
- f = 0;
- // back side
- normal[0] = dx;normal[1] = dy;normal[2] = dz;
- VectorNormalize(normal);
- dot = DotProduct(normal, light);
- if (dot > 0.5) // interior reflection
- f += ((dot * 2) - 1);
- else if (dot < -0.5) // exterior reflection
- f += ((dot * -2) - 1);
- // front side
- normal[0] = dx;normal[1] = dy;normal[2] = -dz;
- VectorNormalize(normal);
- dot = DotProduct(normal, light);
- if (dot > 0.5) // interior reflection
- f += ((dot * 2) - 1);
- else if (dot < -0.5) // exterior reflection
- f += ((dot * -2) - 1);
- f *= 128;
- f += 16; // just to give it a haze so you can see the outline
- f = bound(0, f, 255);
- return (unsigned char) f;
- }
- else
- return 0;
-}
-
-static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
-{
- int basex, basey, y;
- basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
- basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
- for (y = 0;y < PARTICLETEXTURESIZE;y++)
- memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
-}
-
-void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
-{
- int x, y;
- float cx, cy, dx, dy, f, iradius;
- unsigned char *d;
- cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
- cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
- iradius = 1.0f / radius;
- alpha *= (1.0f / 255.0f);
- for (y = 0;y < PARTICLETEXTURESIZE;y++)
- {
- for (x = 0;x < PARTICLETEXTURESIZE;x++)
- {
- dx = (x - cx);
- dy = (y - cy);
- f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
- if (f > 0)
- {
- if (f > 1)
- f = 1;
- d = data + (y * PARTICLETEXTURESIZE + x) * 4;
- d[0] += (int)(f * (blue - d[0]));
- d[1] += (int)(f * (green - d[1]));
- d[2] += (int)(f * (red - d[2]));
- }
- }
- }
-}
-
-void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
-{
- int i;
- for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
- {
- data[0] = bound(minb, data[0], maxb);
- data[1] = bound(ming, data[1], maxg);
- data[2] = bound(minr, data[2], maxr);
- }
-}
-
-void particletextureinvert(unsigned char *data)
-{
- int i;
- for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
- {
- data[0] = 255 - data[0];
- data[1] = 255 - data[1];
- data[2] = 255 - data[2];
- }
-}
-
-// Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
-static void R_InitBloodTextures (unsigned char *particletexturedata)
-{
- int i, j, k, m;
- unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
-
- // blood particles
- for (i = 0;i < 8;i++)
- {
- memset(&data[0][0][0], 255, sizeof(data));