]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_particles.c
Elric added BuffBigLong, BuffBigShort, BuffLittleLong, and BuffLittleShort functions...
[xonotic/darkplaces.git] / cl_particles.c
index 66880582ed488fdfffeb7ad850c5469f8bc0a93e..2dbe39dc99ff463ffc1b68e4e748921b61d8633b 100644 (file)
@@ -41,7 +41,7 @@ void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, i
 #define CL_RunParticleEffect R_RunParticleEffect
 #define CL_LavaSplash R_LavaSplash
 #define CL_RocketTrail2 R_RocketTrail2
-void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width)
+void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
 {
        vec3_t right1, right2, diff, normal;
 
@@ -61,15 +61,15 @@ void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width)
        vert[ 0] = org1[0] + width * right1[0];
        vert[ 1] = org1[1] + width * right1[1];
        vert[ 2] = org1[2] + width * right1[2];
-       vert[ 4] = org1[0] - width * right1[0];
-       vert[ 5] = org1[1] - width * right1[1];
-       vert[ 6] = org1[2] - width * right1[2];
-       vert[ 8] = org2[0] - width * right2[0];
-       vert[ 9] = org2[1] - width * right2[1];
-       vert[10] = org2[2] - width * right2[2];
-       vert[12] = org2[0] + width * right2[0];
-       vert[13] = org2[1] + width * right2[1];
-       vert[14] = org2[2] + width * right2[2];
+       vert[ 3] = org1[0] - width * right1[0];
+       vert[ 4] = org1[1] - width * right1[1];
+       vert[ 5] = org1[2] - width * right1[2];
+       vert[ 6] = org2[0] - width * right2[0];
+       vert[ 7] = org2[1] - width * right2[1];
+       vert[ 8] = org2[2] - width * right2[2];
+       vert[ 9] = org2[0] + width * right2[0];
+       vert[10] = org2[1] + width * right2[1];
+       vert[11] = org2[2] + width * right2[2];
 }
 void fractalnoise(qbyte *noise, int size, int startgrid)
 {
@@ -148,6 +148,29 @@ void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
        VectorNormalizeFast(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 contents, int hitbmodels, void **hitent)
+{
+#if QW
+       pmtrace_t trace;
+#else
+       trace_t trace;
+#endif
+       memset (&trace, 0, sizeof(trace));
+       trace.fraction = 1;
+       VectorCopy (end, trace.endpos);
+#if QW
+       PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
+#else
+       RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
+#endif
+       VectorCopy(trace.endpos, impact);
+       VectorCopy(trace.plane.normal, normal);
+       return trace.fraction;
+}
 #else
 #include "cl_collision.h"
 #endif
@@ -192,6 +215,12 @@ typedef struct particle_s
        float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
        float           pressure; // if non-zero, apply pressure to other particles
        qbyte           color[4];
+#ifndef WORKINGLQUAKE
+       entity_render_t *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;
 
@@ -255,6 +284,9 @@ cvar_t cl_particles_blood_size = {CVAR_SAVE, "cl_particles_blood_size", "8"};
 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
+cvar_t cl_particles_smoke_size = {CVAR_SAVE, "cl_particles_smoke_size", "7"};
+cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
+cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
@@ -301,6 +333,9 @@ void CL_Particles_Init (void)
        Cvar_RegisterVariable (&cl_particles_blood_alpha);
        Cvar_RegisterVariable (&cl_particles_bulletimpacts);
        Cvar_RegisterVariable (&cl_particles_smoke);
+       Cvar_RegisterVariable (&cl_particles_smoke_size);
+       Cvar_RegisterVariable (&cl_particles_smoke_alpha);
+       Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
        Cvar_RegisterVariable (&cl_particles_sparks);
        Cvar_RegisterVariable (&cl_particles_bubbles);
        Cvar_RegisterVariable (&cl_decals);
@@ -340,6 +375,7 @@ void CL_Particles_Init (void)
                        pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;\
                }\
                part = &particles[cl_numparticles++];\
+               memset(part, 0, sizeof(*part));\
                part->type = (ptype);\
                part->color[0] = pcr2;\
                part->color[1] = pcg2;\
@@ -407,9 +443,9 @@ void CL_EntityParticles (entity_t *ent)
                forward[2] = -sp;
 
 #ifdef WORKINGLQUAKE
-               particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ALPHA, 2, 2, 255, 0, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+               particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ADD, 2, 2, 255, 0, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 #else
-               particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ALPHA, 2, 2, 255, 0, 0, 0, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+               particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ADD, 2, 2, 255, 0, 0, 0, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 #endif
        }
 }
