Files
assaultcube-/source/src/renderparticles.cpp
T
ac-stef 6f5635917f visualisation for tagclips
To keep things cheap, only tagged areas inside the editing grid are shown.
I added two variables, tagnum and taglife, to allow playing with the
parameters of the sparklies. Those should be removed later. The default
values work quite well on my really crappy onboard graphics card. Higher
values start dropping fps.

Also, the edit status line now shows the tag value of the cube pointed at.

The patch also fixes the bug, that the edit status line did not show
details about the selection, unless there was at least one entity in the
map.
2014-01-28 14:28:43 +01:00

699 lines
25 KiB
C++

// renderparticles.cpp
#include "cube.h"
static GLushort *hemiindices = NULL;
static vec *hemiverts = NULL;
static int heminumverts = 0, heminumindices = 0;
static void subdivide(int depth, int face);
static void genface(int depth, int i1, int i2, int i3)
{
int face = heminumindices; heminumindices += 3;
hemiindices[face] = i1;
hemiindices[face+1] = i2;
hemiindices[face+2] = i3;
subdivide(depth, face);
}
static void subdivide(int depth, int face)
{
if(depth-- <= 0) return;
int idx[6];
loopi(3) idx[i] = hemiindices[face+i];
loopi(3)
{
int vert = heminumverts++;
hemiverts[vert] = vec(hemiverts[idx[i]]).add(hemiverts[idx[(i+1)%3]]).normalize(); //push on to unit sphere
idx[3+i] = vert;
hemiindices[face+i] = vert;
}
subdivide(depth, face);
loopi(3) genface(depth, idx[i], idx[3+i], idx[3+(i+2)%3]);
}
//subdiv version wobble much more nicely than a lat/longitude version
static void inithemisphere(int hres, int depth)
{
const int tris = hres << (2*depth);
heminumverts = heminumindices = 0;
DELETEA(hemiverts);
DELETEA(hemiindices);
hemiverts = new vec[tris+1];
hemiindices = new GLushort[tris*3];
hemiverts[heminumverts++] = vec(0.0f, 0.0f, 1.0f); //build initial 'hres' sided pyramid
loopi(hres)
{
float a = PI2*float(i)/hres;
hemiverts[heminumverts++] = vec(cosf(a), sinf(a), 0.0f);
}
loopi(hres) genface(depth, 0, i+1, 1+(i+1)%hres);
}
GLuint createexpmodtex(int size, float minval)
{
uchar *data = new uchar[size*size], *dst = data;
loop(y, size) loop(x, size)
{
float dx = 2*float(x)/(size-1) - 1, dy = 2*float(y)/(size-1) - 1;
float z = max(0.0f, 1.0f - dx*dx - dy*dy);
if(minval) z = sqrtf(z);
else loopk(2) z *= z;
*dst++ = uchar(max(z, minval)*255);
}
GLuint tex = 0;
glGenTextures(1, &tex);
createtexture(tex, size, size, data, 3, true, false, GL_ALPHA);
delete[] data;
return tex;
}
static struct expvert
{
vec pos;
float u, v, s, t;
} *expverts = NULL;
static GLuint expmodtex[2] = {0, 0};
static GLuint lastexpmodtex = 0;
VARP(mtexplosion, 0, 1, 1);
void setupexplosion()
{
if(!hemiindices) inithemisphere(5, 2);
static int lastexpmillis = 0;
if(lastexpmillis != lastmillis || !expverts)
{
vec center = vec(13.0f, 2.3f, 7.1f);
lastexpmillis = lastmillis;
if(!expverts) expverts = new expvert[heminumverts];
loopi(heminumverts)
{
expvert &e = expverts[i];
vec &v = hemiverts[i];
e.u = v.x*0.5f + 0.001f*lastmillis;
e.v = v.y*0.5f + 0.001f*lastmillis;
e.s = v.x*0.5f + 0.5f;
e.t = v.y*0.5f + 0.5f;
float wobble = v.dot(center) + 0.002f*lastmillis;
wobble -= floor(wobble);
wobble = 1.0f + fabs(wobble - 0.5f)*0.5f;
e.pos = vec(v).mul(wobble);
}
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(expvert), &expverts->pos);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(expvert), &expverts->u);
if(mtexplosion && maxtmus>=2)
{
setuptmu(0, "C * T", "= Ca");
glActiveTexture_(GL_TEXTURE1_ARB);
glClientActiveTexture_(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
setuptmu(1, "P * Ta x 4", "Pa * Ta x 4");
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(expvert), &expverts->s);
glActiveTexture_(GL_TEXTURE0_ARB);
glClientActiveTexture_(GL_TEXTURE0_ARB);
if(!expmodtex[0]) expmodtex[0] = createexpmodtex(64, 0);
if(!expmodtex[1]) expmodtex[1] = createexpmodtex(64, 0.25f);
lastexpmodtex = 0;
}
}
void drawexplosion(bool inside, float r, float g, float b, float a)
{
if(mtexplosion && maxtmus>=2 && lastexpmodtex != expmodtex[inside ? 1 : 0])
{
glActiveTexture_(GL_TEXTURE1_ARB);
lastexpmodtex = expmodtex[inside ? 1 :0];
glBindTexture(GL_TEXTURE_2D, lastexpmodtex);
glActiveTexture_(GL_TEXTURE0_ARB);
}
loopi(!reflecting && inside ? 2 : 1)
{
glColor4f(r, g, b, i ? a/2 : a);
if(i)
{
glScalef(1, 1, -1);
glDepthFunc(GL_GEQUAL);
}
if(inside)
{
if(!reflecting)
{
glCullFace(GL_BACK);
glDrawElements(GL_TRIANGLES, heminumindices, GL_UNSIGNED_SHORT, hemiindices);
glCullFace(GL_FRONT);
}
glScalef(1, 1, -1);
}
glDrawElements(GL_TRIANGLES, heminumindices, GL_UNSIGNED_SHORT, hemiindices);
if(i) glDepthFunc(GL_LESS);
}
}
void cleanupexplosion()
{
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if(mtexplosion && maxtmus>=2)
{
resettmu(0);
glActiveTexture_(GL_TEXTURE1_ARB);
glClientActiveTexture_(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
resettmu(1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glActiveTexture_(GL_TEXTURE0_ARB);
glClientActiveTexture_(GL_TEXTURE0_ARB);
}
}
#define MAXPARTYPES 22
struct particle { vec o, d; int fade, type; int millis; particle *next; };
particle *parlist[MAXPARTYPES], *parempty = NULL;
static Texture *parttex[7];
void particleinit()
{
loopi(MAXPARTYPES) parlist[i] = NULL;
parttex[0] = textureload("packages/misc/base.png");
parttex[1] = textureload("packages/misc/smoke.png");
parttex[2] = textureload("packages/misc/explosion.png");
parttex[3] = textureload("<decal>packages/misc/bullethole.png");
parttex[4] = textureload("packages/misc/blood.png");
parttex[5] = textureload("packages/misc/scorch.png");
parttex[6] = textureload("packages/misc/muzzleflash.jpg");
}
void cleanupparticles()
{
loopi(2) if(expmodtex[i]) { glDeleteTextures(1, &expmodtex[i]); expmodtex[i] = 0; }
}
void particlereset()
{
loopi(MAXPARTYPES)
{
while(parlist[i])
{
particle *p = parlist[i];
parlist[i] = p->next;
p->next = parempty;
parempty = p;
}
}
}
void newparticle(const vec &o, const vec &d, int fade, int type)
{
if(OUTBORD((int)o.x, (int)o.y)) return;
if(!parempty)
{
particle *ps = new particle[256];
loopi(256)
{
ps[i].next = parempty;
parempty = &ps[i];
}
}
particle *p = parempty;
parempty = p->next;
p->o = o;
p->d = d;
p->fade = fade;
p->type = type;
p->millis = lastmillis;
p->next = parlist[type];
parlist[type] = p;
}
static struct parttype { int type; float r, g, b; int gr, tex; float sz; } parttypes[] =
{
{ PT_PART, 0.4f, 0.4f, 0.4f, 2, 0, 0.06f }, // yellow: sparks
{ PT_PART, 1.0f, 1.0f, 1.0f, 20, 1, 0.15f }, // grey: small smoke
{ PT_PART, 0.0f, 0.0f, 1.0f, 20, 0, 0.08f }, // blue: edit mode closest ent
{ PT_BLOOD, 0.5f, 0.0f, 0.0f, 1, 4, 0.3f }, // red: blood spats
{ PT_PART, 1.0f, 0.1f, 0.1f, 0, 1, 0.2f }, // red: demotrack
{ PT_FIREBALL, 1.0f, 0.5f, 0.5f, 0, 2, 7.0f }, // explosion fireball
{ PT_SHOTLINE, 1.0f, 1.0f, 0.7f, 0, -1, 0.0f }, // yellow: shotline
{ PT_BULLETHOLE, 1.0f, 1.0f, 1.0f, 0, 3, 0.3f }, // hole decal
{ PT_STAIN, 0.5f, 0.0f, 0.0f, 0, 4, 0.6f }, // red: blood stain
{ PT_DECAL, 1.0f, 1.0f, 1.0f, 0, 5, 1.5f }, // scorch decal
{ PT_HUDFLASH, 1.0f, 1.0f, 1.0f, 0, 6, 0.7f }, // hudgun muzzle flash
{ PT_FLASH, 1.0f, 1.0f, 1.0f, 0, 6, 0.7f }, // muzzle flash
{ PT_PART, 1.0f, 1.0f, 1.0f, 20, 0, 0.08f }, // white: edit mode ent type : light
{ PT_PART, 0.0f, 1.0f, 0.0f, 20, 0, 0.08f }, // green: edit mode ent type : spawn
{ PT_PART, 1.0f, 0.0f, 0.0f, 20, 0, 0.08f }, // red: edit mode ent type : ammo
{ PT_PART, 1.0f, 1.0f, 0.0f, 20, 0, 0.08f }, // yellow: edit mode ent type : pickup
{ PT_PART, 1.0f, 0.0f, 1.0f, 20, 0, 0.08f }, // magenta: edit mode ent type : model, sound
{ PT_PART, 1.0f, 0.5f, 0.2f, 20, 0, 0.08f }, // orange: edit mode ent type : "carrot"
{ PT_PART, 0.5f, 0.5f, 0.5f, 20, 0, 0.08f }, // grey: edit mode ent type : ladder, (pl)clip
{ PT_PART, 0.0f, 1.0f, 1.0f, 20, 0, 0.08f }, // turquoise: edit mode ent type : CTF-flag
// 2011jun18 : shotty decals
{ PT_BULLETHOLE, 0.2f, 0.2f, 1.0f, 0, 3, 0.1f }, // hole decal M
{ PT_BULLETHOLE, 0.2f, 1.0f, 0.2f, 0, 3, 0.1f }, // hole decal C
};
VAR(particlesize, 20, 100, 500);
VARP(blood, 0, 1, 1);
VARP(bloodttl, 0, 10000, 30000);
void render_particles(int time, int typemask)
{
bool rendered = false;
for(int i = MAXPARTYPES-1; i>=0; i--) if(typemask&(1<<parttypes[i].type) && parlist[i])
{
if(!rendered)
{
rendered = true;
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glDisable(GL_FOG);
}
parttype &pt = parttypes[i];
float sz = pt.sz*particlesize/100.0f;
if(pt.tex>=0) glBindTexture(GL_TEXTURE_2D, parttex[pt.tex]->id);
else glDisable(GL_TEXTURE_2D);
switch(pt.type)
{
case PT_HUDFLASH:
sethudgunperspective(true);
// fall through
case PT_FLASH:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
glColor3f(pt.r, pt.g, pt.b);
glBegin(GL_QUADS);
break;
case PT_PART:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glColor3f(pt.r, pt.g, pt.b);
glBegin(GL_QUADS);
break;
case PT_FIREBALL:
setupexplosion();
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
case PT_SHOTLINE:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(pt.r, pt.g, pt.b, 0.5f);
glBegin(GL_LINES);
break;
case PT_DECAL:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS);
break;
case PT_BULLETHOLE:
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS);
break;
case PT_BLOOD:
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
glColor3f(1-pt.r, 1-pt.g, 1-pt.b);
glBegin(GL_QUADS);
break;
case PT_STAIN:
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
glBegin(GL_QUADS);
break;
}
for(particle *p, **pp = &parlist[i]; (p = *pp);)
{
switch(pt.type)
{
case PT_HUDFLASH:
case PT_FLASH:
{
vec corners[4] =
{
vec(-camright.x+camup.x, -camright.y+camup.y, -camright.z+camup.z),
vec( camright.x+camup.x, camright.y+camup.y, camright.z+camup.z),
vec( camright.x-camup.x, camright.y-camup.y, camright.z-camup.z),
vec(-camright.x-camup.x, -camright.y-camup.y, -camright.z-camup.z)
};
loopk(4) corners[k].rotate(p->d.x, p->d.y, camdir).mul(sz*p->d.z).add(p->o);
glTexCoord2i(0, 1); glVertex3fv(corners[0].v);
glTexCoord2i(1, 1); glVertex3fv(corners[1].v);
glTexCoord2i(1, 0); glVertex3fv(corners[2].v);
glTexCoord2i(0, 0); glVertex3fv(corners[3].v);
xtraverts += 4;
break;
}
case PT_PART:
glTexCoord2i(0, 1); glVertex3f(p->o.x+(-camright.x+camup.x)*sz, p->o.y+(-camright.y+camup.y)*sz, p->o.z+(-camright.z+camup.z)*sz);
glTexCoord2i(1, 1); glVertex3f(p->o.x+( camright.x+camup.x)*sz, p->o.y+( camright.y+camup.y)*sz, p->o.z+( camright.z+camup.z)*sz);
glTexCoord2i(1, 0); glVertex3f(p->o.x+( camright.x-camup.x)*sz, p->o.y+( camright.y-camup.y)*sz, p->o.z+( camright.z-camup.z)*sz);
glTexCoord2i(0, 0); glVertex3f(p->o.x+(-camright.x-camup.x)*sz, p->o.y+(-camright.y-camup.y)*sz, p->o.z+(-camright.z-camup.z)*sz);
xtraverts += 4;
break;
case PT_FIREBALL:
{
sz = 1.0f + (pt.sz-1.0f)*min(p->fade, lastmillis-p->millis)/p->fade;
glPushMatrix();
glTranslatef(p->o.x, p->o.y, p->o.z);
bool inside = p->o.dist(camera1->o) <= sz*1.25f; //1.25 is max wobble scale
vec oc(p->o);
oc.sub(camera1->o);
if(reflecting && !refracting) oc.z = p->o.z - (hdr.waterlevel-0.3f);
glRotatef(inside ? camera1->yaw - 180 : atan2(oc.y, oc.x)/RAD - 90, 0, 0, 1);
glRotatef((inside ? camera1->pitch : asin(oc.z/oc.magnitude())/RAD) - 90, 1, 0, 0);
glRotatef(lastmillis/7.0f, 0, 0, 1);
glScalef(-sz, sz, -sz);
drawexplosion(inside, pt.r, pt.g, pt.b, 1.0f-sz/pt.sz);
glPopMatrix();
xtraverts += heminumverts;
break;
}
case PT_SHOTLINE:
glVertex3f(p->o.x, p->o.y, p->o.z);
glVertex3f(p->d.x, p->d.y, p->d.z);
xtraverts += 2;
break;
case PT_DECAL:
{
sqr *s = S((int)p->o.x, (int)p->o.y);
glColor4f(s->r/127.5f, s->g/127.5f, s->b/127.5, max(0.0f, min((p->millis+p->fade - lastmillis)/1000.0f, 0.7f)));
vec dx(0, 0, 0), dy(0, 0, 0);
loopk(3) if(p->d[k]) { dx[(k+1)%3] = -1; dy[(k+2)%3] = p->d[k]; break; }
int o = detrnd((size_t)p, 11);
static const int tc[4][2] = { {0, 1}, {1, 1}, {1, 0}, {0, 0} };
glTexCoord2i(tc[o%4][0], tc[o%4][1]); glVertex3f(p->o.x+(-dx.x+dy.x)*pt.sz, p->o.y+(-dx.y+dy.y)*pt.sz, p->o.z+(-dx.z+dy.z)*pt.sz);
glTexCoord2i(tc[(o+1)%4][0], tc[(o+1)%4][1]); glVertex3f(p->o.x+( dx.x+dy.x)*pt.sz, p->o.y+( dx.y+dy.y)*pt.sz, p->o.z+( dx.z+dy.z)*pt.sz);
glTexCoord2i(tc[(o+2)%4][0], tc[(o+2)%4][1]); glVertex3f(p->o.x+( dx.x-dy.x)*pt.sz, p->o.y+( dx.y-dy.y)*pt.sz, p->o.z+( dx.z-dy.z)*pt.sz);
glTexCoord2i(tc[(o+3)%4][0], tc[(o+3)%4][1]); glVertex3f(p->o.x+(-dx.x-dy.x)*pt.sz, p->o.y+(-dx.y-dy.y)*pt.sz, p->o.z+(-dx.z-dy.z)*pt.sz);
xtraverts += 4;
break;
}
case PT_BULLETHOLE:
{
float blend = max(0.0f, min((p->millis+p->fade - lastmillis)/1000.0f, 1.0f));
glColor4f(pt.r*blend, pt.g*blend, pt.b*blend, blend);
vec dx(0, 0, 0), dy(0, 0, 0);
int tx = 0, ty = 1;
loopk(3) if(p->d[k])
{
dx[(k+1)%3] = -1; dy[(k+2)%3] = p->d[k];
if(k<2) { tx = k^1; ty = 2; }
break;
}
glTexCoord2f(0.5f+0.5f*(-dx[tx]+dy[tx]), 0.5f+0.5f*(-dx[ty]+dy[ty])); glVertex3f(p->o.x+(-dx.x+dy.x)*pt.sz, p->o.y+(-dx.y+dy.y)*pt.sz, p->o.z+(-dx.z+dy.z)*pt.sz);
glTexCoord2f(0.5f+0.5f*( dx[tx]+dy[tx]), 0.5f+0.5f*( dx[ty]+dy[ty])); glVertex3f(p->o.x+( dx.x+dy.x)*pt.sz, p->o.y+( dx.y+dy.y)*pt.sz, p->o.z+( dx.z+dy.z)*pt.sz);
glTexCoord2f(0.5f+0.5f*( dx[tx]-dy[tx]), 0.5f+0.5f*( dx[ty]-dy[ty])); glVertex3f(p->o.x+( dx.x-dy.x)*pt.sz, p->o.y+( dx.y-dy.y)*pt.sz, p->o.z+( dx.z-dy.z)*pt.sz);
glTexCoord2f(0.5f+0.5f*(-dx[tx]-dy[tx]), 0.5f+0.5f*(-dx[ty]-dy[ty])); glVertex3f(p->o.x+(-dx.x-dy.x)*pt.sz, p->o.y+(-dx.y-dy.y)*pt.sz, p->o.z+(-dx.z-dy.z)*pt.sz);
xtraverts += 4;
break;
}
case PT_BLOOD:
{
int n = detrnd((size_t)p, 4), o = detrnd((size_t)p, 11);;
float tx = 0.5f*(n&1), ty = 0.5f*((n>>1)&1), tsz = 0.5f;
static const int tc[4][2] = { {0, 1}, {1, 1}, {1, 0}, {0, 0} };
glTexCoord2f(tx+tsz*tc[o%4][0], ty+tsz*tc[o%4][1]); glVertex3f(p->o.x+(-camright.x+camup.x)*sz, p->o.y+(-camright.y+camup.y)*sz, p->o.z+(-camright.z+camup.z)*sz);
glTexCoord2f(tx+tsz*tc[(o+1)%4][0], ty+tsz*tc[(o+1)%4][1]); glVertex3f(p->o.x+( camright.x+camup.x)*sz, p->o.y+( camright.y+camup.y)*sz, p->o.z+( camright.z+camup.z)*sz);
glTexCoord2f(tx+tsz*tc[(o+2)%4][0], ty+tsz*tc[(o+2)%4][1]); glVertex3f(p->o.x+( camright.x-camup.x)*sz, p->o.y+( camright.y-camup.y)*sz, p->o.z+( camright.z-camup.z)*sz);
glTexCoord2f(tx+tsz*tc[(o+3)%4][0], ty+tsz*tc[(o+3)%4][1]); glVertex3f(p->o.x+(-camright.x-camup.x)*sz, p->o.y+(-camright.y-camup.y)*sz, p->o.z+(-camright.z-camup.z)*sz);
xtraverts += 4;
break;
}
case PT_STAIN:
{
float blend = max(0.0f, min((p->millis+p->fade - lastmillis)/1000.0f, 1.0f));
glColor3f(blend*(1-pt.r), blend*(1-pt.g), blend*(1-pt.b));
int n = detrnd((size_t)p, 4), o = detrnd((size_t)p, 11);
float tx = 0.5f*(n&1), ty = 0.5f*((n>>1)&1), tsz = 0.5f;
static const int tc[4][2] = { {0, 1}, {1, 1}, {1, 0}, {0, 0} };
vec dx(0, 0, 0), dy(0, 0, 0);
loopk(3) if(p->d[k]) { dx[(k+1)%3] = -1; dy[(k+2)%3] = p->d[k]; break; }
glTexCoord2f(tx+tsz*tc[o%4][0], ty+tsz*tc[o%4][1]); glVertex3f(p->o.x+(-dx.x+dy.x)*pt.sz, p->o.y+(-dx.y+dy.y)*pt.sz, p->o.z+(-dx.z+dy.z)*pt.sz);
glTexCoord2f(tx+tsz*tc[(o+1)%4][0], ty+tsz*tc[(o+1)%4][1]); glVertex3f(p->o.x+( dx.x+dy.x)*pt.sz, p->o.y+( dx.y+dy.y)*pt.sz, p->o.z+( dx.z+dy.z)*pt.sz);
glTexCoord2f(tx+tsz*tc[(o+2)%4][0], ty+tsz*tc[(o+2)%4][1]); glVertex3f(p->o.x+( dx.x-dy.x)*pt.sz, p->o.y+( dx.y-dy.y)*pt.sz, p->o.z+( dx.z-dy.z)*pt.sz);
glTexCoord2f(tx+tsz*tc[(o+3)%4][0], ty+tsz*tc[(o+3)%4][1]); glVertex3f(p->o.x+(-dx.x-dy.x)*pt.sz, p->o.y+(-dx.y-dy.y)*pt.sz, p->o.z+(-dx.z-dy.z)*pt.sz);
xtraverts += 4;
break;
}
}
if(time < 0) pp = &p->next;
else if(!p->fade || lastmillis-p->millis>p->fade)
{
*pp = p->next;
p->next = parempty;
parempty = p;
}
else
{
if(pt.gr) p->o.z -= ((lastmillis-p->millis)/3.0f)*time/(pt.gr*10000);
if(pt.type==PT_PART || pt.type==PT_BLOOD)
{
p->o.add(vec(p->d).mul(time/20000.0f));
if(OUTBORD((int)p->o.x, (int)p->o.y))
{
*pp = p->next;
p->next = parempty;
parempty = p;
continue;
}
}
if(pt.type==PT_BLOOD)
{
sqr *s = S((int)p->o.x, (int)p->o.y);
if(s->type==SPACE && p->o.z<=s->floor)
{
*pp = p->next;
p->next = parempty;
parempty = p;
newparticle(vec(p->o.x, p->o.y, s->floor+0.005f), vec(0, 0, 1), bloodttl, 8);
continue;
}
}
pp = &p->next;
}
}
switch(pt.type)
{
case PT_HUDFLASH:
glEnd();
sethudgunperspective(false);
break;
case PT_PART:
case PT_SHOTLINE:
case PT_DECAL:
case PT_BULLETHOLE:
case PT_BLOOD:
case PT_STAIN:
case PT_FLASH:
glEnd();
break;
case PT_FIREBALL:
cleanupexplosion();
break;
}
if(pt.tex<0) glEnable(GL_TEXTURE_2D);
}
if(rendered)
{
glEnable(GL_FOG);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
}
void particle_emit(int type, int *args, int basetime, int seed, const vec &p)
{
if(type<0 || type>=MAXPARTYPES) return;
parttype &pt = parttypes[type];
if(pt.type==PT_FIREBALL) particle_fireball(type, p);
else if(pt.type==PT_FLASH || pt.type==PT_HUDFLASH)
{
if(lastmillis - basetime < args[0])
particle_flash(type, args[1]>0 ? args[1]/100.0f : 1.0f, seed%360, p);
}
else particle_splash(type, args[0], args[1], p);
}
void particle_flash(int type, float scale, float angle, const vec &p)
{
angle *= RAD;
newparticle(p, vec(cosf(angle), sinf(angle), scale), 0, type);
}
void particle_splash(int type, int num, int fade, const vec &p)
{
if(parttypes[type].type==PT_BLOOD && !blood) return;
loopi(num)
{
const int radius = 150;
int x, y, z;
do
{
x = rnd(radius*2)-radius;
y = rnd(radius*2)-radius;
z = rnd(radius*2)-radius;
}
while(x*x+y*y+z*z>radius*radius);
vec d((float)x, (float)y, (float)z);
newparticle(p, d, rnd(fade*3), type);
}
}
void particle_cube(int type, int num, int fade, int x, int y)
{
sqr *s = S(x, y);
const int fc = s->ceil - s->floor;
if(SOLID(s) || fc < 1) return;
loopi(num * fc / 3)
{
vec p(float(rnd(100)) / 100.0 + x, float(rnd(100)) / 100.0 + y, float(rnd(100) * fc) / 100 + s->floor), d(0, 0, 0);
newparticle(p, d, rnd(fade*3), type);
}
}
VARP(maxtrail, 1, 500, 10000);
void particle_trail(int type, int fade, const vec &s, const vec &e)
{
vec v;
float d = e.dist(s, v);
int steps = clamp(int(d*2), 1, maxtrail);
v.div(steps);
vec p = s;
loopi(steps)
{
p.add(v);
vec tmp((float)(rnd(11)-5), (float)(rnd(11)-5), (float)(rnd(11)-5));
newparticle(p, tmp, rnd(fade)+fade, type);
}
}
void particle_fireball(int type, const vec &o)
{
newparticle(o, vec(0, 0, 0), (int)((parttypes[type].sz-1.0f)*100.0f), type);
}
VARP(bulletbouncesound, 0, 1, 1);
VARP(bullethole, 0, 1, 1);
VARP(bulletholettl, 0, 10000, 30000);
VARP(bulletbouncesoundrad, 0, 15, 1000);
// 2011jun18: shotty decals
//bool addbullethole(dynent *d, const vec &from, const vec &to, float radius, bool noisy)
bool addbullethole(dynent *d, const vec &from, const vec &to, float radius, bool noisy, int type)
{
if(!bulletholettl || !bullethole) return false;
vec surface, ray(to);
ray.sub(from);
ray.normalize();
float dist = raycube(from, ray, surface), mag = to.dist(from);
if(surface.iszero() || (radius>0 && (dist < mag-radius || dist > mag+radius))) return false;
vec o(from);
o.add(ray.mul(dist));
o.add(vec(surface).normalize().mul(0.01f));
// 2011jun18: shotty decals
int tf = type > 0 ? ( type > 1 ? 21 : 20 ) : 7;
newparticle(o, surface, bulletholettl, tf);
//newparticle(o, surface, bulletholettl, 7);
if(noisy && bulletbouncesound && bulletbouncesoundrad && d!=player1 && o.dist(camera1->o) <= bulletbouncesoundrad)
{
audiomgr.playsound(o.z<hdr.waterlevel ? S_BULLETWATERHIT : S_BULLETHIT, &o, SP_LOW);
}
return true;
}
VARP(scorch, 0, 1, 1);
VARP(scorchttl, 0, 10000, 30000);
bool addscorchmark(vec &o, float radius)
{
if(!scorchttl || !scorch) return false;
sqr *s = S((int)o.x, (int)o.y);
if(s->type!=SPACE || o.z-s->floor>radius) return false;
newparticle(vec(o.x, o.y, s->floor+0.02f), vec(0, 0, 1), scorchttl, 9);
return true;
}
VARP(shotline, 0, 1, 1);
VARP(shotlinettl, 0, 75, 10000);
VARP(bulletairsound, 0, 1, 1);
VARP(bulletairsoundrad, 0, 15, 1000);
VARP(bulletairsoundsourcerad, 0, 8, 1000);
VARP(bulletairsounddestrad, 0, 8, 1000);
void addshotline(dynent *pl, const vec &from, const vec &to)
{
if(pl == player1 || !shotlinettl || !shotline) return;
bool fx = shotlinettl > 0 && (player1->isspectating() || !multiplayer(false)); // allow fx only in spect mode and locally
if(rnd(3) && !fx) return; // show all shotlines when fx enabled
int start = (camera1->o.dist(to) <= 10.0f) ? 8 : 5;
vec unitv;
float dist = to.dist(from, unitv);
unitv.div(dist);
// shotline visuals
vec o = unitv;
o.mul(dist/10+start).add(from);
vec d = unitv;
d.mul(dist/10*-(10-start-2)).add(to);
newparticle(o, d, fx ? shotlinettl : min(75, shotlinettl), 6);
// shotline sound fx
if(!bulletairsoundrad) return;
vec fromuv, touv;
float fd = camera1->o.dist(from, fromuv);
float td = camera1->o.dist(to, touv);
if(fd <= (float)bulletairsoundsourcerad || td <= (float)bulletairsounddestrad) return; // ignore nearly fired/detonated shots
vec soundpos(unitv);
soundpos.mul(fd/(fd+td)*dist);
soundpos.add(from);
if(!bulletairsound || soundpos.dist(camera1->o) > bulletairsoundrad) return; // outside player radius
audiomgr.playsound(S_BULLETAIR1 + rnd(2), &soundpos, SP_LOW);
}