cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
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 = {CVAR_SAVE, "cl_decals_newsystem", "1", "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)"};
part->typeindex = pt_spark;
part->bounce = 0;
VectorMA(part->org, lifetime, part->vel, endvec);
- trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false);
+ trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false, false);
part->die = cl.time + lifetime * trace.fraction;
part2 = CL_NewParticle(endvec, pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0, pqualityreduction, 0, 1, PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, -1, -1, -1, 1, 1, 0, 0, NULL);
if (part2)
{
VectorRandom(org2);
VectorMA(org, maxdist, org2, org2);
- trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
+ trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false, true);
// take the closest trace result that doesn't end up hitting a NOMARKS
// surface (sky for example)
if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
{
VectorRandom(v2);
VectorMA(org, 128, v2, v);
- trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
+ trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, false);
}
while (k < 16 && trace.fraction < 0.1f);
VectorSubtract(trace.endpos, org, v2);
char *buf;
fs_offset_t filesize;
char texturename[MAX_QPATH];
+ skinframe_t *sf;
// a note: decals need to modulate (multiply) the background color to
// properly darken it (stain), and they need to be able to alpha fade,
// we invert it again during the blendfunc to make it work...
#ifndef DUMPPARTICLEFONT
- decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR, false);
+ decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, false);
if (decalskinframe)
{
particlefonttexture = decalskinframe->base;
Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
#endif
- decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE);
+ decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE, false);
particlefonttexture = decalskinframe->base;
Mem_Free(particletexturedata);
}
#ifndef DUMPPARTICLEFONT
- particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR, true, r_texture_convertsRGB_particles.integer != 0);
+ particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true, r_texture_sRGB_skin_diffuse.integer != 0);
if (!particletexture[tex_beam].texture)
#endif
{
#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, -1, NULL);
+ particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, -1, NULL);
}
particletexture[tex_beam].s1 = 0;
particletexture[tex_beam].t1 = 0;
Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES);
continue;
}
- particletexture[i].texture = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR, false)->base;
+ sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true);
+ if(!sf)
+ {
+ // R_SkinFrame_LoadExternal already complained
+ continue;
+ }
+ particletexture[i].texture = sf->base;
particletexture[i].s1 = s1;
particletexture[i].t1 = t1;
particletexture[i].s2 = s2;
RSurf_ActiveWorldEntity();
r_refdef.stats.drawndecals += numsurfaces;
- R_Mesh_ResetTextureState();
+// R_Mesh_ResetTextureState();
GL_DepthMask(false);
GL_DepthRange(0, 1);
GL_PolygonOffset(0, 0);
float *v3f, *t2f, *c4f;
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 ambient[3], diffuse[3], diffusenormal[3];
float palpha, spintime, spinrad, spincos, spinsin, spinm1, spinm2, spinm3, spinm4, baseright[3], baseup[3];
vec4_t colormultiplier;
float minparticledist_start, minparticledist_end;
Vector4Set(colormultiplier, r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), cl_particles_alpha.value * (1.0 / 256.0f));
r_refdef.stats.particles += numsurfaces;
- R_Mesh_ResetTextureState();
+// R_Mesh_ResetTextureState();
GL_DepthMask(false);
GL_DepthRange(0, 1);
GL_PolygonOffset(0, 0);
GL_DepthTest(true);
- GL_AlphaTest(false);
GL_CullFace(GL_NONE);
spintime = r_refdef.scene.time;
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));
+ alpha = palpha * colormultiplier[3];
+ // ensure alpha multiplier saturates properly
+ if (alpha > 1.0f)
+ alpha = 1.0f;
switch (blendmode)
{
case PBLEND_INVALID:
case PBLEND_INVMOD:
- alpha = palpha * colormultiplier[3];
- // ensure alpha multiplier saturates properly
- if (alpha > 1.0f)
- alpha = 1.0f;
// additive and modulate can just fade out in fog (this is correct)
if (r_refdef.fogenabled)
alpha *= RSurf_FogVertex(p->org);
c4f[0] = p->color[0] * alpha;
c4f[1] = p->color[1] * alpha;
c4f[2] = p->color[2] * alpha;
- c4f[3] = 1;
+ c4f[3] = 0;
break;
case PBLEND_ADD:
- alpha = palpha * colormultiplier[3];
- // ensure alpha multiplier saturates properly
- if (alpha > 1.0f)
- alpha = 1.0f;
// additive and modulate can just fade out in fog (this is correct)
if (r_refdef.fogenabled)
alpha *= RSurf_FogVertex(p->org);
c4f[0] = p->color[0] * colormultiplier[0] * alpha;
c4f[1] = p->color[1] * colormultiplier[1] * alpha;
c4f[2] = p->color[2] * colormultiplier[2] * alpha;
- c4f[3] = 1;
+ c4f[3] = 0;
break;
case PBLEND_ALPHA:
c4f[0] = p->color[0] * colormultiplier[0];
c4f[1] = p->color[1] * colormultiplier[1];
c4f[2] = p->color[2] * colormultiplier[2];
- c4f[3] = palpha * colormultiplier[3];
+ c4f[3] = alpha;
// note: lighting is not cheap!
if (particletype[p->typeindex].lighting)
- {
- 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]);
- }
+ R_LightPoint(c4f, p->org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
// mix in the fog color
if (r_refdef.fogenabled)
{
c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
}
+ // for premultiplied alpha we have to apply the alpha to the color (after fog of course)
+ VectorScale(c4f, alpha, c4f);
break;
}
// copy the color into the other three vertices
{
p = cl.particles + surfacelist[surfacelistindex];
- if (blendmode != p->blendmode)
- {
- blendmode = (pblend_t)p->blendmode;
- switch(blendmode)
- {
- case PBLEND_ALPHA:
- GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- break;
- case PBLEND_INVALID:
- case PBLEND_ADD:
- GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
- break;
- case PBLEND_INVMOD:
- GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
- break;
- }
- }
if (texture != particletexture[p->texnum].texture)
{
texture = particletexture[p->texnum].texture;
R_SetupShader_Generic(texture, NULL, GL_MODULATE, 1);
}
- // iterate until we find a change in settings
- batchstart = surfacelistindex++;
- for (;surfacelistindex < numsurfaces;surfacelistindex++)
+ if (p->blendmode == PBLEND_INVMOD)
{
- p = cl.particles + surfacelist[surfacelistindex];
- if (blendmode != p->blendmode || texture != particletexture[p->texnum].texture)
- break;
+ // inverse modulate blend - group these
+ GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
+ // iterate until we find a change in settings
+ batchstart = surfacelistindex++;
+ for (;surfacelistindex < numsurfaces;surfacelistindex++)
+ {
+ p = cl.particles + surfacelist[surfacelistindex];
+ if (p->blendmode != PBLEND_INVMOD || texture != particletexture[p->texnum].texture)
+ break;
+ }
+ }
+ else
+ {
+ // additive or alpha blend - group these
+ // (we can group these because we premultiplied the texture alpha)
+ GL_BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // iterate until we find a change in settings
+ batchstart = surfacelistindex++;
+ for (;surfacelistindex < numsurfaces;surfacelistindex++)
+ {
+ p = cl.particles + surfacelist[surfacelistindex];
+ if (p->blendmode == PBLEND_INVMOD || texture != particletexture[p->texnum].texture)
+ break;
+ }
}
batchcount = surfacelistindex - batchstart;
// if (p->bounce && cl.time >= p->delayedcollisions)
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);
+ 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, 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