@@ -422,7 +458,7 @@ void CL_ReadPointFile_f (void)
        char    *pointfile = NULL, *pointfilepos, *t, tchar;
 #if WORKINGLQUAKE
        char    name[MAX_OSPATH];
-       
+
        sprintf (name,"maps/%s.pts", cl.worldmodel->name);
        COM_FOpenFile (name, &f);
        if (f)
@@ -437,7 +473,7 @@ void CL_ReadPointFile_f (void)
                fclose(f);
        }
 #else
-       pointfile = COM_LoadFile(va("maps/%s.pts", cl.worldmodel->name), true);
+       pointfile = FS_LoadFile(va("maps/%s.pts", cl.worldmodel->name), true);
 #endif
        if (!pointfile)
        {
@@ -536,7 +572,7 @@ void CL_ParticleExplosion (vec3_t org)
                /*
                // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
                // smoke puff
-               if (cl_particles_smoke.integer)
+               if (cl_particles.integer && cl_particles_smoke.integer)
                {
                        for (i = 0;i < 64;i++)
                        {
@@ -561,7 +597,7 @@ void CL_ParticleExplosion (vec3_t org)
                }
                */
 
-               if (cl_particles_sparks.integer)
+               if (cl_particles.integer && cl_particles_sparks.integer)
                {
                        // sparks
                        for (i = 0;i < 256;i++)
@@ -640,12 +676,14 @@ CL_SparkShower
 */
 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
 {
+       vec3_t org2, org3;
        int k;
-       if (!cl_particles.integer) return;
 
        if (cl_stainmaps.integer)
                R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
 
+       if (!cl_particles.integer) return;
+
        if (cl_particles_bulletimpacts.integer)
        {
                // smoke puff
@@ -654,7 +692,11 @@ void CL_SparkShower (vec3_t org, vec3_t dir, int count)
                        k = count / 4;
                        while(k--)
                        {
-                               particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, 255, 1024, 9999, -0.2, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0, 0);
+                               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, 0, true, NULL);
+                               particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, 255, 1024, 9999, -0.2, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0, 0);
                        }
                }
 
@@ -680,6 +722,7 @@ static float bloodcount = 0;
 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
 {
        float s, r, a;
+       vec3_t org2, org3;
        // bloodcount is used to accumulate counts too small to cause a blood particle
        if (!cl_particles.integer) return;
        if (!cl_particles_blood.integer) return;
@@ -693,8 +736,12 @@ void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
        a = cl_particles_blood_alpha.value * 255;
        while(bloodcount > 0)
        {
-               particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_blooddecal[rand()&7], true, PBLEND_MOD, r, r, a * 3, a * 1.5, 9999, 0, -1, org[0], org[1], org[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
-               //particle(pt_blood, PARTICLE_BILLBOARD, 0x000000, 0x200000, tex_smoke[rand()&7], true, PBLEND_ALPHA, r, r, a, a * 0.5, 9999, 0, -1, org[0], org[1], org[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
+               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, 0, true, NULL);
+               particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_blooddecal[rand()&7], true, PBLEND_MOD, r, r, a * 3, a * 1.5, 9999, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
+               //particle(pt_blood, PARTICLE_BILLBOARD, 0x000000, 0x200000, tex_smoke[rand()&7], true, PBLEND_ALPHA, r, r, a, a * 0.5, 9999, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
                bloodcount -= r;
        }
 }
@@ -920,6 +967,9 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
        float len, dec, speed, r;
        int contents, smoke, blood, bubbles;
 
+       if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
+               return;
+
        VectorSubtract(end, start, dir);
        VectorNormalize(dir);
 
@@ -964,8 +1014,8 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
                                dec = 3;
                                if (smoke)
                                {
-                                       particle(pt_grow,   PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, 32, 64, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 6, 0, 0, 0, 0, 0);
-                                       particle(pt_static, PARTICLE_BILLBOARD, 0x801010, 0xFFA020, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, 128, 768, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_grow,   PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, cl_particles_smoke_alpha.value*125, cl_particles_smoke_alphafade.value*125, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), cl_particles_smoke_size.value, 0, 0, 0, 0, 0);
+                                       particle(pt_static, PARTICLE_BILLBOARD, 0x801010, 0xFFA020, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, cl_particles_smoke_alpha.value*288, cl_particles_smoke_alphafade.value*1400, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0, 0, 0, 0, 0, 0);
                                }
                                if (bubbles)
                                {
@@ -977,9 +1027,9 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
                        case 1: // grenade trail
                                // FIXME: make it gradually stop smoking
                                dec = 3;
-                               if (cl_particles.integer && cl_particles_smoke.integer)
+                               if (smoke)
                                {
-                                       particle(pt_static, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, 32, 96, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_grow, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, dec, dec, cl_particles_smoke_alpha.value*100, cl_particles_smoke_alphafade.value*100, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), cl_particles_smoke_size.value, 0, 0, 0, 0, 0);
                                }
                                break;
 
