cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
cvar_t cl_decals_newsystem = {CVAR_SAVE, "cl_decals_newsystem", "0", "enables new advanced decal system"};
cvar_t cl_decals_newsystem_intensitymultiplier = {CVAR_SAVE, "cl_decals_newsystem_intensitymultiplier", "2", "boosts intensity of decals (because the distance fade can make them hard to see otherwise)"};
+cvar_t cl_decals_newsystem_immediatebloodstain = {CVAR_SAVE, "cl_decals_newsystem_immediatebloodstain", "2", "0: no on-spawn blood stains; 1: on-spawn blood stains for pt_blood; 2: always use on-spawn blood stains"};
cvar_t cl_decals_models = {CVAR_SAVE, "cl_decals_models", "0", "enables decals on animated models (if newsystem is also 1)"};
cvar_t cl_decals_bias = {CVAR_SAVE, "cl_decals_bias", "0.125", "distance to bias decals from surface to prevent depth fighting"};
cvar_t cl_decals_max = {CVAR_SAVE, "cl_decals_max", "4096", "maximum number of decals allowed to exist in the world at once"};
Cvar_RegisterVariable (&cl_decals_fadetime);
Cvar_RegisterVariable (&cl_decals_newsystem);
Cvar_RegisterVariable (&cl_decals_newsystem_intensitymultiplier);
+ Cvar_RegisterVariable (&cl_decals_newsystem_immediatebloodstain);
Cvar_RegisterVariable (&cl_decals_models);
Cvar_RegisterVariable (&cl_decals_bias);
Cvar_RegisterVariable (&cl_decals_max);
{
// bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
if (count == 1024)
- CL_ParticleExplosion(center);
+ CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
else
else
{
static double bloodaccumulator = 0;
- qboolean immediatebloodstain = true;
+ qboolean immediatebloodstain = (cl_decals_newsystem_immediatebloodstain.integer >= 1);
//CL_NewParticle(center, pt_alphastatic, 0x4f0000,0x7f0000, tex_particle, 2.5, 0, 256, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 1, 4, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, NULL);
bloodaccumulator += count * 0.333 * cl_particles_quality.value;
for (;bloodaccumulator > 0;bloodaccumulator--)
{
info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
trailstep = 0;
- immediatebloodstain = info->particletype == pt_blood || staintex;
+ immediatebloodstain =
+ ((cl_decals_newsystem_immediatebloodstain.integer >= 1) && (info->particletype == pt_blood))
+ ||
+ ((cl_decals_newsystem_immediatebloodstain.integer >= 2) && staintex);
}
info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
for (;info->particleaccumulator >= 1;info->particleaccumulator--)
static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
+static cvar_t r_drawparticles_nearclip_min = {CVAR_SAVE, "r_drawparticles_nearclip_min", "4", "particles closer than drawnearclip_min will not be drawn"};
+static cvar_t r_drawparticles_nearclip_max = {CVAR_SAVE, "r_drawparticles_nearclip_max", "4", "particles closer than drawnearclip_min will be faded"};
static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
#ifdef DUMPPARTICLEFONT
Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
#endif
- particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
+ particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR, -1, NULL);
}
particletexture[tex_beam].s1 = 0;
particletexture[tex_beam].t1 = 0;
if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
{
+ strlcpy(texturename, com_token, sizeof(texturename));
s1 = atof(com_token);
if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
{
+ texturename[0] = 0;
t1 = atof(com_token);
if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
{
}
}
else
- {
s1 = 0;
- strlcpy(texturename, com_token, sizeof(texturename));
- }
}
if (!texturename[0])
{
Cvar_RegisterVariable(&r_drawparticles);
Cvar_RegisterVariable(&r_drawparticles_drawdistance);
+ Cvar_RegisterVariable(&r_drawparticles_nearclip_min);
+ Cvar_RegisterVariable(&r_drawparticles_nearclip_max);
Cvar_RegisterVariable(&r_drawdecals);
Cvar_RegisterVariable(&r_drawdecals_drawdistance);
- R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
+ R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap, NULL, NULL);
}
void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
particletexture_t *tex;
float up2[3], v[3], right[3], up[3], fog, ifog, size, len, lenfactor, alpha;
float ambient[3], diffuse[3], diffusenormal[3];
- float spintime, spinrad, spincos, spinsin, spinm1, spinm2, spinm3, spinm4, baseright[3], baseup[3];
+ float palpha, spintime, spinrad, spincos, spinsin, spinm1, spinm2, spinm3, spinm4, baseright[3], baseup[3];
vec4_t colormultiplier;
+ float minparticledist_start, minparticledist_end;
+ qboolean dofade;
RSurf_ActiveWorldEntity();
spintime = r_refdef.scene.time;
+ minparticledist_start = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + r_drawparticles_nearclip_min.value;
+ minparticledist_end = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + r_drawparticles_nearclip_max.value;
+ dofade = (minparticledist_start < minparticledist_end);
+
// first generate all the vertices at once
for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
{
p = cl.particles + surfacelist[surfacelistindex];
blendmode = (pblend_t)p->blendmode;
+ palpha = p->alpha;
+ if(dofade && p->orientation != PARTICLE_VBEAM && p->orientation != PARTICLE_HBEAM)
+ palpha *= min(1, (DotProduct(p->org, r_refdef.view.forward) - minparticledist_start) / (minparticledist_end - minparticledist_start));
switch (blendmode)
{
case PBLEND_INVALID:
case PBLEND_INVMOD:
- alpha = p->alpha * colormultiplier[3];
+ alpha = palpha * colormultiplier[3];
// ensure alpha multiplier saturates properly
if (alpha > 1.0f)
alpha = 1.0f;
c4f[3] = 1;
break;
case PBLEND_ADD:
- alpha = p->alpha * colormultiplier[3];
+ alpha = palpha * colormultiplier[3];
// ensure alpha multiplier saturates properly
if (alpha > 1.0f)
alpha = 1.0f;
c4f[0] = p->color[0] * colormultiplier[0];
c4f[1] = p->color[1] * colormultiplier[1];
c4f[2] = p->color[2] * colormultiplier[2];
- c4f[3] = p->alpha * colormultiplier[3];
+ c4f[3] = palpha * colormultiplier[3];
// note: lighting is not cheap!
if (particletype[p->typeindex].lighting)
{
- R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
+ R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true, false);
c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
{
int i, a;
int drawparticles = r_drawparticles.integer;
- float minparticledist;
+ float minparticledist_start;
particle_t *p;
float gravity, frametime, f, dist, oldorg[3];
float drawdist2;
if (!cl.num_particles)
return;
- minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f;
+ minparticledist_start = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + r_drawparticles_nearclip_min.value;
gravity = frametime * cl.movevars_gravity;
update = frametime > 0;
drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
VectorCopy(p->org, oldorg);
VectorMA(p->org, frametime, p->vel, p->org);
// if (p->bounce && cl.time >= p->delayedcollisions)
- if (p->bounce && cl_particles_collisions.integer)
+ if (p->bounce && cl_particles_collisions.integer && VectorLength(p->vel))
{
trace = CL_TraceLine(oldorg, 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
// 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 (VectorLength2(p->vel) < 0.03)
+ {
+ if(p->orientation == PARTICLE_SPARK) // sparks are virtually invisible if very slow, so rather let them go off
+ goto killparticle;
+ VectorClear(p->vel);
+ }
}
if (p->typeindex != pt_static)
continue;
}
// anything else just has to be in front of the viewer and visible at this distance
- if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
+ if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist_start && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
R_MeshQueue_AddTransparent(p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
break;
}