]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_particles.c
changed most Sys_Error calls to Con_Printf with appropriate error returns
[xonotic/darkplaces.git] / cl_particles.c
index 352dbc393d3220eba54b7535dba67f372f688c88..235a5424f4f58418b8d99d90f7fc9a11e489eed4 100644 (file)
@@ -25,7 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define NUMVERTEXNORMALS       162
 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
 #define m_bytenormals r_avertexnormals
-#define VectorNormalizeFast VectorNormalize
 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
 typedef unsigned char qbyte;
 #define cl_stainmaps.integer 0
@@ -46,16 +45,16 @@ void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
        vec3_t right1, right2, diff, normal;
 
        VectorSubtract (org2, org1, normal);
-       VectorNormalizeFast (normal);
+       VectorNormalize (normal);
 
        // calculate 'right' vector for start
        VectorSubtract (r_vieworigin, org1, diff);
-       VectorNormalizeFast (diff);
+       VectorNormalize (diff);
        CrossProduct (normal, diff, right1);
 
        // calculate 'right' vector for end
        VectorSubtract (r_vieworigin, org2, diff);
-       VectorNormalizeFast (diff);
+       VectorNormalize (diff);
        CrossProduct (normal, diff, right2);
 
        vert[ 0] = org1[0] + width * right1[0];
@@ -79,11 +78,17 @@ void fractalnoise(qbyte *noise, int size, int startgrid)
 
        for (sizepower = 0;(1 << sizepower) < size;sizepower++);
        if (size != (1 << sizepower))
-               Sys_Error("fractalnoise: size must be power of 2\n");
+       {
+               Con_Printf("fractalnoise: size must be power of 2\n");
+               return;
+       }
 
        for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
        if (startgrid != (1 << gridpower))
-               Sys_Error("fractalnoise: grid must be power of 2\n");
+       {
+               Con_Printf("fractalnoise: grid must be power of 2\n");
+               return;
+       }
 
        startgrid = bound(0, startgrid, size);
 
@@ -145,14 +150,14 @@ void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
        right[0] -= d * forward[0];
        right[1] -= d * forward[1];
        right[2] -= d * forward[2];
-       VectorNormalizeFast(right);
+       VectorNormalize(right);
        CrossProduct(right, forward, up);
 }
 #if QW
 #include "pmove.h"
 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
 #endif
-float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
+trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
 {
 #if QW
        pmtrace_t trace;
@@ -167,9 +172,7 @@ float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int
 #else
        RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
 #endif
-       VectorCopy(trace.endpos, impact);
-       VectorCopy(trace.plane.normal, normal);
-       return trace.fraction;
+       return trace;
 }
 #else
 #include "cl_collision.h"