@@ -1130,6 +1180,11 @@ void CL_MoveParticles (void)
        particle_t *p;
        int i, activeparticles, maxparticle, j, a, pressureused = false, content;
        float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
+#ifdef WORKINGLQUAKE
+       void *hitent;
+#else
+       entity_render_t *hitent;
+#endif
 
        // LordHavoc: early out condition
        if (!cl_numparticles)
@@ -1153,21 +1208,29 @@ void CL_MoveParticles (void)
                VectorCopy(p->org, p->oldorg);
                VectorMA(p->org, frametime, p->vel, p->org);
                VectorCopy(p->org, org);
-#ifndef WORKINGLQUAKE
                if (p->bounce)
                {
-                       if (CL_TraceLine(p->oldorg, p->org, v, normal, 0, true, NULL) < 1)
+                       if (CL_TraceLine(p->oldorg, p->org, v, normal, 0, true, &hitent) < 1)
                        {
                                VectorCopy(v, p->org);
                                if (p->bounce < 0)
                                {
                                        // assume it's blood (lame, but...)
+#ifndef WORKINGLQUAKE
                                        if (cl_stainmaps.integer)
                                                R_Stain(v, 32, 32, 16, 16, p->alpha * p->scalex * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->scalex * (1.0f / 40.0f));
+#endif
                                        if (cl_decals.integer)
                                        {
                                                p->type = pt_decal;
                                                p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
+#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);
+#endif
                                                p->time2 = cl.time + cl_decals_time.value;
                                                p->die = p->time2 + cl_decals_fadetime.value;
                                                p->alphafade = 0;
@@ -1196,7 +1259,6 @@ void CL_MoveParticles (void)
                                }
                        }
                }
-#endif
                p->vel[2] -= p->gravity * gravity;
                p->alpha -= p->alphafade * frametime;
                if (p->friction)
@@ -1261,6 +1323,15 @@ void CL_MoveParticles (void)
                                p->scaley += frametime * p->time2;
                                break;
                        case pt_decal:
+#ifndef WORKINGLQUAKE
+                               if (p->owner->model == p->ownermodel)
+                               {
+                                       Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
+                                       Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
+                               }
+                               else
+                                       p->die = -1;
+#endif
                                if (cl.time > p->time2)
                                {
                                        p->alphafade = p->alpha / (p->die - cl.time);
@@ -1268,7 +1339,7 @@ void CL_MoveParticles (void)
                                }
                                break;
                        default:
-                               printf("unknown particle type %i\n", p->type);
+                               Con_Printf("unknown particle type %i\n", p->type);
                                p->die = -1;
                                break;
                        }
@@ -1599,73 +1670,23 @@ void R_InitParticles(void)
        R_Particles_Init();
 }
 
-float varray_vertex[16];
+float varray_vertex3f[12], varray_texcoord2f[1][8];
 #endif
 
