diff options
Diffstat (limited to 'src/engine/renderparticles.cpp')
| -rw-r--r-- | src/engine/renderparticles.cpp | 2402 |
1 files changed, 1199 insertions, 1203 deletions
diff --git a/src/engine/renderparticles.cpp b/src/engine/renderparticles.cpp index c6994bf..accc204 100644 --- a/src/engine/renderparticles.cpp +++ b/src/engine/renderparticles.cpp @@ -15,54 +15,51 @@ static bool canemit = false, regenemitters = false, canstep = false; static bool canemitparticles() { - if(reflecting || refracting) return false; - return canemit || emitoffset; + if(reflecting || refracting) return false; + return canemit || emitoffset; } VARP(showparticles, 0, 1, 1); VAR(cullparticles, 0, 1, 1); VAR(replayparticles, 0, 1, 1); VARN(seedparticles, seedmillis, 0, 3000, 10000); -VAR(dbgpcull, 0, 0, 1); -VAR(dbgpseed, 0, 0, 1); struct particleemitter { - extentity *ent; - vec bbmin, bbmax; - vec center; - float radius; - ivec cullmin, cullmax; - int maxfade, lastemit, lastcull; - - particleemitter(extentity *ent) - : ent(ent), bbmin(ent->o), bbmax(ent->o), maxfade(-1), lastemit(0), lastcull(0) - {} - - void finalize() - { - center = vec(bbmin).add(bbmax).mul(0.5f); - radius = bbmin.dist(bbmax)/2; - cullmin = ivec(int(floor(bbmin.x)), int(floor(bbmin.y)), int(floor(bbmin.z))); - cullmax = ivec(int(ceil(bbmax.x)), int(ceil(bbmax.y)), int(ceil(bbmax.z))); - if(dbgpseed) conoutf(CON_DEBUG, "radius: %f, maxfade: %d", radius, maxfade); - } - - void extendbb(const vec &o, float size = 0) - { - bbmin.x = min(bbmin.x, o.x - size); - bbmin.y = min(bbmin.y, o.y - size); - bbmin.z = min(bbmin.z, o.z - size); - bbmax.x = max(bbmax.x, o.x + size); - bbmax.y = max(bbmax.y, o.y + size); - bbmax.z = max(bbmax.z, o.z + size); - } - - void extendbb(float z, float size = 0) - { - bbmin.z = min(bbmin.z, z - size); - bbmax.z = max(bbmax.z, z + size); - } + extentity *ent; + vec bbmin, bbmax; + vec center; + float radius; + ivec cullmin, cullmax; + int maxfade, lastemit, lastcull; + + particleemitter(extentity *ent) + : ent(ent), bbmin(ent->o), bbmax(ent->o), maxfade(-1), lastemit(0), lastcull(0) + {} + + void finalize() + { + center = vec(bbmin).add(bbmax).mul(0.5f); + radius = bbmin.dist(bbmax)/2; + cullmin = ivec(int(floor(bbmin.x)), int(floor(bbmin.y)), int(floor(bbmin.z))); + cullmax = ivec(int(ceil(bbmax.x)), int(ceil(bbmax.y)), int(ceil(bbmax.z))); + } + + void extendbb(const vec &o, float size = 0) + { + bbmin.x = min(bbmin.x, o.x - size); + bbmin.y = min(bbmin.y, o.y - size); + bbmin.z = min(bbmin.z, o.z - size); + bbmax.x = max(bbmax.x, o.x + size); + bbmax.y = max(bbmax.y, o.y + size); + bbmax.z = max(bbmax.z, o.z + size); + } + + void extendbb(float z, float size = 0) + { + bbmin.z = min(bbmin.z, z - size); + bbmax.z = max(bbmax.z, z + size); + } }; static vector<particleemitter> emitters; @@ -70,80 +67,80 @@ static particleemitter *seedemitter = NULL; void clearparticleemitters() { - emitters.setsize(0); - regenemitters = true; + emitters.setsize(0); + regenemitters = true; } void addparticleemitters() { - emitters.setsize(0); - const vector<extentity *> &ents = entities::getents(); - loopv(ents) - { - extentity &e = *ents[i]; - if(e.type != ET_PARTICLES) continue; - emitters.add(particleemitter(&e)); - } - regenemitters = false; + emitters.setsize(0); + const vector<extentity *> &ents = entities::getents(); + loopv(ents) + { + extentity &e = *ents[i]; + if(e.type != ET_PARTICLES) continue; + emitters.add(particleemitter(&e)); + } + regenemitters = false; } enum { - PT_PART = 0, - PT_TAPE, - PT_TRAIL, - PT_TEXT, - PT_TEXTICON, - PT_METER, - PT_METERVS, - PT_FIREBALL, - PT_LIGHTNING, - PT_FLARE, - - PT_MOD = 1<<8, - PT_RND4 = 1<<9, - PT_LERP = 1<<10, // use very sparingly - order of blending issues - PT_TRACK = 1<<11, - PT_GLARE = 1<<12, - PT_SOFT = 1<<13, - PT_HFLIP = 1<<14, - PT_VFLIP = 1<<15, - PT_ROT = 1<<16, - PT_CULL = 1<<17, - PT_FEW = 1<<18, - PT_ICON = 1<<19, - PT_NOTEX = 1<<20, - PT_SHADER = 1<<21, - PT_FLIP = PT_HFLIP | PT_VFLIP | PT_ROT + PT_PART = 0, + PT_TAPE, + PT_TRAIL, + PT_TEXT, + PT_TEXTICON, + PT_METER, + PT_METERVS, + PT_FIREBALL, + PT_LIGHTNING, + PT_FLARE, + + PT_MOD = 1<<8, + PT_RND4 = 1<<9, + PT_LERP = 1<<10, // use very sparingly - order of blending issues + PT_TRACK = 1<<11, + PT_GLARE = 1<<12, + PT_SOFT = 1<<13, + PT_HFLIP = 1<<14, + PT_VFLIP = 1<<15, + PT_ROT = 1<<16, + PT_CULL = 1<<17, + PT_FEW = 1<<18, + PT_ICON = 1<<19, + PT_NOTEX = 1<<20, + PT_SHADER = 1<<21, + PT_FLIP = PT_HFLIP | PT_VFLIP | PT_ROT }; const char *partnames[] = { "part", "tape", "trail", "text", "texticon", "meter", "metervs", "fireball", "lightning", "flare" }; struct particle { - vec o, d; - int gravity, fade, millis; - bvec color; - uchar flags; - float size; - union - { - const char *text; - float val; - physent *owner; - struct - { - uchar color2[3]; - uchar progress; - }; - }; + vec o, d; + int gravity, fade, millis; + bvec color; + uchar flags; + float size; + union + { + const char *text; + float val; + physent *owner; + struct + { + uchar color2[3]; + uchar progress; + }; + }; }; struct partvert { - vec pos; - bvec4 color; - vec2 tc; + vec pos; + bvec4 color; + vec2 tc; }; #define COLLIDERADIUS 8.0f @@ -151,381 +148,381 @@ struct partvert struct partrenderer { - Texture *tex; - const char *texname; - int texclamp; - uint type; - int collide; - string info; - - partrenderer(const char *texname, int texclamp, int type, int collide = 0) - : tex(NULL), texname(texname), texclamp(texclamp), type(type), collide(collide) - { - } - partrenderer(int type, int collide = 0) - : tex(NULL), texname(NULL), texclamp(0), type(type), collide(collide) - { - } - virtual ~partrenderer() - { - } - - virtual void init(int n) { } - virtual void reset() = 0; - virtual void resettracked(physent *owner) { } - virtual particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity = 0) = 0; - virtual int adddepthfx(vec &bbmin, vec &bbmax) { return 0; } - virtual void update() { } - virtual void render() = 0; - virtual bool haswork() = 0; - virtual void cleanup() {} - - virtual void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity) - { - } - - //blend = 0 => remove it - void calc(particle *p, int &blend, int &ts, vec &o, vec &d, bool step = true) - { - o = p->o; - d = p->d; - if(type&PT_TRACK && p->owner) game::particletrack(p->owner, o, d); - if(p->fade <= 5) - { - ts = 1; - blend = 255; - } - else - { - ts = lastmillis-p->millis; - blend = max(255 - (ts<<8)/p->fade, 0); - if(p->gravity) - { - if(ts > p->fade) ts = p->fade; - float t = ts; - o.add(vec(d).mul(t/5000.0f)); - o.z -= t*t/(2.0f * 5000.0f * p->gravity); - } - if(collide && o.z < p->val && step) - { - if(collide >= 0) - { - vec surface; - float floorz = rayfloor(vec(o.x, o.y, p->val), surface, RAY_CLIPMAT, COLLIDERADIUS); - float collidez = floorz<0 ? o.z-COLLIDERADIUS : p->val - floorz; - if(o.z >= collidez+COLLIDEERROR) - p->val = collidez+COLLIDEERROR; - else - { - adddecal(collide, vec(o.x, o.y, collidez), vec(p->o).sub(o).normalize(), 2*p->size, p->color, type&PT_RND4 ? (p->flags>>5)&3 : 0); - blend = 0; - } - } - else blend = 0; - } - } - } + Texture *tex; + const char *texname; + int texclamp; + uint type; + int collide; + string info; + + partrenderer(const char *texname, int texclamp, int type, int collide = 0) + : tex(NULL), texname(texname), texclamp(texclamp), type(type), collide(collide) + { + } + partrenderer(int type, int collide = 0) + : tex(NULL), texname(NULL), texclamp(0), type(type), collide(collide) + { + } + virtual ~partrenderer() + { + } + + virtual void init(int n) { } + virtual void reset() = 0; + virtual void resettracked(physent *owner) { } + virtual particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity = 0) = 0; + virtual int adddepthfx(vec &bbmin, vec &bbmax) { return 0; } + virtual void update() { } + virtual void render() = 0; + virtual bool haswork() = 0; + virtual void cleanup() {} + + virtual void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity) + { + } + + //blend = 0 => remove it + void calc(particle *p, int &blend, int &ts, vec &o, vec &d, bool step = true) + { + o = p->o; + d = p->d; + if(type&PT_TRACK && p->owner) game::particletrack(p->owner, o, d); + if(p->fade <= 5) + { + ts = 1; + blend = 255; + } + else + { + ts = lastmillis-p->millis; + blend = max(255 - (ts<<8)/p->fade, 0); + if(p->gravity) + { + if(ts > p->fade) ts = p->fade; + float t = ts; + o.add(vec(d).mul(t/5000.0f)); + o.z -= t*t/(2.0f * 5000.0f * p->gravity); + } + if(collide && o.z < p->val && step) + { + if(collide >= 0) + { + vec surface; + float floorz = rayfloor(vec(o.x, o.y, p->val), surface, RAY_CLIPMAT, COLLIDERADIUS); + float collidez = floorz<0 ? o.z-COLLIDERADIUS : p->val - floorz; + if(o.z >= collidez+COLLIDEERROR) + p->val = collidez+COLLIDEERROR; + else + { + adddecal(collide, vec(o.x, o.y, collidez), vec(p->o).sub(o).normalize(), 2*p->size, p->color, type&PT_RND4 ? (p->flags>>5)&3 : 0); + blend = 0; + } + } + else blend = 0; + } + } + } }; struct listparticle : particle { - listparticle *next; + listparticle *next; }; VARP(outlinemeters, 0, 0, 1); struct listrenderer : partrenderer { - static listparticle *parempty; - listparticle *list; - - listrenderer(const char *texname, int texclamp, int type, int collide = 0) - : partrenderer(texname, texclamp, type, collide), list(NULL) - { - } - listrenderer(int type, int collide = 0) - : partrenderer(type, collide), list(NULL) - { - } - - virtual ~listrenderer() - { - } - - virtual void killpart(listparticle *p) - { - } - - void reset() - { - if(!list) return; - listparticle *p = list; - for(;;) - { - killpart(p); - if(p->next) p = p->next; - else break; - } - p->next = parempty; - parempty = list; - list = NULL; - } - - void resettracked(physent *owner) - { - if(!(type&PT_TRACK)) return; - for(listparticle **prev = &list, *cur = list; cur; cur = *prev) - { - if(!owner || cur->owner==owner) - { - *prev = cur->next; - cur->next = parempty; - parempty = cur; - } - else prev = &cur->next; - } - } - - particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity) - { - if(!parempty) - { - listparticle *ps = new listparticle[256]; - loopi(255) ps[i].next = &ps[i+1]; - ps[255].next = parempty; - parempty = ps; - } - listparticle *p = parempty; - parempty = p->next; - p->next = list; - list = p; - p->o = o; - p->d = d; - p->gravity = gravity; - p->fade = fade; - p->millis = lastmillis + emitoffset; - p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF); - p->size = size; - p->owner = NULL; - p->flags = 0; - return p; - } - - int count() - { - int num = 0; - listparticle *lp; - for(lp = list; lp; lp = lp->next) num++; - return num; - } - - bool haswork() - { - return (list != NULL); - } - - virtual void startrender() = 0; - virtual void endrender() = 0; - virtual void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) = 0; - - void render() - { - startrender(); - if(texname) - { - if(!tex) tex = textureload(texname, texclamp); - glBindTexture(GL_TEXTURE_2D, tex->id); - } - - for(listparticle **prev = &list, *p = list; p; p = *prev) - { - vec o, d; - int blend, ts; - calc(p, blend, ts, o, d, canstep); - if(blend > 0) - { - renderpart(p, o, d, blend, ts); - - if(p->fade > 5 || !canstep) - { - prev = &p->next; - continue; - } - } - //remove - *prev = p->next; - p->next = parempty; - killpart(p); - parempty = p; - } - - endrender(); - } + static listparticle *parempty; + listparticle *list; + + listrenderer(const char *texname, int texclamp, int type, int collide = 0) + : partrenderer(texname, texclamp, type, collide), list(NULL) + { + } + listrenderer(int type, int collide = 0) + : partrenderer(type, collide), list(NULL) + { + } + + virtual ~listrenderer() + { + } + + virtual void killpart(listparticle *p) + { + } + + void reset() + { + if(!list) return; + listparticle *p = list; + for(;;) + { + killpart(p); + if(p->next) p = p->next; + else break; + } + p->next = parempty; + parempty = list; + list = NULL; + } + + void resettracked(physent *owner) + { + if(!(type&PT_TRACK)) return; + for(listparticle **prev = &list, *cur = list; cur; cur = *prev) + { + if(!owner || cur->owner==owner) + { + *prev = cur->next; + cur->next = parempty; + parempty = cur; + } + else prev = &cur->next; + } + } + + particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity) + { + if(!parempty) + { + listparticle *ps = new listparticle[256]; + loopi(255) ps[i].next = &ps[i+1]; + ps[255].next = parempty; + parempty = ps; + } + listparticle *p = parempty; + parempty = p->next; + p->next = list; + list = p; + p->o = o; + p->d = d; + p->gravity = gravity; + p->fade = fade; + p->millis = lastmillis + emitoffset; + p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF); + p->size = size; + p->owner = NULL; + p->flags = 0; + return p; + } + + int count() + { + int num = 0; + listparticle *lp; + for(lp = list; lp; lp = lp->next) num++; + return num; + } + + bool haswork() + { + return (list != NULL); + } + + virtual void startrender() = 0; + virtual void endrender() = 0; + virtual void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) = 0; + + void render() + { + startrender(); + if(texname) + { + if(!tex) tex = textureload(texname, texclamp); + glBindTexture(GL_TEXTURE_2D, tex->id); + } + + for(listparticle **prev = &list, *p = list; p; p = *prev) + { + vec o, d; + int blend, ts; + calc(p, blend, ts, o, d, canstep); + if(blend > 0) + { + renderpart(p, o, d, blend, ts); + + if(p->fade > 5 || !canstep) + { + prev = &p->next; + continue; + } + } + //remove + *prev = p->next; + p->next = parempty; + killpart(p); + parempty = p; + } + + endrender(); + } }; listparticle *listrenderer::parempty = NULL; struct meterrenderer : listrenderer { - meterrenderer(int type) - : listrenderer(type|PT_NOTEX|PT_LERP) - {} - - void startrender() - { - glDisable(GL_BLEND); - gle::defvertex(); - } - - void endrender() - { - glEnable(GL_BLEND); - } - - void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) - { - int basetype = type&0xFF; - float scale = FONTH*p->size/80.0f, right = 8, left = p->progress/100.0f*right; - matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); - m.scale(scale); - m.translate(-right/2.0f, 0, 0); - - if(outlinemeters) - { - gle::colorf(0, 0.8f, 0); - gle::begin(GL_TRIANGLE_STRIP); - loopk(10) - { - const vec2 &sc = sincos360[k*(180/(10-1))]; - float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x; - gle::attrib(m.transform(vec2(-c, s))); - gle::attrib(m.transform(vec2(right + c, s))); - } - gle::end(); - } - - if(basetype==PT_METERVS) gle::colorub(p->color2[0], p->color2[1], p->color2[2]); - else gle::colorf(0, 0, 0); - gle::begin(GL_TRIANGLE_STRIP); - loopk(10) - { - const vec2 &sc = sincos360[k*(180/(10-1))]; - float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x; - gle::attrib(m.transform(vec2(left + c, s))); - gle::attrib(m.transform(vec2(right + c, s))); - } - gle::end(); - - if(outlinemeters) - { - gle::colorf(0, 0.8f, 0); - gle::begin(GL_TRIANGLE_FAN); - loopk(10) - { - const vec2 &sc = sincos360[k*(180/(10-1))]; - float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x; - gle::attrib(m.transform(vec2(left + c, s))); - } - gle::end(); - } - - gle::color(p->color); - gle::begin(GL_TRIANGLE_STRIP); - loopk(10) - { - const vec2 &sc = sincos360[k*(180/(10-1))]; - float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x; - gle::attrib(m.transform(vec2(-c, s))); - gle::attrib(m.transform(vec2(left + c, s))); - } - gle::end(); - } + meterrenderer(int type) + : listrenderer(type|PT_NOTEX|PT_LERP) + {} + + void startrender() + { + glDisable(GL_BLEND); + gle::defvertex(); + } + + void endrender() + { + glEnable(GL_BLEND); + } + + void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) + { + int basetype = type&0xFF; + float scale = FONTH*p->size/80.0f, right = 8, left = p->progress/100.0f*right; + matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); + m.scale(scale); + m.translate(-right/2.0f, 0, 0); + + if(outlinemeters) + { + gle::colorf(0, 0.8f, 0); + gle::begin(GL_TRIANGLE_STRIP); + loopk(10) + { + const vec2 &sc = sincos360[k*(180/(10-1))]; + float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x; + gle::attrib(m.transform(vec2(-c, s))); + gle::attrib(m.transform(vec2(right + c, s))); + } + gle::end(); + } + + if(basetype==PT_METERVS) gle::colorub(p->color2[0], p->color2[1], p->color2[2]); + else gle::colorf(0, 0, 0); + gle::begin(GL_TRIANGLE_STRIP); + loopk(10) + { + const vec2 &sc = sincos360[k*(180/(10-1))]; + float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x; + gle::attrib(m.transform(vec2(left + c, s))); + gle::attrib(m.transform(vec2(right + c, s))); + } + gle::end(); + + if(outlinemeters) + { + gle::colorf(0, 0.8f, 0); + gle::begin(GL_TRIANGLE_FAN); + loopk(10) + { + const vec2 &sc = sincos360[k*(180/(10-1))]; + float c = (0.5f + 0.1f)*sc.y, s = 0.5f - (0.5f + 0.1f)*sc.x; + gle::attrib(m.transform(vec2(left + c, s))); + } + gle::end(); + } + + gle::color(p->color); + gle::begin(GL_TRIANGLE_STRIP); + loopk(10) + { + const vec2 &sc = sincos360[k*(180/(10-1))]; + float c = 0.5f*sc.y, s = 0.5f - 0.5f*sc.x; + gle::attrib(m.transform(vec2(-c, s))); + gle::attrib(m.transform(vec2(left + c, s))); + } + gle::end(); + } }; static meterrenderer meters(PT_METER), metervs(PT_METERVS); struct textrenderer : listrenderer { - textrenderer(int type) - : listrenderer(type) - {} - - void startrender() - { - } - - void endrender() - { - } - - void killpart(listparticle *p) - { - if(p->text && p->flags&1) delete[] p->text; - } - - void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) - { - float scale = p->size/80.0f, xoff = -(text_width(p->text) + ((p->flags>>1)&7)*FONTH)/2, yoff = 0; - - matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); - m.scale(scale); - m.translate(xoff, yoff, 50); - - textmatrix = &m; - draw_text(p->text, 0, 0, p->color.r, p->color.g, p->color.b, blend); - textmatrix = NULL; - } + textrenderer(int type) + : listrenderer(type) + {} + + void startrender() + { + } + + void endrender() + { + } + + void killpart(listparticle *p) + { + if(p->text && p->flags&1) delete[] p->text; + } + + void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) + { + float scale = p->size/80.0f, xoff = -(text_width(p->text) + ((p->flags>>1)&7)*FONTH)/2, yoff = 0; + + matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); + m.scale(scale); + m.translate(xoff, yoff, 50); + + textmatrix = &m; + draw_text(p->text, 0, 0, p->color.r, p->color.g, p->color.b, blend); + textmatrix = NULL; + } }; static textrenderer texts(PT_TEXT|PT_LERP); struct texticonrenderer : listrenderer { - texticonrenderer(const char *texname, int type) - : listrenderer(texname, 3, type) - {} - - void startrender() - { - gle::defvertex(); - gle::deftexcoord0(); - gle::defcolor(4, GL_UNSIGNED_BYTE); - gle::begin(GL_QUADS); - } - - void endrender() - { - gle::end(); - } - - void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) - { - float scale = p->size/80.0f, xoff = p->val, yoff = 0; - - matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); - m.scale(scale); - m.translate(xoff, yoff, 50); - - float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3); - - gle::attrib(m.transform(vec2(0, 0))); - gle::attrib(tx, ty); - gle::attrib(p->color, blend); - gle::attrib(m.transform(vec2(FONTH, 0))); - gle::attrib(tx + 0.25f, ty); - gle::attrib(p->color, blend); - gle::attrib(m.transform(vec2(FONTH, FONTH))); - gle::attrib(tx + 0.25f, ty + 0.25f); - gle::attrib(p->color, blend); - gle::attrib(m.transform(vec2(0, FONTH))); - gle::attrib(tx, ty + 0.25f); - gle::attrib(p->color, blend); - } + texticonrenderer(const char *texname, int type) + : listrenderer(texname, 3, type) + {} + + void startrender() + { + gle::defvertex(); + gle::deftexcoord0(); + gle::defcolor(4, GL_UNSIGNED_BYTE); + gle::begin(GL_QUADS); + } + + void endrender() + { + gle::end(); + } + + void renderpart(listparticle *p, const vec &o, const vec &d, int blend, int ts) + { + float scale = p->size/80.0f, xoff = p->val, yoff = 0; + + matrix4x3 m(camright, vec(camup).neg(), vec(camdir).neg(), o); + m.scale(scale); + m.translate(xoff, yoff, 50); + + float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3); + + gle::attrib(m.transform(vec2(0, 0))); + gle::attrib(tx, ty); + gle::attrib(p->color, blend); + gle::attrib(m.transform(vec2(FONTH, 0))); + gle::attrib(tx + 0.25f, ty); + gle::attrib(p->color, blend); + gle::attrib(m.transform(vec2(FONTH, FONTH))); + gle::attrib(tx + 0.25f, ty + 0.25f); + gle::attrib(p->color, blend); + gle::attrib(m.transform(vec2(0, FONTH))); + gle::attrib(tx, ty + 0.25f); + gle::attrib(p->color, blend); + } }; static texticonrenderer texticons("packages/hud/items.png", PT_TEXTICON|PT_LERP); template<int T> static inline void modifyblend(const vec &o, int &blend) { - blend = min(blend<<2, 255); + blend = min(blend<<2, 255); } template<> @@ -536,306 +533,306 @@ inline void modifyblend<PT_TAPE>(const vec &o, int &blend) template<int T> static inline void genpos(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs) { - vec udir = vec(camup).sub(camright).mul(size); - vec vdir = vec(camup).add(camright).mul(size); - vs[0].pos = vec(o.x + udir.x, o.y + udir.y, o.z + udir.z); - vs[1].pos = vec(o.x + vdir.x, o.y + vdir.y, o.z + vdir.z); - vs[2].pos = vec(o.x - udir.x, o.y - udir.y, o.z - udir.z); - vs[3].pos = vec(o.x - vdir.x, o.y - vdir.y, o.z - vdir.z); + vec udir = vec(camup).sub(camright).mul(size); + vec vdir = vec(camup).add(camright).mul(size); + vs[0].pos = vec(o.x + udir.x, o.y + udir.y, o.z + udir.z); + vs[1].pos = vec(o.x + vdir.x, o.y + vdir.y, o.z + vdir.z); + vs[2].pos = vec(o.x - udir.x, o.y - udir.y, o.z - udir.z); + vs[3].pos = vec(o.x - vdir.x, o.y - vdir.y, o.z - vdir.z); } template<> inline void genpos<PT_TAPE>(const vec &o, const vec &d, float size, int ts, int grav, partvert *vs) { - vec dir1 = d, dir2 = d, c; - dir1.sub(o); - dir2.sub(camera1->o); - c.cross(dir2, dir1).normalize().mul(size); - vs[0].pos = vec(d.x-c.x, d.y-c.y, d.z-c.z); - vs[1].pos = vec(o.x-c.x, o.y-c.y, o.z-c.z); - vs[2].pos = vec(o.x+c.x, o.y+c.y, o.z+c.z); - vs[3].pos = vec(d.x+c.x, d.y+c.y, d.z+c.z); + vec dir1 = d, dir2 = d, c; + dir1.sub(o); + dir2.sub(camera1->o); + c.cross(dir2, dir1).normalize().mul(size); + vs[0].pos = vec(d.x-c.x, d.y-c.y, d.z-c.z); + vs[1].pos = vec(o.x-c.x, o.y-c.y, o.z-c.z); + vs[2].pos = vec(o.x+c.x, o.y+c.y, o.z+c.z); + vs[3].pos = vec(d.x+c.x, d.y+c.y, d.z+c.z); } template<> inline void genpos<PT_TRAIL>(const vec &o, const vec &d, float size, int ts, int grav, partvert *vs) { - vec e = d; - if(grav) e.z -= float(ts)/grav; - e.div(-75.0f).add(o); - genpos<PT_TAPE>(o, e, size, ts, grav, vs); + vec e = d; + if(grav) e.z -= float(ts)/grav; + e.div(-75.0f).add(o); + genpos<PT_TAPE>(o, e, size, ts, grav, vs); } template<int T> static inline void genrotpos(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs, int rot) { - genpos<T>(o, d, size, grav, ts, vs); + genpos<T>(o, d, size, grav, ts, vs); } #define ROTCOEFFS(n) { \ - vec(-1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \ - vec( 1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \ - vec( 1, -1, 0).rotate_around_z(n*2*M_PI/32.0f), \ - vec(-1, -1, 0).rotate_around_z(n*2*M_PI/32.0f) \ + vec(-1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \ + vec( 1, 1, 0).rotate_around_z(n*2*M_PI/32.0f), \ + vec( 1, -1, 0).rotate_around_z(n*2*M_PI/32.0f), \ + vec(-1, -1, 0).rotate_around_z(n*2*M_PI/32.0f) \ } static const vec rotcoeffs[32][4] = { - ROTCOEFFS(0), ROTCOEFFS(1), ROTCOEFFS(2), ROTCOEFFS(3), ROTCOEFFS(4), ROTCOEFFS(5), ROTCOEFFS(6), ROTCOEFFS(7), - ROTCOEFFS(8), ROTCOEFFS(9), ROTCOEFFS(10), ROTCOEFFS(11), ROTCOEFFS(12), ROTCOEFFS(13), ROTCOEFFS(14), ROTCOEFFS(15), - ROTCOEFFS(16), ROTCOEFFS(17), ROTCOEFFS(18), ROTCOEFFS(19), ROTCOEFFS(20), ROTCOEFFS(21), ROTCOEFFS(22), ROTCOEFFS(7), - ROTCOEFFS(24), ROTCOEFFS(25), ROTCOEFFS(26), ROTCOEFFS(27), ROTCOEFFS(28), ROTCOEFFS(29), ROTCOEFFS(30), ROTCOEFFS(31), + ROTCOEFFS(0), ROTCOEFFS(1), ROTCOEFFS(2), ROTCOEFFS(3), ROTCOEFFS(4), ROTCOEFFS(5), ROTCOEFFS(6), ROTCOEFFS(7), + ROTCOEFFS(8), ROTCOEFFS(9), ROTCOEFFS(10), ROTCOEFFS(11), ROTCOEFFS(12), ROTCOEFFS(13), ROTCOEFFS(14), ROTCOEFFS(15), + ROTCOEFFS(16), ROTCOEFFS(17), ROTCOEFFS(18), ROTCOEFFS(19), ROTCOEFFS(20), ROTCOEFFS(21), ROTCOEFFS(22), ROTCOEFFS(7), + ROTCOEFFS(24), ROTCOEFFS(25), ROTCOEFFS(26), ROTCOEFFS(27), ROTCOEFFS(28), ROTCOEFFS(29), ROTCOEFFS(30), ROTCOEFFS(31), }; template<> inline void genrotpos<PT_PART>(const vec &o, const vec &d, float size, int grav, int ts, partvert *vs, int rot) { - const vec *coeffs = rotcoeffs[rot]; - (vs[0].pos = o).add(vec(camright).mul(coeffs[0].x*size)).add(vec(camup).mul(coeffs[0].y*size)); - (vs[1].pos = o).add(vec(camright).mul(coeffs[1].x*size)).add(vec(camup).mul(coeffs[1].y*size)); - (vs[2].pos = o).add(vec(camright).mul(coeffs[2].x*size)).add(vec(camup).mul(coeffs[2].y*size)); - (vs[3].pos = o).add(vec(camright).mul(coeffs[3].x*size)).add(vec(camup).mul(coeffs[3].y*size)); + const vec *coeffs = rotcoeffs[rot]; + (vs[0].pos = o).add(vec(camright).mul(coeffs[0].x*size)).add(vec(camup).mul(coeffs[0].y*size)); + (vs[1].pos = o).add(vec(camright).mul(coeffs[1].x*size)).add(vec(camup).mul(coeffs[1].y*size)); + (vs[2].pos = o).add(vec(camright).mul(coeffs[2].x*size)).add(vec(camup).mul(coeffs[2].y*size)); + (vs[3].pos = o).add(vec(camright).mul(coeffs[3].x*size)).add(vec(camup).mul(coeffs[3].y*size)); } template<int T> static inline void seedpos(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav) { - if(grav) - { - vec end(o); - float t = fade; - end.add(vec(d).mul(t/5000.0f)); - end.z -= t*t/(2.0f * 5000.0f * grav); - pe.extendbb(end, size); - - float tpeak = d.z*grav; - if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size); - } + if(grav) + { + vec end(o); + float t = fade; + end.add(vec(d).mul(t/5000.0f)); + end.z -= t*t/(2.0f * 5000.0f * grav); + pe.extendbb(end, size); + + float tpeak = d.z*grav; + if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size); + } } template<> inline void seedpos<PT_TAPE>(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav) { - pe.extendbb(d, size); + pe.extendbb(d, size); } template<> inline void seedpos<PT_TRAIL>(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int grav) { - vec e = d; - if(grav) e.z -= float(fade)/grav; - e.div(-75.0f).add(o); - pe.extendbb(e, size); + vec e = d; + if(grav) e.z -= float(fade)/grav; + e.div(-75.0f).add(o); + pe.extendbb(e, size); } template<int T> struct varenderer : partrenderer { - partvert *verts; - particle *parts; - int maxparts, numparts, lastupdate, rndmask; - GLuint vbo; - - varenderer(const char *texname, int type, int collide = 0) - : partrenderer(texname, 3, type, collide), - verts(NULL), parts(NULL), maxparts(0), numparts(0), lastupdate(-1), rndmask(0), vbo(0) - { - if(type & PT_HFLIP) rndmask |= 0x01; - if(type & PT_VFLIP) rndmask |= 0x02; - if(type & PT_ROT) rndmask |= 0x1F<<2; - if(type & PT_RND4) rndmask |= 0x03<<5; - } - - void cleanup() - { - if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; } - } - - void init(int n) - { - DELETEA(parts); - DELETEA(verts); - parts = new particle[n]; - verts = new partvert[n*4]; - maxparts = n; - numparts = 0; - lastupdate = -1; - } - - void reset() - { - numparts = 0; - lastupdate = -1; - } - - void resettracked(physent *owner) - { - if(!(type&PT_TRACK)) return; - loopi(numparts) - { - particle *p = parts+i; - if(!owner || (p->owner == owner)) p->fade = -1; - } - lastupdate = -1; - } - - int count() - { - return numparts; - } - - bool haswork() - { - return (numparts > 0); - } - - particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity) - { - particle *p = parts + (numparts < maxparts ? numparts++ : rnd(maxparts)); //next free slot, or kill a random kitten - p->o = o; - p->d = d; - p->gravity = gravity; - p->fade = fade; - p->millis = lastmillis + emitoffset; - p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF); - p->size = size; - p->owner = NULL; - p->flags = 0x80 | (rndmask ? rnd(0x80) & rndmask : 0); - lastupdate = -1; - return p; - } - - void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity) - { - pe.maxfade = max(pe.maxfade, fade); - size *= SQRT2; - pe.extendbb(o, size); - - seedpos<T>(pe, o, d, fade, size, gravity); - if(!gravity) return; - - vec end(o); - float t = fade; - end.add(vec(d).mul(t/5000.0f)); - end.z -= t*t/(2.0f * 5000.0f * gravity); - pe.extendbb(end, size); - - float tpeak = d.z*gravity; - if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size); - } - - void genverts(particle *p, partvert *vs, bool regen) - { - vec o, d; - int blend, ts; - - calc(p, blend, ts, o, d); - if(blend <= 1 || p->fade <= 5) p->fade = -1; //mark to remove on next pass (i.e. after render) - - modifyblend<T>(o, blend); - - if(regen) - { - p->flags &= ~0x80; - - #define SETTEXCOORDS(u1c, u2c, v1c, v2c, body) \ - { \ - float u1 = u1c, u2 = u2c, v1 = v1c, v2 = v2c; \ - body; \ - vs[0].tc = vec2(u1, v1); \ - vs[1].tc = vec2(u2, v1); \ - vs[2].tc = vec2(u2, v2); \ - vs[3].tc = vec2(u1, v2); \ - } - if(type&PT_RND4) - { - float tx = 0.5f*((p->flags>>5)&1), ty = 0.5f*((p->flags>>6)&1); - SETTEXCOORDS(tx, tx + 0.5f, ty, ty + 0.5f, - { - if(p->flags&0x01) swap(u1, u2); - if(p->flags&0x02) swap(v1, v2); - }); - } - else if(type&PT_ICON) - { - float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3); - SETTEXCOORDS(tx, tx + 0.25f, ty, ty + 0.25f, {}); - } - else SETTEXCOORDS(0, 1, 0, 1, {}); - - #define SETCOLOR(r, g, b, a) \ - do { \ - bvec4 col(r, g, b, a); \ - loopi(4) vs[i].color = col; \ - } while(0) - #define SETMODCOLOR SETCOLOR((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8, 255) - if(type&PT_MOD) SETMODCOLOR; - else SETCOLOR(p->color.r, p->color.g, p->color.b, blend); - } - else if(type&PT_MOD) SETMODCOLOR; - else loopi(4) vs[i].color.a = blend; - - if(type&PT_ROT) genrotpos<T>(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F); - else genpos<T>(o, d, p->size, ts, p->gravity, vs); - } - - void genverts() - { - loopi(numparts) - { - particle *p = &parts[i]; - partvert *vs = &verts[i*4]; - if(p->fade < 0) - { - do - { - --numparts; - if(numparts <= i) return; - } - while(parts[numparts].fade < 0); - *p = parts[numparts]; - genverts(p, vs, true); - } - else genverts(p, vs, (p->flags&0x80)!=0); - } - } - - void update() - { - if(lastmillis == lastupdate && vbo) return; - lastupdate = lastmillis; - - genverts(); - - if(!vbo) glGenBuffers_(1, &vbo); - gle::bindvbo(vbo); - glBufferData_(GL_ARRAY_BUFFER, maxparts*4*sizeof(partvert), NULL, GL_STREAM_DRAW); - glBufferSubData_(GL_ARRAY_BUFFER, 0, numparts*4*sizeof(partvert), verts); - gle::clearvbo(); - } - - void render() - { - if(!tex) tex = textureload(texname, texclamp); - glBindTexture(GL_TEXTURE_2D, tex->id); - - gle::bindvbo(vbo); - const partvert *ptr = 0; - gle::vertexpointer(sizeof(partvert), ptr->pos.v); - gle::texcoord0pointer(sizeof(partvert), ptr->tc.v); - gle::colorpointer(sizeof(partvert), ptr->color.v); - gle::enablevertex(); - gle::enabletexcoord0(); - gle::enablecolor(); - gle::enablequads(); - - gle::drawquads(0, numparts); - - gle::disablequads(); - gle::disablevertex(); - gle::disabletexcoord0(); - gle::disablecolor(); - gle::clearvbo(); - } + partvert *verts; + particle *parts; + int maxparts, numparts, lastupdate, rndmask; + GLuint vbo; + + varenderer(const char *texname, int type, int collide = 0) + : partrenderer(texname, 3, type, collide), + verts(NULL), parts(NULL), maxparts(0), numparts(0), lastupdate(-1), rndmask(0), vbo(0) + { + if(type & PT_HFLIP) rndmask |= 0x01; + if(type & PT_VFLIP) rndmask |= 0x02; + if(type & PT_ROT) rndmask |= 0x1F<<2; + if(type & PT_RND4) rndmask |= 0x03<<5; + } + + void cleanup() + { + if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; } + } + + void init(int n) + { + DELETEA(parts); + DELETEA(verts); + parts = new particle[n]; + verts = new partvert[n*4]; + maxparts = n; + numparts = 0; + lastupdate = -1; + } + + void reset() + { + numparts = 0; + lastupdate = -1; + } + + void resettracked(physent *owner) + { + if(!(type&PT_TRACK)) return; + loopi(numparts) + { + particle *p = parts+i; + if(!owner || (p->owner == owner)) p->fade = -1; + } + lastupdate = -1; + } + + int count() + { + return numparts; + } + + bool haswork() + { + return (numparts > 0); + } + + particle *addpart(const vec &o, const vec &d, int fade, int color, float size, int gravity) + { + particle *p = parts + (numparts < maxparts ? numparts++ : rnd(maxparts)); //next free slot, or kill a random kitten + p->o = o; + p->d = d; + p->gravity = gravity; + p->fade = fade; + p->millis = lastmillis + emitoffset; + p->color = bvec(color>>16, (color>>8)&0xFF, color&0xFF); + p->size = size; + p->owner = NULL; + p->flags = 0x80 | (rndmask ? rnd(0x80) & rndmask : 0); + lastupdate = -1; + return p; + } + + void seedemitter(particleemitter &pe, const vec &o, const vec &d, int fade, float size, int gravity) + { + pe.maxfade = max(pe.maxfade, fade); + size *= SQRT2; + pe.extendbb(o, size); + + seedpos<T>(pe, o, d, fade, size, gravity); + if(!gravity) return; + + vec end(o); + float t = fade; + end.add(vec(d).mul(t/5000.0f)); + end.z -= t*t/(2.0f * 5000.0f * gravity); + pe.extendbb(end, size); + + float tpeak = d.z*gravity; + if(tpeak > 0 && tpeak < fade) pe.extendbb(o.z + 1.5f*d.z*tpeak/5000.0f, size); + } + + void genverts(particle *p, partvert *vs, bool regen) + { + vec o, d; + int blend, ts; + + calc(p, blend, ts, o, d); + if(blend <= 1 || p->fade <= 5) p->fade = -1; //mark to remove on next pass (i.e. after render) + + modifyblend<T>(o, blend); + + if(regen) + { + p->flags &= ~0x80; + + #define SETTEXCOORDS(u1c, u2c, v1c, v2c, body) \ + { \ + float u1 = u1c, u2 = u2c, v1 = v1c, v2 = v2c; \ + body; \ + vs[0].tc = vec2(u1, v1); \ + vs[1].tc = vec2(u2, v1); \ + vs[2].tc = vec2(u2, v2); \ + vs[3].tc = vec2(u1, v2); \ + } + if(type&PT_RND4) + { + float tx = 0.5f*((p->flags>>5)&1), ty = 0.5f*((p->flags>>6)&1); + SETTEXCOORDS(tx, tx + 0.5f, ty, ty + 0.5f, + { + if(p->flags&0x01) swap(u1, u2); + if(p->flags&0x02) swap(v1, v2); + }); + } + else if(type&PT_ICON) + { + float tx = 0.25f*(p->flags&3), ty = 0.25f*((p->flags>>2)&3); + SETTEXCOORDS(tx, tx + 0.25f, ty, ty + 0.25f, {}); + } + else SETTEXCOORDS(0, 1, 0, 1, {}); + + #define SETCOLOR(r, g, b, a) \ + do { \ + bvec4 col(r, g, b, a); \ + loopi(4) vs[i].color = col; \ + } while(0) + #define SETMODCOLOR SETCOLOR((p->color.r*blend)>>8, (p->color.g*blend)>>8, (p->color.b*blend)>>8, 255) + if(type&PT_MOD) SETMODCOLOR; + else SETCOLOR(p->color.r, p->color.g, p->color.b, blend); + } + else if(type&PT_MOD) SETMODCOLOR; + else loopi(4) vs[i].color.a = blend; + + if(type&PT_ROT) genrotpos<T>(o, d, p->size, ts, p->gravity, vs, (p->flags>>2)&0x1F); + else genpos<T>(o, d, p->size, ts, p->gravity, vs); + } + + void genverts() + { + loopi(numparts) + { + particle *p = &parts[i]; + partvert *vs = &verts[i*4]; + if(p->fade < 0) + { + do + { + --numparts; + if(numparts <= i) return; + } + while(parts[numparts].fade < 0); + *p = parts[numparts]; + genverts(p, vs, true); + } + else genverts(p, vs, (p->flags&0x80)!=0); + } + } + + void update() + { + if(lastmillis == lastupdate && vbo) return; + lastupdate = lastmillis; + + genverts(); + + if(!vbo) glGenBuffers_(1, &vbo); + gle::bindvbo(vbo); + glBufferData_(GL_ARRAY_BUFFER, maxparts*4*sizeof(partvert), NULL, GL_STREAM_DRAW); + glBufferSubData_(GL_ARRAY_BUFFER, 0, numparts*4*sizeof(partvert), verts); + gle::clearvbo(); + } + + void render() + { + if(!tex) tex = textureload(texname, texclamp); + glBindTexture(GL_TEXTURE_2D, tex->id); + + gle::bindvbo(vbo); + const partvert *ptr = 0; + gle::vertexpointer(sizeof(partvert), ptr->pos.v); + gle::texcoord0pointer(sizeof(partvert), ptr->tc.v); + gle::colorpointer(sizeof(partvert), ptr->color.v); + gle::enablevertex(); + gle::enabletexcoord0(); + gle::enablecolor(); + gle::enablequads(); + + gle::drawquads(0, numparts); + + gle::disablequads(); + gle::disablevertex(); + gle::disabletexcoord0(); + gle::disablecolor(); + gle::clearvbo(); + } }; typedef varenderer<PT_PART> quadrenderer; typedef varenderer<PT_TAPE> taperenderer; @@ -847,92 +844,92 @@ typedef varenderer<PT_TRAIL> trailrenderer; struct softquadrenderer : quadrenderer { - softquadrenderer(const char *texname, int type, int collide = 0) - : quadrenderer(texname, type|PT_SOFT, collide) - { - } - - int adddepthfx(vec &bbmin, vec &bbmax) - { - if(!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision()) return 0; - int numsoft = 0; - loopi(numparts) - { - particle &p = parts[i]; - float radius = p.size*SQRT2; - vec o, d; - int blend, ts; - calc(&p, blend, ts, o, d, false); - if(!isfoggedsphere(radius, p.o) && (depthfxscissor!=2 || depthfxtex.addscissorbox(p.o, radius))) - { - numsoft++; - loopk(3) - { - bbmin[k] = min(bbmin[k], o[k] - radius); - bbmax[k] = max(bbmax[k], o[k] + radius); - } - } - } - return numsoft; - } + softquadrenderer(const char *texname, int type, int collide = 0) + : quadrenderer(texname, type|PT_SOFT, collide) + { + } + + int adddepthfx(vec &bbmin, vec &bbmax) + { + if(!depthfxtex.highprecision() && !depthfxtex.emulatehighprecision()) return 0; + int numsoft = 0; + loopi(numparts) + { + particle &p = parts[i]; + float radius = p.size*SQRT2; + vec o, d; + int blend, ts; + calc(&p, blend, ts, o, d, false); + if(!isfoggedsphere(radius, p.o) && (depthfxscissor!=2 || depthfxtex.addscissorbox(p.o, radius))) + { + numsoft++; + loopk(3) + { + bbmin[k] = min(bbmin[k], o[k] - radius); + bbmax[k] = max(bbmax[k], o[k] + radius); + } + } + } + return numsoft; + } }; static partrenderer *parts[] = { - new quadrenderer("<grey>packages/particles/blood.png", PT_PART|PT_FLIP|PT_MOD|PT_RND4, DECAL_BLOOD), // blood spats (note: rgb is inverted) - new trailrenderer("packages/particles/base.png", PT_TRAIL|PT_LERP), // water, entity - new quadrenderer("<grey>packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke - new quadrenderer("<grey>packages/particles/steam.png", PT_PART|PT_FLIP), // steam - new quadrenderer("<grey>packages/particles/flames.png", PT_PART|PT_HFLIP|PT_RND4|PT_GLARE), // flame on - no flipping please, they have orientation - new quadrenderer("packages/particles/ball1.png", PT_PART|PT_FEW|PT_GLARE), // fireball1 - new quadrenderer("packages/particles/ball2.png", PT_PART|PT_FEW|PT_GLARE), // fireball2 - new quadrenderer("packages/particles/ball3.png", PT_PART|PT_FEW|PT_GLARE), // fireball3 - new taperenderer("packages/particles/flare.png", PT_TAPE|PT_GLARE), // streak - &lightnings, // lightning - &fireballs, // explosion fireball - &bluefireballs, // bluish explosion fireball - new quadrenderer("packages/particles/spark.png", PT_PART|PT_FLIP|PT_GLARE), // sparks - new quadrenderer("packages/particles/base.png", PT_PART|PT_FLIP|PT_GLARE), // edit mode entities - new quadrenderer("<grey>packages/particles/snow.png", PT_PART|PT_FLIP|PT_RND4, -1), // colliding snow - new quadrenderer("packages/particles/muzzleflash1.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash - new quadrenderer("packages/particles/muzzleflash2.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash - new quadrenderer("packages/particles/muzzleflash3.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash - new quadrenderer("packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // hud icon - new quadrenderer("<colorify:1/1/1>packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // grey hud icon - &texts, // text - &texticons, // text icons - &meters, // meter - &metervs, // meter vs. + new quadrenderer("<grey>packages/particles/blood.png", PT_PART|PT_FLIP|PT_MOD|PT_RND4, DECAL_BLOOD), // blood spats (note: rgb is inverted) + new trailrenderer("packages/particles/base.png", PT_TRAIL|PT_LERP), // water, entity + new quadrenderer("<grey>packages/particles/smoke.png", PT_PART|PT_FLIP|PT_LERP), // smoke + new quadrenderer("<grey>packages/particles/steam.png", PT_PART|PT_FLIP), // steam + new quadrenderer("<grey>packages/particles/flames.png", PT_PART|PT_HFLIP|PT_RND4|PT_GLARE), // flame on - no flipping please, they have orientation + new quadrenderer("packages/particles/ball1.png", PT_PART|PT_FEW|PT_GLARE), // fireball1 + new quadrenderer("packages/particles/ball2.png", PT_PART|PT_FEW|PT_GLARE), // fireball2 + new quadrenderer("packages/particles/ball3.png", PT_PART|PT_FEW|PT_GLARE), // fireball3 + new taperenderer("packages/particles/flare.png", PT_TAPE|PT_GLARE), // streak + &lightnings, // lightning + &fireballs, // explosion fireball + &bluefireballs, // bluish explosion fireball + new quadrenderer("packages/particles/spark.png", PT_PART|PT_FLIP|PT_GLARE), // sparks + new quadrenderer("packages/particles/base.png", PT_PART|PT_FLIP|PT_GLARE), // edit mode entities + new quadrenderer("<grey>packages/particles/snow.png", PT_PART|PT_FLIP|PT_RND4, -1), // colliding snow + new quadrenderer("packages/particles/muzzleflash1.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash + new quadrenderer("packages/particles/muzzleflash2.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash + new quadrenderer("packages/particles/muzzleflash3.png", PT_PART|PT_FEW|PT_FLIP|PT_GLARE|PT_TRACK), // muzzle flash + new quadrenderer("packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // hud icon + new quadrenderer("<colorify:1/1/1>packages/hud/items.png", PT_PART|PT_FEW|PT_ICON), // grey hud icon + &texts, // text + &texticons, // text icons + &meters, // meter + &metervs, // meter vs. }; void finddepthfxranges() { - depthfxmin = vec(1e16f, 1e16f, 1e16f); - depthfxmax = vec(0, 0, 0); - numdepthfxranges = fireballs.finddepthfxranges(depthfxowners, depthfxranges, 0, MAXDFXRANGES, depthfxmin, depthfxmax); - numdepthfxranges = bluefireballs.finddepthfxranges(depthfxowners, depthfxranges, numdepthfxranges, MAXDFXRANGES, depthfxmin, depthfxmax); - loopk(3) - { - depthfxmin[k] -= depthfxmargin; - depthfxmax[k] += depthfxmargin; - } - if(depthfxparts) - { - loopi(sizeof(parts)/sizeof(parts[0])) - { - partrenderer *p = parts[i]; - if(p->type&PT_SOFT && p->adddepthfx(depthfxmin, depthfxmax)) - { - if(!numdepthfxranges) - { - numdepthfxranges = 1; - depthfxowners[0] = NULL; - depthfxranges[0] = 0; - } - } - } - } - if(depthfxscissor<2 && numdepthfxranges>0) depthfxtex.addscissorbox(depthfxmin, depthfxmax); + depthfxmin = vec(1e16f, 1e16f, 1e16f); + depthfxmax = vec(0, 0, 0); + numdepthfxranges = fireballs.finddepthfxranges(depthfxowners, depthfxranges, 0, MAXDFXRANGES, depthfxmin, depthfxmax); + numdepthfxranges = bluefireballs.finddepthfxranges(depthfxowners, depthfxranges, numdepthfxranges, MAXDFXRANGES, depthfxmin, depthfxmax); + loopk(3) + { + depthfxmin[k] -= depthfxmargin; + depthfxmax[k] += depthfxmargin; + } + if(depthfxparts) + { + loopi(sizeof(parts)/sizeof(parts[0])) + { + partrenderer *p = parts[i]; + if(p->type&PT_SOFT && p->adddepthfx(depthfxmin, depthfxmax)) + { + if(!numdepthfxranges) + { + numdepthfxranges = 1; + depthfxowners[0] = NULL; + depthfxranges[0] = 0; + } + } + } + } + if(depthfxscissor<2 && numdepthfxranges>0) depthfxtex.addscissorbox(depthfxmin, depthfxmax); } VARFP(maxparticles, 10, 4000, 40000, initparticles()); @@ -940,185 +937,185 @@ VARFP(fewparticles, 10, 100, 40000, initparticles()); void initparticles() { - if(!particleshader) particleshader = lookupshaderbyname("particle"); - if(!particlenotextureshader) particlenotextureshader = lookupshaderbyname("particlenotexture"); - loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->init(parts[i]->type&PT_FEW ? min(fewparticles, maxparticles) : maxparticles); + if(!particleshader) particleshader = lookupshaderbyname("particle"); + if(!particlenotextureshader) particlenotextureshader = lookupshaderbyname("particlenotexture"); + loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->init(parts[i]->type&PT_FEW ? min(fewparticles, maxparticles) : maxparticles); } void clearparticles() { - loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->reset(); - clearparticleemitters(); + loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->reset(); + clearparticleemitters(); } void cleanupparticles() { - loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->cleanup(); + loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->cleanup(); } void removetrackedparticles(physent *owner) { - loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->resettracked(owner); + loopi(sizeof(parts)/sizeof(parts[0])) parts[i]->resettracked(owner); } VARP(particleglare, 0, 2, 100); void renderparticles(bool mainpass) { - canstep = mainpass; - - if(glaring && !particleglare) return; - - loopi(sizeof(parts)/sizeof(parts[0])) - { - if(glaring && !(parts[i]->type&PT_GLARE)) continue; - parts[i]->update(); - } - - bool rendered = false; - uint lastflags = PT_LERP|PT_SHADER, - flagmask = PT_LERP|PT_MOD|PT_SHADER|PT_NOTEX; - - if(binddepthfxtex()) flagmask |= PT_SOFT; - - loopi(sizeof(parts)/sizeof(parts[0])) - { - partrenderer *p = parts[i]; - if(glaring && !(p->type&PT_GLARE)) continue; - if(!p->haswork()) continue; - - if(!rendered) - { - rendered = true; - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - if(glaring) GLOBALPARAMF(colorscale, particleglare, particleglare, particleglare, 1); - else GLOBALPARAMF(colorscale, 1, 1, 1, 1); - } - - uint flags = p->type & flagmask, changedbits = (flags ^ lastflags); - if(changedbits) - { - if(changedbits&PT_LERP) - { - if(flags&PT_LERP) resetfogcolor(); - else zerofogcolor(); - } - if(changedbits&(PT_LERP|PT_MOD)) - { - if(flags&PT_LERP) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - else if(flags&PT_MOD) glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - else glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } - if(!(flags&PT_SHADER)) - { - if(changedbits&(PT_SOFT|PT_SHADER|PT_NOTEX|PT_LERP)) - { - if(flags&PT_SOFT) - { - if(!depthfxtex.highprecision()) SETSHADER(particlesoft8); - else SETSHADER(particlesoft); - - binddepthfxparams(depthfxpartblend); - } - else if(flags&PT_NOTEX) particlenotextureshader->set(); - else particleshader->set(); - } - } - lastflags = flags; - } - p->render(); - } - - if(rendered) - { - if(lastflags&(PT_LERP|PT_MOD)) glBlendFunc(GL_SRC_ALPHA, GL_ONE); - if(!(lastflags&PT_LERP)) resetfogcolor(); - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - } + canstep = mainpass; + + if(glaring && !particleglare) return; + + loopi(sizeof(parts)/sizeof(parts[0])) + { + if(glaring && !(parts[i]->type&PT_GLARE)) continue; + parts[i]->update(); + } + + bool rendered = false; + uint lastflags = PT_LERP|PT_SHADER, + flagmask = PT_LERP|PT_MOD|PT_SHADER|PT_NOTEX; + + if(binddepthfxtex()) flagmask |= PT_SOFT; + + loopi(sizeof(parts)/sizeof(parts[0])) + { + partrenderer *p = parts[i]; + if(glaring && !(p->type&PT_GLARE)) continue; + if(!p->haswork()) continue; + + if(!rendered) + { + rendered = true; + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if(glaring) GLOBALPARAMF(colorscale, particleglare, particleglare, particleglare, 1); + else GLOBALPARAMF(colorscale, 1, 1, 1, 1); + } + + uint flags = p->type & flagmask, changedbits = (flags ^ lastflags); + if(changedbits) + { + if(changedbits&PT_LERP) + { + if(flags&PT_LERP) resetfogcolor(); + else zerofogcolor(); + } + if(changedbits&(PT_LERP|PT_MOD)) + { + if(flags&PT_LERP) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else if(flags&PT_MOD) glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + if(!(flags&PT_SHADER)) + { + if(changedbits&(PT_SOFT|PT_SHADER|PT_NOTEX|PT_LERP)) + { + if(flags&PT_SOFT) + { + if(!depthfxtex.highprecision()) SETSHADER(particlesoft8); + else SETSHADER(particlesoft); + + binddepthfxparams(depthfxpartblend); + } + else if(flags&PT_NOTEX) particlenotextureshader->set(); + else particleshader->set(); + } + } + lastflags = flags; + } + p->render(); + } + + if(rendered) + { + if(lastflags&(PT_LERP|PT_MOD)) glBlendFunc(GL_SRC_ALPHA, GL_ONE); + if(!(lastflags&PT_LERP)) resetfogcolor(); + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + } } static int addedparticles = 0; static inline particle *newparticle(const vec &o, const vec &d, int fade, int type, int color, float size, int gravity = 0) { - static particle dummy; - if(seedemitter) - { - parts[type]->seedemitter(*seedemitter, o, d, fade, size, gravity); - return &dummy; - } - if(fade + emitoffset < 0) return &dummy; - addedparticles++; - return parts[type]->addpart(o, d, fade, color, size, gravity); + static particle dummy; + if(seedemitter) + { + parts[type]->seedemitter(*seedemitter, o, d, fade, size, gravity); + return &dummy; + } + if(fade + emitoffset < 0) return &dummy; + addedparticles++; + return parts[type]->addpart(o, d, fade, color, size, gravity); } VARP(maxparticledistance, 256, 1024, 4096); static void splash(int type, int color, int radius, int num, int fade, const vec &p, float size, int gravity) { - if(camera1->o.dist(p) > maxparticledistance && !seedemitter) return; - float collidez = parts[type]->collide ? p.z - raycube(p, vec(0, 0, -1), COLLIDERADIUS, RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0) : -1; - int fmin = 1; - int fmax = fade*3; - loopi(num) - { - 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 tmp = vec((float)x, (float)y, (float)z); - int f = (num < 10) ? (fmin + rnd(fmax)) : (fmax - (i*(fmax-fmin))/(num-1)); //help deallocater by using fade distribution rather than random - newparticle(p, tmp, f, type, color, size, gravity)->val = collidez; - } + if(camera1->o.dist(p) > maxparticledistance && !seedemitter) return; + float collidez = parts[type]->collide ? p.z - raycube(p, vec(0, 0, -1), COLLIDERADIUS, RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0) : -1; + int fmin = 1; + int fmax = fade*3; + loopi(num) + { + 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 tmp = vec((float)x, (float)y, (float)z); + int f = (num < 10) ? (fmin + rnd(fmax)) : (fmax - (i*(fmax-fmin))/(num-1)); //help deallocater by using fade distribution rather than random + newparticle(p, tmp, f, type, color, size, gravity)->val = collidez; + } } static void regularsplash(int type, int color, int radius, int num, int fade, const vec &p, float size, int gravity, int delay = 0) { - if(!canemitparticles() || (delay > 0 && rnd(delay) != 0)) return; - splash(type, color, radius, num, fade, p, size, gravity); + if(!canemitparticles() || (delay > 0 && rnd(delay) != 0)) return; + splash(type, color, radius, num, fade, p, size, gravity); } bool canaddparticles() { - return !renderedgame && !shadowmapping && !minimized; + return !renderedgame && !shadowmapping && !minimized; } void regular_particle_splash(int type, int num, int fade, const vec &p, int color, float size, int radius, int gravity, int delay) { - if(!canaddparticles()) return; - regularsplash(type, color, radius, num, fade, p, size, gravity, delay); + if(!canaddparticles()) return; + regularsplash(type, color, radius, num, fade, p, size, gravity, delay); } void particle_splash(int type, int num, int fade, const vec &p, int color, float size, int radius, int gravity) { - if(!canaddparticles()) return; - splash(type, color, radius, num, fade, p, size, gravity); + if(!canaddparticles()) return; + splash(type, color, radius, num, fade, p, size, gravity); } VARP(maxtrail, 1, 500, 10000); void particle_trail(int type, int fade, const vec &s, const vec &e, int color, float size, int gravity) { - if(!canaddparticles()) return; - 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 = vec(float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5)); - newparticle(p, tmp, rnd(fade)+fade, type, color, size, gravity); - } + if(!canaddparticles()) return; + 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 = vec(float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5)); + newparticle(p, tmp, rnd(fade)+fade, type, color, size, gravity); + } } VARP(particletext, 0, 1, 1); @@ -1126,74 +1123,74 @@ VARP(maxparticletextdistance, 0, 128, 10000); void particle_text(const vec &s, const char *t, int type, int fade, int color, float size, int gravity, int icons) { - if(!canaddparticles()) return; - if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; - particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); - p->text = t; - p->flags = icons<<1; + if(!canaddparticles()) return; + if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); + p->text = t; + p->flags = icons<<1; } void particle_textcopy(const vec &s, const char *t, int type, int fade, int color, float size, int gravity) { - if(!canaddparticles()) return; - if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; - particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); - p->text = newstring(t); - p->flags = 1; + if(!canaddparticles()) return; + if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); + p->text = newstring(t); + p->flags = 1; } void particle_texticon(const vec &s, int ix, int iy, float offset, int type, int fade, int color, float size, int gravity) { - if(!canaddparticles()) return; - if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; - particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); - p->flags |= ix | (iy<<2); - p->val = offset; + if(!canaddparticles()) return; + if(!particletext || camera1->o.dist(s) > maxparticletextdistance) return; + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); + p->flags |= ix | (iy<<2); + p->val = offset; } void particle_icon(const vec &s, int ix, int iy, int type, int fade, int color, float size, int gravity) { - if(!canaddparticles()) return; - particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); - p->flags |= ix | (iy<<2); + if(!canaddparticles()) return; + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size, gravity); + p->flags |= ix | (iy<<2); } void particle_meter(const vec &s, float val, int type, int fade, int color, int color2, float size) { - if(!canaddparticles()) return; - particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size); - p->color2[0] = color2>>16; - p->color2[1] = (color2>>8)&0xFF; - p->color2[2] = color2&0xFF; - p->progress = clamp(int(val*100), 0, 100); + if(!canaddparticles()) return; + particle *p = newparticle(s, vec(0, 0, 1), fade, type, color, size); + p->color2[0] = color2>>16; + p->color2[1] = (color2>>8)&0xFF; + p->color2[2] = color2&0xFF; + p->progress = clamp(int(val*100), 0, 100); } void particle_flare(const vec &p, const vec &dest, int fade, int type, int color, float size, physent *owner) { - if(!canaddparticles()) return; - newparticle(p, dest, fade, type, color, size)->owner = owner; + if(!canaddparticles()) return; + newparticle(p, dest, fade, type, color, size)->owner = owner; } void particle_fireball(const vec &dest, float maxsize, int type, int fade, int color, float size) { - if(!canaddparticles()) return; - float growth = maxsize - size; - if(fade < 0) fade = int(growth*20); - newparticle(dest, vec(0, 0, 1), fade, type, color, size)->val = growth; + if(!canaddparticles()) return; + float growth = maxsize - size; + if(fade < 0) fade = int(growth*20); + newparticle(dest, vec(0, 0, 1), fade, type, color, size)->val = growth; } //dir = 0..6 where 0=up static inline vec offsetvec(vec o, int dir, int dist) { - vec v = vec(o); - v[(2+dir)%3] += (dir>2)?(-dist):dist; - return v; + vec v = vec(o); + v[(2+dir)%3] += (dir>2)?(-dist):dist; + return v; } //converts a 16bit color to 24bit static inline int colorfromattr(int attr) { - return (((attr&0xF)<<4) | ((attr&0xF0)<<8) | ((attr&0xF00)<<12)) + 0x0F0F0F; + return (((attr&0xF)<<4) | ((attr&0xF0)<<8) | ((attr&0xF00)<<12)) + 0x0F0F0F; } /* Experiments in shapes... @@ -1209,300 +1206,299 @@ static inline int colorfromattr(int attr) */ void regularshape(int type, int radius, int color, int dir, int num, int fade, const vec &p, float size, int gravity, int vel = 200) { - if(!canemitparticles()) return; - - int basetype = parts[type]->type&0xFF; - bool flare = (basetype == PT_TAPE) || (basetype == PT_LIGHTNING), - inv = (dir&0x20)!=0, taper = (dir&0x40)!=0 && !seedemitter; - dir &= 0x1F; - loopi(num) - { - vec to, from; - if(dir < 12) - { - const vec2 &sc = sincos360[rnd(360)]; - to[dir%3] = sc.y*radius; - to[(dir+1)%3] = sc.x*radius; - to[(dir+2)%3] = 0.0; - to.add(p); - if(dir < 3) //circle - from = p; - else if(dir < 6) //cylinder - { - from = to; - to[(dir+2)%3] += radius; - from[(dir+2)%3] -= radius; - } - else //cone - { - from = p; - to[(dir+2)%3] += (dir < 9)?radius:(-radius); - } - } - else if(dir < 15) //plane - { - to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0; - to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0; - to[(dir+2)%3] = radius; - to.add(p); - from = to; - from[(dir+2)%3] -= 2*radius; - } - else if(dir < 21) //line - { - if(dir < 18) - { - to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0; - to[(dir+1)%3] = 0.0; - } - else - { - to[dir%3] = 0.0; - to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0; - } - to[(dir+2)%3] = 0.0; - to.add(p); - from = to; - to[(dir+2)%3] += radius; - } - else if(dir < 24) //sphere - { - to = vec(2*M_PI*float(rnd(1000))/1000.0, M_PI*float(rnd(1000)-500)/1000.0).mul(radius); - to.add(p); - from = p; - } - else if(dir < 27) // flat plane - { - to[dir%3] = float(rndscale(2*radius)-radius); - to[(dir+1)%3] = float(rndscale(2*radius)-radius); - to[(dir+2)%3] = 0.0; - to.add(p); - from = to; - } - else from = to = p; - - if(inv) swap(from, to); - - if(taper) - { - float dist = clamp(from.dist2(camera1->o)/maxparticledistance, 0.0f, 1.0f); - if(dist > 0.2f) - { - dist = 1 - (dist - 0.2f)/0.8f; - if(rnd(0x10000) > dist*dist*0xFFFF) continue; - } - } - - if(flare) - newparticle(from, to, rnd(fade*3)+1, type, color, size, gravity); - else - { - vec d = vec(to).sub(from).rescale(vel); //velocity - particle *n = newparticle(from, d, rnd(fade*3)+1, type, color, size, gravity); - if(parts[type]->collide) - n->val = from.z - raycube(from, vec(0, 0, -1), parts[type]->collide >= 0 ? COLLIDERADIUS : max(from.z, 0.0f), RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0); - } - } + if(!canemitparticles()) return; + + int basetype = parts[type]->type&0xFF; + bool flare = (basetype == PT_TAPE) || (basetype == PT_LIGHTNING), + inv = (dir&0x20)!=0, taper = (dir&0x40)!=0 && !seedemitter; + dir &= 0x1F; + loopi(num) + { + vec to, from; + if(dir < 12) + { + const vec2 &sc = sincos360[rnd(360)]; + to[dir%3] = sc.y*radius; + to[(dir+1)%3] = sc.x*radius; + to[(dir+2)%3] = 0.0; + to.add(p); + if(dir < 3) //circle + from = p; + else if(dir < 6) //cylinder + { + from = to; + to[(dir+2)%3] += radius; + from[(dir+2)%3] -= radius; + } + else //cone + { + from = p; + to[(dir+2)%3] += (dir < 9)?radius:(-radius); + } + } + else if(dir < 15) //plane + { + to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0; + to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0; + to[(dir+2)%3] = radius; + to.add(p); + from = to; + from[(dir+2)%3] -= 2*radius; + } + else if(dir < 21) //line + { + if(dir < 18) + { + to[dir%3] = float(rnd(radius<<4)-(radius<<3))/8.0; + to[(dir+1)%3] = 0.0; + } + else + { + to[dir%3] = 0.0; + to[(dir+1)%3] = float(rnd(radius<<4)-(radius<<3))/8.0; + } + to[(dir+2)%3] = 0.0; + to.add(p); + from = to; + to[(dir+2)%3] += radius; + } + else if(dir < 24) //sphere + { + to = vec(2*M_PI*float(rnd(1000))/1000.0, M_PI*float(rnd(1000)-500)/1000.0).mul(radius); + to.add(p); + from = p; + } + else if(dir < 27) // flat plane + { + to[dir%3] = float(rndscale(2*radius)-radius); + to[(dir+1)%3] = float(rndscale(2*radius)-radius); + to[(dir+2)%3] = 0.0; + to.add(p); + from = to; + } + else from = to = p; + + if(inv) swap(from, to); + + if(taper) + { + float dist = clamp(from.dist2(camera1->o)/maxparticledistance, 0.0f, 1.0f); + if(dist > 0.2f) + { + dist = 1 - (dist - 0.2f)/0.8f; + if(rnd(0x10000) > dist*dist*0xFFFF) continue; + } + } + + if(flare) + newparticle(from, to, rnd(fade*3)+1, type, color, size, gravity); + else + { + vec d = vec(to).sub(from).rescale(vel); //velocity + particle *n = newparticle(from, d, rnd(fade*3)+1, type, color, size, gravity); + if(parts[type]->collide) + n->val = from.z - raycube(from, vec(0, 0, -1), parts[type]->collide >= 0 ? COLLIDERADIUS : max(from.z, 0.0f), RAY_CLIPMAT) + (parts[type]->collide >= 0 ? COLLIDEERROR : 0); + } + } } static void regularflame(int type, const vec &p, float radius, float height, int color, int density = 3, float scale = 2.0f, float speed = 200.0f, float fade = 600.0f, int gravity = -15) { - if(!canemitparticles()) return; - - float size = scale * min(radius, height); - vec v(0, 0, min(1.0f, height)*speed); - loopi(density) - { - vec s = p; - s.x += rndscale(radius*2.0f)-radius; - s.y += rndscale(radius*2.0f)-radius; - newparticle(s, v, rnd(max(int(fade*height), 1))+1, type, color, size, gravity); - } + if(!canemitparticles()) return; + + float size = scale * min(radius, height); + vec v(0, 0, min(1.0f, height)*speed); + loopi(density) + { + vec s = p; + s.x += rndscale(radius*2.0f)-radius; + s.y += rndscale(radius*2.0f)-radius; + newparticle(s, v, rnd(max(int(fade*height), 1))+1, type, color, size, gravity); + } } void regular_particle_flame(int type, const vec &p, float radius, float height, int color, int density, float scale, float speed, float fade, int gravity) { - if(!canaddparticles()) return; - regularflame(type, p, radius, height, color, density, scale, speed, fade, gravity); + if(!canaddparticles()) return; + regularflame(type, p, radius, height, color, density, scale, speed, fade, gravity); } static void makeparticles(entity &e) { - switch(e.attr1) - { - case 0: //fire and smoke - <radius> <height> <rgb> - 0 values default to compat for old maps - { - //regularsplash(PART_FIREBALL1, 0xFFC8C8, 150, 1, 40, e.o, 4.8f); - //regularsplash(PART_SMOKE, 0x897661, 50, 1, 200, vec(e.o.x, e.o.y, e.o.z+3.0f), 2.4f, -20, 3); - float radius = e.attr2 ? float(e.attr2)/100.0f : 1.5f, - height = e.attr3 ? float(e.attr3)/100.0f : radius/3; - regularflame(PART_FLAME, e.o, radius, height, e.attr4 ? colorfromattr(e.attr4) : 0x903020, 3, 2.0f); - regularflame(PART_SMOKE, vec(e.o.x, e.o.y, e.o.z + 4.0f*min(radius, height)), radius, height, 0x303020, 1, 4.0f, 100.0f, 2000.0f, -20); - break; - } - case 1: //steam vent - <dir> - regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20); - break; - case 2: //water fountain - <dir> - { - int color; - if(e.attr3 > 0) color = colorfromattr(e.attr3); - else - { - int mat = MAT_WATER + clamp(-e.attr3, 0, 3); - const bvec &wfcol = getwaterfallcolor(mat); - color = (int(wfcol[0])<<16) | (int(wfcol[1])<<8) | int(wfcol[2]); - if(!color) - { - const bvec &wcol = getwatercolor(mat); - color = (int(wcol[0])<<16) | (int(wcol[1])<<8) | int(wcol[2]); - } - } - regularsplash(PART_WATER, color, 150, 4, 200, offsetvec(e.o, e.attr2, rnd(10)), 0.6f, 2); - break; - } - case 3: //fire ball - <size> <rgb> - newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2; - break; - case 4: //tape - <dir> <length> <rgb> - case 7: //lightning - case 9: //steam - case 10: //water - case 13: //snow - { - static const int typemap[] = { PART_STREAK, -1, -1, PART_LIGHTNING, -1, PART_STEAM, PART_WATER, -1, -1, PART_SNOW }; - static const float sizemap[] = { 0.28f, 0.0f, 0.0f, 1.0f, 0.0f, 2.4f, 0.60f, 0.0f, 0.0f, 0.5f }; - static const int gravmap[] = { 0, 0, 0, 0, 0, -20, 2, 0, 0, 20 }; - int type = typemap[e.attr1-4]; - float size = sizemap[e.attr1-4]; - int gravity = gravmap[e.attr1-4]; - if(e.attr2 >= 256) regularshape(type, max(1+e.attr3, 1), colorfromattr(e.attr4), e.attr2-256, 5, e.attr5 > 0 ? min(int(e.attr5), 10000) : 200, e.o, size, gravity); - else newparticle(e.o, offsetvec(e.o, e.attr2, max(1+e.attr3, 0)), 1, type, colorfromattr(e.attr4), size, gravity); - break; - } - case 5: //meter, metervs - <percent> <rgb> <rgb2> - case 6: - { - particle *p = newparticle(e.o, vec(0, 0, 1), 1, e.attr1==5 ? PART_METER : PART_METER_VS, colorfromattr(e.attr3), 2.0f); - int color2 = colorfromattr(e.attr4); - p->color2[0] = color2>>16; - p->color2[1] = (color2>>8)&0xFF; - p->color2[2] = color2&0xFF; - p->progress = clamp(int(e.attr2), 0, 100); - break; - } - case 11: // flame <radius> <height> <rgb> - radius=100, height=100 is the classic size - regularflame(PART_FLAME, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 3, 2.0f); - break; - case 12: // smoke plume <radius> <height> <rgb> - regularflame(PART_SMOKE, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 1, 4.0f, 100.0f, 2000.0f, -20); - break; - case 32: //lens flares - plain/sparkle/sun/sparklesun <red> <green> <blue> - case 33: - case 34: - case 35: - break; - default: - if(!editmode) - { - defformatstring(ds, "particles %d?", e.attr1); - particle_textcopy(e.o, ds, PART_TEXT, 1, 0x6496FF, 2.0f); - } - break; - } + switch(e.attr1) + { + case 0: //fire and smoke - <radius> <height> <rgb> - 0 values default to compat for old maps + { + //regularsplash(PART_FIREBALL1, 0xFFC8C8, 150, 1, 40, e.o, 4.8f); + //regularsplash(PART_SMOKE, 0x897661, 50, 1, 200, vec(e.o.x, e.o.y, e.o.z+3.0f), 2.4f, -20, 3); + float radius = e.attr2 ? float(e.attr2)/100.0f : 1.5f, + height = e.attr3 ? float(e.attr3)/100.0f : radius/3; + regularflame(PART_FLAME, e.o, radius, height, e.attr4 ? colorfromattr(e.attr4) : 0x903020, 3, 2.0f); + regularflame(PART_SMOKE, vec(e.o.x, e.o.y, e.o.z + 4.0f*min(radius, height)), radius, height, 0x303020, 1, 4.0f, 100.0f, 2000.0f, -20); + break; + } + case 1: //steam vent - <dir> + regularsplash(PART_STEAM, 0x897661, 50, 1, 200, offsetvec(e.o, e.attr2, rnd(10)), 2.4f, -20); + break; + case 2: //water fountain - <dir> + { + int color; + if(e.attr3 > 0) color = colorfromattr(e.attr3); + else + { + int mat = MAT_WATER + clamp(-e.attr3, 0, 3); + const bvec &wfcol = getwaterfallcolor(mat); + color = (int(wfcol[0])<<16) | (int(wfcol[1])<<8) | int(wfcol[2]); + if(!color) + { + const bvec &wcol = getwatercolor(mat); + color = (int(wcol[0])<<16) | (int(wcol[1])<<8) | int(wcol[2]); + } + } + regularsplash(PART_WATER, color, 150, 4, 200, offsetvec(e.o, e.attr2, rnd(10)), 0.6f, 2); + break; + } + case 3: //fire ball - <size> <rgb> + newparticle(e.o, vec(0, 0, 1), 1, PART_EXPLOSION, colorfromattr(e.attr3), 4.0f)->val = 1+e.attr2; + break; + case 4: //tape - <dir> <length> <rgb> + case 7: //lightning + case 9: //steam + case 10: //water + case 13: //snow + { + static const int typemap[] = { PART_STREAK, -1, -1, PART_LIGHTNING, -1, PART_STEAM, PART_WATER, -1, -1, PART_SNOW }; + static const float sizemap[] = { 0.28f, 0.0f, 0.0f, 1.0f, 0.0f, 2.4f, 0.60f, 0.0f, 0.0f, 0.5f }; + static const int gravmap[] = { 0, 0, 0, 0, 0, -20, 2, 0, 0, 20 }; + int type = typemap[e.attr1-4]; + float size = sizemap[e.attr1-4]; + int gravity = gravmap[e.attr1-4]; + if(e.attr2 >= 256) regularshape(type, max(1+e.attr3, 1), colorfromattr(e.attr4), e.attr2-256, 5, e.attr5 > 0 ? min(int(e.attr5), 10000) : 200, e.o, size, gravity); + else newparticle(e.o, offsetvec(e.o, e.attr2, max(1+e.attr3, 0)), 1, type, colorfromattr(e.attr4), size, gravity); + break; + } + case 5: //meter, metervs - <percent> <rgb> <rgb2> + case 6: + { + particle *p = newparticle(e.o, vec(0, 0, 1), 1, e.attr1==5 ? PART_METER : PART_METER_VS, colorfromattr(e.attr3), 2.0f); + int color2 = colorfromattr(e.attr4); + p->color2[0] = color2>>16; + p->color2[1] = (color2>>8)&0xFF; + p->color2[2] = color2&0xFF; + p->progress = clamp(int(e.attr2), 0, 100); + break; + } + case 11: // flame <radius> <height> <rgb> - radius=100, height=100 is the classic size + regularflame(PART_FLAME, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 3, 2.0f); + break; + case 12: // smoke plume <radius> <height> <rgb> + regularflame(PART_SMOKE, e.o, float(e.attr2)/100.0f, float(e.attr3)/100.0f, colorfromattr(e.attr4), 1, 4.0f, 100.0f, 2000.0f, -20); + break; + case 32: //lens flares - plain/sparkle/sun/sparklesun <red> <green> <blue> + case 33: + case 34: + case 35: + break; + default: + if(!editmode) + { + defformatstring(ds, "particles %d?", e.attr1); + particle_textcopy(e.o, ds, PART_TEXT, 1, 0x6496FF, 2.0f); + } + break; + } } bool printparticles(extentity &e, char *buf, int len) { - switch(e.attr1) - { - case 0: case 4: case 7: case 8: case 9: case 10: case 11: case 12: case 13: - nformatstring(buf, len, "%s %d %d %d 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); - return true; - case 3: - nformatstring(buf, len, "%s %d %d 0x%.3hX %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); - return true; - case 5: case 6: - nformatstring(buf, len, "%s %d %d 0x%.3hX 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); - return true; - } - return false; + switch(e.attr1) + { + case 0: case 4: case 7: case 8: case 9: case 10: case 11: case 12: case 13: + nformatstring(buf, len, "%s %d %d %d 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); + return true; + case 3: + nformatstring(buf, len, "%s %d %d 0x%.3hX %d %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); + return true; + case 5: case 6: + nformatstring(buf, len, "%s %d %d 0x%.3hX 0x%.3hX %d", entities::entname(e.type), e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); + return true; + } + return false; } void seedparticles() { - renderprogress(0, "seeding particles"); - addparticleemitters(); - canemit = true; - loopv(emitters) - { - particleemitter &pe = emitters[i]; - extentity &e = *pe.ent; - seedemitter = &pe; - for(int millis = 0; millis < seedmillis; millis += min(emitmillis, seedmillis/10)) - makeparticles(e); - seedemitter = NULL; - pe.lastemit = -seedmillis; - pe.finalize(); - } + renderprogress(0, "seeding particles"); + addparticleemitters(); + canemit = true; + loopv(emitters) + { + particleemitter &pe = emitters[i]; + extentity &e = *pe.ent; + seedemitter = &pe; + for(int millis = 0; millis < seedmillis; millis += min(emitmillis, seedmillis/10)) + makeparticles(e); + seedemitter = NULL; + pe.lastemit = -seedmillis; + pe.finalize(); + } } void updateparticles() { - if(regenemitters) addparticleemitters(); - - if(minimized) { canemit = false; return; } - - if(lastmillis - lastemitframe >= emitmillis) - { - canemit = true; - lastemitframe = lastmillis - (lastmillis%emitmillis); - } - else canemit = false; - - if(!editmode || showparticles) - { - int emitted = 0, replayed = 0; - addedparticles = 0; - loopv(emitters) - { - particleemitter &pe = emitters[i]; - extentity &e = *pe.ent; - if(e.o.dist(camera1->o) > maxparticledistance) { pe.lastemit = lastmillis; continue; } - if(cullparticles && pe.maxfade >= 0) - { - if(isfoggedsphere(pe.radius, pe.center)) { pe.lastcull = lastmillis; continue; } - } - makeparticles(e); - emitted++; - if(replayparticles && pe.maxfade > 5 && pe.lastcull > pe.lastemit) - { - for(emitoffset = max(pe.lastemit + emitmillis - lastmillis, -pe.maxfade); emitoffset < 0; emitoffset += emitmillis) - { - makeparticles(e); - replayed++; - } - emitoffset = 0; - } - pe.lastemit = lastmillis; - } - if(dbgpcull && (canemit || replayed) && addedparticles) conoutf(CON_DEBUG, "%d emitters, %d particles", emitted, addedparticles); - } - if(editmode) // show sparkly thingies for map entities in edit mode - { - const vector<extentity *> &ents = entities::getents(); - // note: order matters in this case as particles of the same type are drawn in the reverse order that they are added - loopv(entgroup) - { - entity &e = *ents[entgroup[i]]; - particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0xFF4B19, 2.0f); - } - loopv(ents) - { - entity &e = *ents[i]; - if(e.type==ET_EMPTY) continue; - particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0x1EC850, 2.0f); - regular_particle_splash(PART_EDIT, 2, 40, e.o, 0x3232FF, 0.32f*particlesize/100.0f); - } - } + if(regenemitters) addparticleemitters(); + + if(minimized) { canemit = false; return; } + + if(lastmillis - lastemitframe >= emitmillis) + { + canemit = true; + lastemitframe = lastmillis - (lastmillis%emitmillis); + } + else canemit = false; + + if(!editmode || showparticles) + { + int emitted = 0, replayed = 0; + addedparticles = 0; + loopv(emitters) + { + particleemitter &pe = emitters[i]; + extentity &e = *pe.ent; + if(e.o.dist(camera1->o) > maxparticledistance) { pe.lastemit = lastmillis; continue; } + if(cullparticles && pe.maxfade >= 0) + { + if(isfoggedsphere(pe.radius, pe.center)) { pe.lastcull = lastmillis; continue; } + } + makeparticles(e); + emitted++; + if(replayparticles && pe.maxfade > 5 && pe.lastcull > pe.lastemit) + { + for(emitoffset = max(pe.lastemit + emitmillis - lastmillis, -pe.maxfade); emitoffset < 0; emitoffset += emitmillis) + { + makeparticles(e); + replayed++; + } + emitoffset = 0; + } + pe.lastemit = lastmillis; + } + } + if(editmode) // show sparkly thingies for map entities in edit mode + { + const vector<extentity *> &ents = entities::getents(); + // note: order matters in this case as particles of the same type are drawn in the reverse order that they are added + loopv(entgroup) + { + entity &e = *ents[entgroup[i]]; + particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0xFF4B19, 2.0f); + } + loopv(ents) + { + entity &e = *ents[i]; + if(e.type==ET_EMPTY) continue; + particle_textcopy(e.o, entname(e), PART_TEXT, 1, 0x1EC850, 2.0f); + regular_particle_splash(PART_EDIT, 2, 40, e.o, 0x3232FF, 0.32f*particlesize/100.0f); + } + } } |