@@ -200,30 +203,31 @@ typedef struct particletype_s
 {
        pblend_t blendmode;
        porientation_t orientation;
+       qboolean lighting;
 }
 particletype_t;
 
 typedef enum
 {
-       pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, pt_entityparticle, pt_total
+       pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
 }
 ptype_t;
 
 // must match ptype_t values
 particletype_t particletype[pt_total] =
 {
-       {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_alphastatic
-       {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_static
-       {PBLEND_ADD, PARTICLE_SPARK}, //pt_spark
-       {PBLEND_ADD, PARTICLE_BEAM}, //pt_beam
-       {PBLEND_ADD, PARTICLE_SPARK}, //pt_rain
-       {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_raindecal
-       {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_snow
-       {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_bubble
-       {PBLEND_MOD, PARTICLE_BILLBOARD}, //pt_blood
-       {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_grow
-       {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_decal
-       {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_entityparticle
+       {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
+       {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
+       {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
+       {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
+       {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
+       {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
+       {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
+       {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
+       {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
+       {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
+       {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
+       {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
 };
 
 typedef struct particle_s
@@ -240,12 +244,10 @@ typedef struct particle_s
        float           gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
        float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
        qbyte           color[4];
-#ifndef WORKINGLQUAKE
-       entity_render_t *owner; // decal stuck to this entity
+       unsigned short owner; // decal stuck to this entity
        model_t         *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
        vec3_t          relativeorigin; // decal at this location in entity's coordinate space
        vec3_t          relativedirection; // decal oriented this way relative to entity's coordinate space
-#endif
 }
 particle_t;
 
@@ -324,10 +326,6 @@ cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
 
-#ifndef WORKINGLQUAKE
-static mempool_t *cl_part_mempool;
-#endif
-
 void CL_Particles_Clear(void)
 {
        cl_numparticles = 0;
@@ -383,8 +381,7 @@ void CL_Particles_Init (void)
 #ifdef WORKINGLQUAKE
        particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
 #else
-       cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
-       particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
+       particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
 #endif
        CL_Particles_Clear();
 }
@@ -393,8 +390,6 @@ void CL_Particles_Shutdown (void)
 {
 #ifdef WORKINGLQUAKE
        // No clue what to do here...
-#else
-       Mem_FreePool (&cl_part_mempool);
 #endif
 }
 
@@ -459,7 +454,7 @@ particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex,
        return part;
 }
 
-void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
+void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
 {
        particle_t *p;
        if (!cl_decals.integer)
@@ -470,9 +465,9 @@ void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_
                p->time2 = cl.time;
 #ifndef WORKINGLQUAKE
                p->owner = hitent;
-               p->ownermodel = p->owner->model;
-               Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
-               Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
+               p->ownermodel = cl_entities[p->owner].render.model;
+               Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
+               Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
                VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
 #endif
        }
@@ -482,24 +477,21 @@ void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size,
 {
        int i;
        float bestfrac, bestorg[3], bestnormal[3];
-       float frac, v[3], normal[3], org2[3];
-#ifdef WORKINGLQUAKE
-       void *besthitent = NULL, *hitent;
-#else
-       entity_render_t *besthitent = NULL, *hitent;
-#endif
+       float org2[3];
+       int besthitent = 0, hitent;
+       trace_t trace;
        bestfrac = 10;
        for (i = 0;i < 32;i++)
        {
                VectorRandom(org2);
                VectorMA(org, maxdist, org2, org2);
-               frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
-               if (bestfrac > frac)
+               trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
+               if (bestfrac > trace.fraction)
                {
-                       bestfrac = frac;
+                       bestfrac = trace.fraction;
                        besthitent = hitent;
-                       VectorCopy(v, bestorg);
-                       VectorCopy(normal, bestnormal);
+                       VectorCopy(trace.endpos, bestorg);
+                       VectorCopy(trace.plane.normal, bestnormal);
                }
        }
        if (bestfrac < 1)
@@ -665,6 +657,7 @@ CL_ParticleExplosion
 void CL_ParticleExplosion (vec3_t org)
 {
        int i;
+       trace_t trace;
        //vec3_t v;
        //vec3_t v2;
        if (cl_stainmaps.integer)
@@ -698,13 +691,14 @@ void CL_ParticleExplosion (vec3_t org)
                                        v[0] = org[0] + lhrandom(-48, 48);
                                        v[1] = org[1] + lhrandom(-48, 48);
                                        v[2] = org[2] + lhrandom(-48, 48);
-                                       if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
+                                       trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
+                                       if (trace.fraction >= 0.1)
                                                break;
                                }
-                               VectorSubtract(v2, org, v2);
+                               VectorSubtract(trace.endpos, org, v2);
 #endif
                                VectorScale(v2, 2.0f, v2);
-                               particle(particletype + pt_static, 0xFFFFFF, 0xFFFFFF, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
+                               particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
                        }
                }
 
@@ -806,8 +800,9 @@ void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
 
 void CL_Smoke (vec3_t org, vec3_t dir, int count)
 {
-       vec3_t org2, org3;
+       vec3_t org2;
        int k;
+       trace_t trace;
 
        if (!cl_particles.integer) return;
 
@@ -820,8 +815,8 @@ void CL_Smoke (vec3_t org, vec3_t dir, int count)
                        org2[0] = org[0] + 0.125f * lhrandom(-count, count);
                        org2[1] = org[1] + 0.125f * lhrandom(-count, count);
                        org2[2] = org[2] + 0.125f * lhrandom(-count, count);
-                       CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
-                       particle(particletype + pt_grow, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
+                       trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
+                       particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
                }
        }
 }
@@ -844,7 +839,8 @@ static float bloodcount = 0;
 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
 {
        float s;
-       vec3_t org2, org3;
+       vec3_t org2;
+       trace_t trace;
        // bloodcount is used to accumulate counts too small to cause a blood particle
        if (!cl_particles.integer) return;
        if (!cl_particles_blood.integer) return;
@@ -859,8 +855,8 @@ void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
                org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
                org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
                org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
-               CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
-               particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
+               trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
+               particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, trace.endpos[0], trace.endpos[1], trace.endpos[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
                bloodcount -= 16 / cl_particles_quality.value;
        }
 }
@@ -915,6 +911,7 @@ void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int color
 {
        int k;
        float t, z, minz, maxz;
+       particle_t *p;
        if (!cl_particles.integer) return;
        if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
        if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
@@ -950,9 +947,11 @@ void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int color
                {
                        k = particlepalette[colorbase + (rand()&3)];
                        if (gamemode == GAME_GOODVSBAD2)
-                               particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
+                               p = particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
                        else
-                               particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
+                               p = particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
+                       if (p)
+                               VectorCopy(p->vel, p->relativedirection);
                }
                break;
        default:
@@ -983,7 +982,7 @@ void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
                o[1] = lhrandom(mins[1], maxs[1]);
                o[2] = lhrandom(mins[2], maxs[2]);
                VectorSubtract(o, center, v);
-               VectorNormalizeFast(v);
+               VectorNormalize(v);
                VectorScale(v, 100, v);
                v[2] += sv_gravity.value * 0.15f;
                particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
@@ -1147,7 +1146,7 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *en
                                dec = qd*3;
                                if (smoke)
                                {
-                                       particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
+                                       particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
                                        particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
                                }
                                if (bubbles)
@@ -1158,7 +1157,7 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *en
                                // FIXME: make it gradually stop smoking
                                dec = qd*3;
                                if (smoke)
-                                       particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
+                                       particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
                                break;
 
 
@@ -1244,7 +1243,7 @@ void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
        // smoke puff
        if (cl_particles_smoke.integer)
                for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
-                       particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
+                       particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
 }
 
 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
@@ -1259,7 +1258,7 @@ void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
        // smoke puff
        if (cl_particles_smoke.integer)
                for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
-                       particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
+                       particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
 
        // sparks
        if (cl_particles_sparks.integer)
@@ -1276,12 +1275,9 @@ void CL_MoveParticles (void)
 {
        particle_t *p;
        int i, maxparticle, j, a, content;
-       float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
-#ifdef WORKINGLQUAKE
-       void *hitent;
-#else
-       entity_render_t *hitent;
-#endif
+       float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
+       int hitent;
+       trace_t trace;
 
        // LordHavoc: early out condition
        if (!cl_numparticles)
@@ -1326,17 +1322,17 @@ void CL_MoveParticles (void)
                                if (p->type == particletype + pt_rain)
                                {
                                        // raindrop - splash on solid/water/slime/lava
-                                       if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
+                                       trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
+                                       if (trace.fraction < 1)
                                        {
-                                               VectorCopy(v, p->org);
-                                               // splash
-                                               p->type = particletype + pt_raindecal;
                                                // convert from a raindrop particle to a rainsplash decal
+                                               VectorCopy(trace.endpos, p->org);
+                                               VectorCopy(trace.plane.normal, p->vel);
+                                               VectorAdd(p->org, p->vel, p->org);
+                                               p->type = particletype + pt_raindecal;
                                                p->texnum = tex_rainsplash[0];
                                                p->time2 = cl.time;
                                                p->alphafade = p->alpha / 0.4;
-                                               VectorCopy(normal, p->vel);
-                                               VectorAdd(p->org, normal, p->org);
                                                p->bounce = 0;
                                                p->friction = 0;
                                                p->gravity = 0;
@@ -1346,12 +1342,16 @@ void CL_MoveParticles (void)
                                else if (p->type == particletype + pt_blood)
                                {
                                        // blood - splash on solid
-                                       if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
+                                       trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
+                                       if (trace.fraction < 1)
                                        {
-                                               VectorCopy(v, p->org);
+                                               // convert from a blood particle to a blood decal
+                                               VectorCopy(trace.endpos, p->org);
+                                               VectorCopy(trace.plane.normal, p->vel);
+                                               VectorAdd(p->org, p->vel, p->org);
 #ifndef WORKINGLQUAKE
                                                if (cl_stainmaps.integer)
-                                                       R_Stain(v, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
+                                                       R_Stain(p->org, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
 #endif
                                                if (!cl_decals.integer)
                                                {
@@ -1360,19 +1360,15 @@ void CL_MoveParticles (void)
                                                }
 
                                                p->type = particletype + pt_decal;
-                                               // convert from a blood particle to a blood decal
                                                p->texnum = tex_blooddecal[rand()&7];
 #ifndef WORKINGLQUAKE
                                                p->owner = hitent;
-                                               p->ownermodel = hitent->model;
-                                               Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
-                                               Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
-                                               VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
+                                               p->ownermodel = cl_entities[hitent].render.model;
+                                               Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
+                                               Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
 #endif
                                                p->time2 = cl.time;
                                                p->alphafade = 0;
-                                               VectorCopy(normal, p->vel);
-                                               VectorAdd(p->org, normal, p->org);
                                                p->bounce = 0;
                                                p->friction = 0;
                                                p->gravity = 0;
@@ -1381,9 +1377,10 @@ void CL_MoveParticles (void)
                                }
                                else
                                {
-                                       if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
+                                       trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
+                                       if (trace.fraction < 1)
                                        {
-                                               VectorCopy(v, p->org);
+                                               VectorCopy(trace.endpos, p->org);
                                                if (p->bounce < 0)
                                                {
                                                        p->type = NULL;
@@ -1391,8 +1388,8 @@ void CL_MoveParticles (void)
                                                }
                                                else
                                                {
-                                                       dist = DotProduct(p->vel, normal) * -p->bounce;
-                                                       VectorMA(p->vel, dist, normal, p->vel);
+                                                       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);
                                                }
@@ -1475,9 +1472,9 @@ void CL_MoveParticles (void)
                                {
                                        // snow flutter
                                        p->time2 = cl.time + (rand() & 3) * 0.1;
-                                       p->vel[0] += lhrandom(-32, 32);
-                                       p->vel[1] += lhrandom(-32, 32);
-                                       p->vel[2] += lhrandom(-32, 32);
+                                       p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
+                                       p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
+                                       //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
                                }
 #ifdef WORKINGLQUAKE
                                a = CL_PointQ1Contents(p->org);
@@ -1488,17 +1485,17 @@ void CL_MoveParticles (void)
 #endif
                                        p->type = NULL;
                                break;
-                       case pt_grow:
-                               p->size += frametime * 15;
+                       case pt_smoke:
+                               //p->size += frametime * 15;
                                break;
                        case pt_decal:
                                // FIXME: this has fairly wacky handling of alpha
-                               p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (p->alpha / cl_decals_fadetime.value) : 0;
+                               p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
 #ifndef WORKINGLQUAKE
-                               if (p->owner->model == p->ownermodel)
+                               if (cl_entities[p->owner].render.model == p->ownermodel)
                                {
-                                       Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
-                                       Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel);
+                                       Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
+                                       Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
                                }
                                else
                                        p->type = NULL;
@@ -1637,11 +1634,43 @@ void particletextureinvert(qbyte *data)
        }
 }
 
+// Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
+static void R_InitBloodTextures (qbyte *particletexturedata)
+{
+       int i, j, k, m;
+       qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
+
+       // blood particles
+       for (i = 0;i < 8;i++)
+       {
+               memset(&data[0][0][0], 255, sizeof(data));
+               for (k = 0;k < 24;k++)
+                       particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
+               //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
+               particletextureinvert(&data[0][0][0]);
+               setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
+       }
+
+       // blood decals
+       for (i = 0;i < 8;i++)
+       {
+               memset(&data[0][0][0], 255, sizeof(data));
+               m = 8;
+               for (j = 1;j < 10;j++)
+                       for (k = min(j, m - 1);k < m;k++)
+                               particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
+               //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
+               particletextureinvert(&data[0][0][0]);
+               setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
+       }
+
+}
+
 static void R_InitParticleTexture (void)
 {
-       int x, y, d, i, j, k, m;
+       int x, y, d, i, k, m;
        float dx, dy, radius, f, f2;
-       qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
+       qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
        vec3_t light;
        qbyte *particletexturedata;
 
@@ -1663,6 +1692,8 @@ static void R_InitParticleTexture (void)
                memset(&data[0][0][0], 255, sizeof(data));
                do
                {
+                       qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
+
                        fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
                        fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
                        m = 0;
@@ -1759,29 +1790,8 @@ static void R_InitParticleTexture (void)
        }
        setuptex(tex_bubble, &data[0][0][0], particletexturedata);
 
-       // blood particles
-       for (i = 0;i < 8;i++)
-       {
-               memset(&data[0][0][0], 255, sizeof(data));
-               for (k = 0;k < 24;k++)
-                       particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
-               //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
-               particletextureinvert(&data[0][0][0]);
-               setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
-       }
-
-       // blood decals
-       for (i = 0;i < 8;i++)
-       {
-               memset(&data[0][0][0], 255, sizeof(data));
-               m = 8;
-               for (j = 1;j < 10;j++)
-                       for (k = min(j, m - 1);k < m;k++)
-                               particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
-               //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
-               particletextureinvert(&data[0][0][0]);
-               setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
-       }
+       // Blood particles and blood decals
+       R_InitBloodTextures (particletexturedata);
 
        // bullet decals
        for (i = 0;i < 8;i++)
@@ -1911,6 +1921,14 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
                ca = 1;
        }
 #ifndef WORKINGLQUAKE
+       if (p->type->lighting)
+       {
+               float ambient[3], diffuse[3], diffusenormal[3];
+               R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
+               cr *= (ambient[0] + 0.5 * diffuse[0]);
+               cg *= (ambient[1] + 0.5 * diffuse[1]);
+               cb *= (ambient[2] + 0.5 * diffuse[2]);
+       }
        if (fogenabled)
        {
                VectorSubtract(org, r_vieworigin, fogvec);
@@ -1919,7 +1937,7 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
                cr = cr * ifog;
                cg = cg * ifog;
                cb = cb * ifog;
-               if (blendmode == PBLEND_ADD)
+               if (blendmode == PBLEND_ALPHA)
                {
                        cr += fogcolor[0] * fog;
                        cg += fogcolor[1] * fog;
@@ -1998,7 +2016,7 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
        {
                R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
                VectorSubtract(p->vel, p->org, up);
-               VectorNormalizeFast(up);
+               VectorNormalize(up);
                v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
                v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
                particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
@@ -2024,7 +2042,7 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
        glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
        glEnd();
 #else
-       R_Mesh_Draw(4, 2, polygonelements);
+       R_Mesh_Draw(0, 4, 2, polygonelements);
 #endif
 }