+#ifdef WORKINGLQUAKE
+void R_DrawParticle(particle_t *p)
+{
+#else
 void R_DrawParticleCallback(const void *calldata1, int calldata2)
 {
-       float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
-       particletexture_t *tex;
-#ifndef WORKINGLQUAKE
+       const particle_t *p = calldata1;
        rmeshstate_t m;
 #endif
-       const particle_t *p = calldata1;
+       float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
+       particletexture_t *tex;
 
        VectorCopy(p->org, org);
 
-       if (p->orientation == PARTICLE_BILLBOARD)
-       {
-               VectorScale(vright, p->scalex, right);
-               VectorScale(vup, p->scaley, up);
-               varray_vertex[ 0] = org[0] + right[0] - up[0];
-               varray_vertex[ 1] = org[1] + right[1] - up[1];
-               varray_vertex[ 2] = org[2] + right[2] - up[2];
-               varray_vertex[ 4] = org[0] - right[0] - up[0];
-               varray_vertex[ 5] = org[1] - right[1] - up[1];
-               varray_vertex[ 6] = org[2] - right[2] - up[2];
-               varray_vertex[ 8] = org[0] - right[0] + up[0];
-               varray_vertex[ 9] = org[1] - right[1] + up[1];
-               varray_vertex[10] = org[2] - right[2] + up[2];
-               varray_vertex[12] = org[0] + right[0] + up[0];
-               varray_vertex[13] = org[1] + right[1] + up[1];
-               varray_vertex[14] = org[2] + right[2] + up[2];
-       }
-       else if (p->orientation == PARTICLE_SPARK)
-       {
-               VectorMA(p->org, -p->scaley, p->vel, v);
-               VectorMA(p->org, p->scaley, p->vel, up2);
-               R_CalcBeamVerts(varray_vertex, v, up2, p->scalex);
-       }
-       else if (p->orientation == PARTICLE_BEAM)
-               R_CalcBeamVerts(varray_vertex, p->org, p->vel2, p->scalex);
-       else if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
-       {
-               // double-sided
-               if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, org))
-               {
-                       VectorNegate(p->vel2, v);
-                       VectorVectors(v, right, up);
-               }
-               else
-                       VectorVectors(p->vel2, right, up);
-               VectorScale(right, p->scalex, right);
-               VectorScale(up, p->scaley, up);
-               varray_vertex[ 0] = org[0] + right[0] - up[0];
-               varray_vertex[ 1] = org[1] + right[1] - up[1];
-               varray_vertex[ 2] = org[2] + right[2] - up[2];
-               varray_vertex[ 4] = org[0] - right[0] - up[0];
-               varray_vertex[ 5] = org[1] - right[1] - up[1];
-               varray_vertex[ 6] = org[2] - right[2] - up[2];
-               varray_vertex[ 8] = org[0] - right[0] + up[0];
-               varray_vertex[ 9] = org[1] - right[1] + up[1];
-               varray_vertex[10] = org[2] - right[2] + up[2];
-               varray_vertex[12] = org[0] + right[0] + up[0];
-               varray_vertex[13] = org[1] + right[1] + up[1];
-               varray_vertex[14] = org[2] + right[2] + up[2];
-       }
-       else
-               Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
-
        tex = &particletexture[p->texnum];
        cr = p->color[0] * (1.0f / 255.0f);
        cg = p->color[1] * (1.0f / 255.0f);
@@ -1682,21 +1703,7 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
                ca = 1;
        }
 
-#if WORKINGLQUAKE
-       if (p->blendmode == 0)
-               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-       else if (p->blendmode == 1)
-               glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-       else
-               glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
-       glBegin(GL_QUADS);
-       glColor4f(cr, cg, cb, ca);
-       glTexCoord2f(tex->s2, tex->t1);glVertex3f(varray_vertex[ 0], varray_vertex[ 1], varray_vertex[ 2]);
-       glTexCoord2f(tex->s1, tex->t1);glVertex3f(varray_vertex[ 4], varray_vertex[ 5], varray_vertex[ 6]);
-       glTexCoord2f(tex->s1, tex->t2);glVertex3f(varray_vertex[ 8], varray_vertex[ 9], varray_vertex[10]);
-       glTexCoord2f(tex->s2, tex->t2);glVertex3f(varray_vertex[12], varray_vertex[13], varray_vertex[14]);
-       glEnd();
-#else
+#ifndef WORKINGLQUAKE
        memset(&m, 0, sizeof(m));
        if (p->blendmode == 0)
        {
@@ -1736,26 +1743,87 @@ void R_DrawParticleCallback(const void *calldata1, int calldata2)
        cg *= r_colorscale;
        cb *= r_colorscale;
 
-       if (p->orientation == PARTICLE_BEAM)
+       GL_Color(cr, cg, cb, ca);
+
+       R_Mesh_GetSpace(4);
+#endif
+       if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
+       {
+               if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
+               {
+                       // double-sided
+                       if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, org))
+                       {
+                               VectorNegate(p->vel2, v);
+                               VectorVectors(v, right, up);
+                       }
+                       else
+                               VectorVectors(p->vel2, right, up);
+                       VectorScale(right, p->scalex, right);
+                       VectorScale(up, p->scaley, up);
+               }
+               else
+               {
+                       VectorScale(vright, p->scalex, right);
+                       VectorScale(vup, p->scaley, up);
+               }
+               varray_vertex3f[ 0] = org[0] - right[0] - up[0];
+               varray_vertex3f[ 1] = org[1] - right[1] - up[1];
+               varray_vertex3f[ 2] = org[2] - right[2] - up[2];
+               varray_vertex3f[ 3] = org[0] - right[0] + up[0];
+               varray_vertex3f[ 4] = org[1] - right[1] + up[1];
+               varray_vertex3f[ 5] = org[2] - right[2] + up[2];
+               varray_vertex3f[ 6] = org[0] + right[0] + up[0];
+               varray_vertex3f[ 7] = org[1] + right[1] + up[1];
+               varray_vertex3f[ 8] = org[2] + right[2] + up[2];
+               varray_vertex3f[ 9] = org[0] + right[0] - up[0];
+               varray_vertex3f[10] = org[1] + right[1] - up[1];
+               varray_vertex3f[11] = org[2] + right[2] - up[2];
+               varray_texcoord2f[0][0] = tex->s1;varray_texcoord2f[0][1] = tex->t2;
+               varray_texcoord2f[0][2] = tex->s1;varray_texcoord2f[0][3] = tex->t1;
+               varray_texcoord2f[0][4] = tex->s2;varray_texcoord2f[0][5] = tex->t1;
+               varray_texcoord2f[0][6] = tex->s2;varray_texcoord2f[0][7] = tex->t2;
+       }
+       else if (p->orientation == PARTICLE_SPARK)
        {
+               VectorMA(p->org, -p->scaley, p->vel, v);
+               VectorMA(p->org, p->scaley, p->vel, up2);
+               R_CalcBeam_Vertex3f(varray_vertex3f, v, up2, p->scalex);
+               varray_texcoord2f[0][0] = tex->s1;varray_texcoord2f[0][1] = tex->t2;
+               varray_texcoord2f[0][2] = tex->s1;varray_texcoord2f[0][3] = tex->t1;
+               varray_texcoord2f[0][4] = tex->s2;varray_texcoord2f[0][5] = tex->t1;
+               varray_texcoord2f[0][6] = tex->s2;varray_texcoord2f[0][7] = tex->t2;
+       }
+       else if (p->orientation == PARTICLE_BEAM)
+       {
+               R_CalcBeam_Vertex3f(varray_vertex3f, p->org, p->vel2, p->scalex);
                VectorSubtract(p->vel2, p->org, up);
                VectorNormalizeFast(up);
                v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
                v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
-               varray_texcoord[0][0] = 1;varray_texcoord[0][1] = v[0];
-               varray_texcoord[0][4] = 0;varray_texcoord[0][5] = v[0];
-               varray_texcoord[0][8] = 0;varray_texcoord[0][9] = v[1];
-               varray_texcoord[0][12] = 1;varray_texcoord[0][13] = v[1];
+               varray_texcoord2f[0][0] = 1;varray_texcoord2f[0][1] = v[0];
+               varray_texcoord2f[0][2] = 0;varray_texcoord2f[0][3] = v[0];
+               varray_texcoord2f[0][4] = 0;varray_texcoord2f[0][5] = v[1];
+               varray_texcoord2f[0][6] = 1;varray_texcoord2f[0][7] = v[1];
        }
        else
-       {
-               varray_texcoord[0][0] = tex->s2;varray_texcoord[0][1] = tex->t1;
-               varray_texcoord[0][4] = tex->s1;varray_texcoord[0][5] = tex->t1;
-               varray_texcoord[0][8] = tex->s1;varray_texcoord[0][9] = tex->t2;
-               varray_texcoord[0][12] = tex->s2;varray_texcoord[0][13] = tex->t2;
-       }
+               Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
 
-       GL_Color(cr, cg, cb, ca);
+#if WORKINGLQUAKE
+       if (p->blendmode == 0)
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       else if (p->blendmode == 1)
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+       else
+               glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
+       glColor4f(cr, cg, cb, ca);
+       glBegin(GL_QUADS);
+       glTexCoord2f(varray_texcoord2f[0][0], varray_texcoord2f[0][1]);glVertex3f(varray_vertex3f[ 0], varray_vertex3f[ 1], varray_vertex3f[ 2]);
+       glTexCoord2f(varray_texcoord2f[0][2], varray_texcoord2f[0][3]);glVertex3f(varray_vertex3f[ 3], varray_vertex3f[ 4], varray_vertex3f[ 5]);
+       glTexCoord2f(varray_texcoord2f[0][4], varray_texcoord2f[0][5]);glVertex3f(varray_vertex3f[ 6], varray_vertex3f[ 7], varray_vertex3f[ 8]);
+       glTexCoord2f(varray_texcoord2f[0][6], varray_texcoord2f[0][7]);glVertex3f(varray_vertex3f[ 9], varray_vertex3f[10], varray_vertex3f[11]);
+       glEnd();
+#else
        R_Mesh_Draw(4, 2, polygonelements);
 #endif
 }
@@ -1784,7 +1852,7 @@ void R_DrawParticles (void)
        // LordHavoc: only render if not too close
        for (i = 0, p = particles;i < cl_numparticles;i++, p++)
                if (DotProduct(p->org, vpn) >= minparticledist)
-                       R_DrawParticleCallback(p, 0);
+                       R_DrawParticle(p);
        glDepthMask(1);
        glDisable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);