1 #include "engine.h"
2
3 struct stainvert
4 {
5 vec pos;
6 bvec4 color;
7 vec2 tc;
8 };
9
10 struct staininfo
11 {
12 int millis;
13 bvec color;
14 uchar owner;
15 ushort startvert, endvert;
16 };
17
18 enum
19 {
20 SF_RND4 = 1<<0,
21 SF_ROTATE = 1<<1,
22 SF_INVMOD = 1<<2,
23 SF_OVERBRIGHT = 1<<3,
24 SF_GLOW = 1<<4,
25 SF_SATURATE = 1<<5
26 };
27
28 VARF(IDF_PERSIST, maxstaintris, 1, 2048, 16384, initstains());
29 VAR(IDF_PERSIST, stainfade, 1000, 15000, 60000);
30 VAR(0, dbgstain, 0, 0, 1);
31
32 struct stainbuffer
33 {
34 stainvert *verts;
35 int maxverts, startvert, endvert, lastvert, availverts;
36 GLuint vbo;
37 bool dirty;
38
stainbufferstainbuffer39 stainbuffer() : verts(NULL), maxverts(0), startvert(0), endvert(0), lastvert(0), availverts(0), vbo(0), dirty(false)
40 {}
41
~stainbufferstainbuffer42 ~stainbuffer()
43 {
44 DELETEA(verts);
45 }
46
initstainbuffer47 void init(int tris)
48 {
49 if(verts)
50 {
51 DELETEA(verts);
52 maxverts = startvert = endvert = lastvert = availverts = 0;
53 }
54 if(tris)
55 {
56 maxverts = tris*3 + 3;
57 availverts = maxverts - 3;
58 verts = new stainvert[maxverts];
59 }
60 }
61
cleanupstainbuffer62 void cleanup()
63 {
64 if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
65 }
66
clearstainbuffer67 void clear()
68 {
69 startvert = endvert = lastvert = 0;
70 availverts = max(maxverts - 3, 0);
71 dirty = true;
72 }
73
freestainstainbuffer74 int freestain(const staininfo &d)
75 {
76 int removed = d.endvert < d.startvert ? maxverts - (d.startvert - d.endvert) : d.endvert - d.startvert;
77 startvert = d.endvert;
78 if(startvert==endvert) startvert = endvert = lastvert = 0;
79 availverts += removed;
80 return removed;
81 }
82
clearstainsstainbuffer83 void clearstains(const staininfo &d)
84 {
85 startvert = d.endvert;
86 availverts = endvert < startvert ? startvert - endvert - 3 : maxverts - 3 - (endvert - startvert);
87 dirty = true;
88 }
89
fadedstainbuffer90 bool faded(const staininfo &d) const { return verts[d.startvert].color.a < 255; }
91
fadestainstainbuffer92 void fadestain(const staininfo &d, const bvec4 &color)
93 {
94 stainvert *vert = &verts[d.startvert],
95 *end = &verts[d.endvert < d.startvert ? maxverts : d.endvert];
96 while(vert < end)
97 {
98 vert->color = color;
99 vert++;
100 }
101 if(d.endvert < d.startvert)
102 {
103 vert = verts;
104 end = &verts[d.endvert];
105 while(vert < end)
106 {
107 vert->color = color;
108 vert++;
109 }
110 }
111 dirty = true;
112 }
113
renderstainbuffer114 void render()
115 {
116 if(startvert == endvert) return;
117
118 if(!vbo) { glGenBuffers_(1, &vbo); dirty = true; }
119 gle::bindvbo(vbo);
120
121 int count = endvert < startvert ? maxverts - startvert : endvert - startvert;
122 if(dirty)
123 {
124 glBufferData_(GL_ARRAY_BUFFER, maxverts*sizeof(stainvert), NULL, GL_STREAM_DRAW);
125 glBufferSubData_(GL_ARRAY_BUFFER, 0, count*sizeof(stainvert), &verts[startvert]);
126 if(endvert < startvert)
127 {
128 glBufferSubData_(GL_ARRAY_BUFFER, count*sizeof(stainvert), endvert*sizeof(stainvert), verts);
129 count += endvert;
130 }
131 dirty = false;
132 }
133 else if(endvert < startvert) count += endvert;
134
135 const stainvert *ptr = 0;
136 gle::vertexpointer(sizeof(stainvert), ptr->pos.v);
137 gle::texcoord0pointer(sizeof(stainvert), ptr->tc.v);
138 gle::colorpointer(sizeof(stainvert), ptr->color.v);
139
140 glDrawArrays(GL_TRIANGLES, 0, count);
141 xtravertsva += count;
142 }
143
addtristainbuffer144 stainvert *addtri()
145 {
146 stainvert *tri = &verts[endvert];
147 availverts -= 3;
148 endvert += 3;
149 if(endvert >= maxverts) endvert = 0;
150 return tri;
151 }
152
addstainstainbuffer153 void addstain(staininfo &d)
154 {
155 dirty = true;
156 }
157
hasvertsstainbuffer158 bool hasverts() const { return startvert != endvert; }
159
nextvertsstainbuffer160 int nextverts() const
161 {
162 return endvert < lastvert ? endvert + maxverts - lastvert : endvert - lastvert;
163 }
164
totalvertsstainbuffer165 int totalverts() const
166 {
167 return endvert < startvert ? maxverts - (startvert - endvert) : endvert - startvert;
168 }
169
totaltrisstainbuffer170 int totaltris() const
171 {
172 return (maxverts - 3 - availverts)/3;
173 }
174 };
175
176 struct stainrenderer
177 {
178 const char *texname;
179 int flags, fadeintime, fadeouttime, timetolive;
180 Texture *tex;
181 staininfo *stains;
182 int maxstains, startstain, endstain;
183 stainbuffer verts[NUMSTAINBUFS];
184
stainrendererstainrenderer185 stainrenderer(const char *texname, int flags = 0, int fadeintime = 0, int fadeouttime = 1000, int timetolive = -1)
186 : texname(texname), flags(flags),
187 fadeintime(fadeintime), fadeouttime(fadeouttime), timetolive(timetolive),
188 tex(NULL),
189 stains(NULL), maxstains(0), startstain(0), endstain(0),
190 stainu(0), stainv(0)
191 {
192 }
193
~stainrendererstainrenderer194 ~stainrenderer()
195 {
196 DELETEA(stains);
197 }
198
usegbufferstainrenderer199 bool usegbuffer() const { return !(flags&(SF_INVMOD|SF_GLOW)); }
200
initstainrenderer201 void init(int tris)
202 {
203 if(stains)
204 {
205 DELETEA(stains);
206 maxstains = startstain = endstain = 0;
207 }
208 stains = new staininfo[tris];
209 maxstains = tris;
210 loopi(NUMSTAINBUFS) verts[i].init(i == STAINBUF_TRANSPARENT ? tris/2 : tris);
211 }
212
preloadstainrenderer213 void preload()
214 {
215 tex = textureload(texname, 3);
216 }
217
totalstainsstainrenderer218 int totalstains()
219 {
220 return endstain < startstain ? maxstains - (startstain - endstain) : endstain - startstain;
221 }
222
hasstainsstainrenderer223 bool hasstains(int sbuf)
224 {
225 return verts[sbuf].hasverts();
226 }
227
clearstainsstainrenderer228 void clearstains()
229 {
230 startstain = endstain = 0;
231 loopi(NUMSTAINBUFS) verts[i].clear();
232 }
233
freestainstainrenderer234 int freestain()
235 {
236 if(startstain==endstain) return 0;
237
238 staininfo &d = stains[startstain];
239 startstain++;
240 if(startstain >= maxstains) startstain = 0;
241
242 return verts[d.owner].freestain(d);
243 }
244
fadedstainrenderer245 bool faded(const staininfo &d) const { return verts[d.owner].faded(d); }
246
fadestainstainrenderer247 void fadestain(const staininfo &d, uchar alpha)
248 {
249 bvec color = d.color;
250 if(flags&(SF_OVERBRIGHT|SF_GLOW|SF_INVMOD)) color.scale(alpha, 255);
251 verts[d.owner].fadestain(d, bvec4(color, alpha));
252 }
253
clearfadedstainsstainrenderer254 void clearfadedstains()
255 {
256 int threshold = lastmillis - (timetolive>=0 ? timetolive : stainfade) - fadeouttime;
257 staininfo *d = &stains[startstain],
258 *end = &stains[endstain < startstain ? maxstains : endstain],
259 *cleared[NUMSTAINBUFS] = { NULL };
260 for(; d < end && d->millis <= threshold; d++)
261 cleared[d->owner] = d;
262 if(d >= end && endstain < startstain)
263 for(d = stains, end = &stains[endstain]; d < end && d->millis <= threshold; d++)
264 cleared[d->owner] = d;
265 startstain = d - stains;
266 if(startstain == endstain) loopi(NUMSTAINBUFS) verts[i].clear();
267 else loopi(NUMSTAINBUFS) if(cleared[i]) verts[i].clearstains(*cleared[i]);
268 }
269
fadeinstainsstainrenderer270 void fadeinstains()
271 {
272 if(!fadeintime) return;
273 staininfo *d = &stains[endstain],
274 *end = &stains[endstain < startstain ? 0 : startstain];
275 while(d > end)
276 {
277 d--;
278 int fade = lastmillis - d->millis;
279 if(fade < fadeintime) fadestain(*d, (fade<<8)/fadeintime);
280 else if(faded(*d)) fadestain(*d, 255);
281 else return;
282 }
283 if(endstain < startstain)
284 {
285 d = &stains[maxstains];
286 end = &stains[startstain];
287 while(d > end)
288 {
289 d--;
290 int fade = lastmillis - d->millis;
291 if(fade < fadeintime) fadestain(*d, (fade<<8)/fadeintime);
292 else if(faded(*d)) fadestain(*d, 255);
293 else return;
294 }
295 }
296 }
297
fadeoutstainsstainrenderer298 void fadeoutstains()
299 {
300 staininfo *d = &stains[startstain],
301 *end = &stains[endstain < startstain ? maxstains : endstain];
302 int offset = (timetolive>=0 ? timetolive : stainfade) + fadeouttime - lastmillis;
303 while(d < end)
304 {
305 int fade = d->millis + offset;
306 if(fade >= fadeouttime) return;
307 fadestain(*d, (fade<<8)/fadeouttime);
308 d++;
309 }
310 if(endstain < startstain)
311 {
312 d = stains;
313 end = &stains[endstain];
314 while(d < end)
315 {
316 int fade = d->millis + offset;
317 if(fade >= fadeouttime) return;
318 fadestain(*d, (fade<<8)/fadeouttime);
319 d++;
320 }
321 }
322 }
323
setuprenderstatestainrenderer324 static void setuprenderstate(int sbuf, bool gbuf, int layer)
325 {
326 if(gbuf) maskgbuffer(sbuf == STAINBUF_TRANSPARENT ? "cg" : "c");
327 else zerofogcolor();
328
329 if(layer && ghasstencil)
330 {
331 glStencilFunc(GL_EQUAL, layer, 0x07);
332 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
333 }
334
335 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
336
337 enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
338
339 glDepthMask(GL_FALSE);
340 glEnable(GL_BLEND);
341
342 gle::enablevertex();
343 gle::enabletexcoord0();
344 gle::enablecolor();
345 }
346
cleanuprenderstatestainrenderer347 static void cleanuprenderstate(int sbuf, bool gbuf, int layer)
348 {
349 gle::clearvbo();
350
351 gle::disablevertex();
352 gle::disabletexcoord0();
353 gle::disablecolor();
354
355 glDepthMask(GL_TRUE);
356 glDisable(GL_BLEND);
357
358 disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
359
360 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
361
362 if(gbuf) maskgbuffer(sbuf == STAINBUF_TRANSPARENT ? "cndg" : "cnd");
363 else resetfogcolor();
364 }
365
cleanupstainrenderer366 void cleanup()
367 {
368 loopi(NUMSTAINBUFS) verts[i].cleanup();
369 }
370
renderstainrenderer371 void render(int sbuf)
372 {
373 float colorscale = 1, alphascale = 1;
374 if(flags&SF_OVERBRIGHT)
375 {
376 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
377 SETVARIANT(overbrightstain, sbuf == STAINBUF_TRANSPARENT ? 0 : -1, 0);
378 }
379 else if(flags&SF_GLOW)
380 {
381 glBlendFunc(GL_ONE, GL_ONE);
382 colorscale = ldrscale;
383 if(flags&SF_SATURATE) colorscale *= 2;
384 alphascale = 0;
385 SETSHADER(foggedstain);
386 }
387 else if(flags&SF_INVMOD)
388 {
389 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
390 alphascale = 0;
391 SETSHADER(foggedstain);
392 }
393 else
394 {
395 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
396 colorscale = ldrscale;
397 if(flags&SF_SATURATE) colorscale *= 2;
398 SETVARIANT(stain, sbuf == STAINBUF_TRANSPARENT ? 0 : -1, 0);
399 }
400 LOCALPARAMF(colorscale, colorscale, colorscale, colorscale, alphascale);
401
402 glBindTexture(GL_TEXTURE_2D, tex->id);
403
404 verts[sbuf].render();
405 }
406
newstainstainrenderer407 staininfo &newstain()
408 {
409 staininfo &d = stains[endstain];
410 int next = endstain + 1;
411 if(next>=maxstains) next = 0;
412 if(next==startstain) freestain();
413 endstain = next;
414 return d;
415 }
416
417 ivec bbmin, bbmax;
418 vec staincenter, stainnormal, staintangent, stainbitangent;
419 float stainradius, stainu, stainv;
420 bvec4 staincolor;
421
addstainstainrenderer422 void addstain(const vec ¢er, const vec &dir, float radius, const bvec &color, int info)
423 {
424 if(dir.iszero()) return;
425
426 bbmin = ivec(center).sub(radius);
427 bbmax = ivec(center).add(radius).add(1);
428
429 staincolor = bvec4(color, 255);
430 staincenter = center;
431 stainradius = radius;
432 stainnormal = dir;
433 #if 0
434 staintangent.orthogonal(dir);
435 #else
436 staintangent = vec(dir.z, -dir.x, dir.y);
437 staintangent.project(dir);
438 #endif
439 if(flags&SF_ROTATE) staintangent.rotate(sincos360[rnd(360)], dir);
440 staintangent.normalize();
441 stainbitangent.cross(staintangent, dir);
442 if(flags&SF_RND4)
443 {
444 stainu = 0.5f*(info&1);
445 stainv = 0.5f*((info>>1)&1);
446 }
447
448 loopi(NUMSTAINBUFS) verts[i].lastvert = verts[i].endvert;
449 gentris(worldroot, ivec(0, 0, 0), worldsize>>1);
450 loopi(NUMSTAINBUFS)
451 {
452 stainbuffer &buf = verts[i];
453 if(buf.endvert == buf.lastvert) continue;
454
455 if(dbgstain)
456 {
457 int nverts = buf.nextverts();
458 static const char * const sbufname[NUMSTAINBUFS] = { "opaque", "transparent", "mapmodel" };
459 conoutf("tris = %d, verts = %d, total tris = %d, %s", nverts/3, nverts, buf.totaltris(), sbufname[i]);
460 }
461
462 staininfo &d = newstain();
463 d.owner = i;
464 d.color = color;
465 d.millis = lastmillis;
466 d.startvert = buf.lastvert;
467 d.endvert = buf.endvert;
468 buf.addstain(d);
469 }
470 }
471
gentrisstainrenderer472 void gentris(cube &cu, int orient, const ivec &o, int size, materialsurface *mat = NULL, int vismask = 0)
473 {
474 vec pos[MAXFACEVERTS+4];
475 int numverts = 0, numplanes = 1;
476 vec planes[2];
477 if(mat)
478 {
479 planes[0] = vec(0, 0, 0);
480 switch(orient)
481 {
482 #define GENFACEORIENT(orient, v0, v1, v2, v3) \
483 case orient: \
484 planes[0][dimension(orient)] = dimcoord(orient) ? 1 : -1; \
485 v0 v1 v2 v3 \
486 break;
487 #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
488 pos[numverts++] = vec(x xv, y yv, z zv);
489 GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
490 #undef GENFACEORIENT
491 #undef GENFACEVERT
492 }
493 }
494 else if(cu.texture[orient] == DEFAULT_SKY) return;
495 else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&MAXFACEVERTS))
496 {
497 vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
498 ivec vo = ivec(o).mask(~0xFFF).shl(3);
499 loopj(numverts) pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
500 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
501 if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size))
502 {
503 planes[1].cross(pos[0], pos[2], pos[3]).normalize();
504 numplanes++;
505 }
506 }
507 else if(cu.merged&(1<<orient)) return;
508 else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, MAT_AIR, (cu.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA)))
509 {
510 ivec v[4];
511 genfaceverts(cu, orient, v);
512 int vis = 3, convex = faceconvexity(v, vis), order = convex < 0 ? 1 : 0;
513 vec vo(o);
514 pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
515 if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
516 pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
517 if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
518 planes[0].cross(pos[0], pos[1], pos[2]).normalize();
519 if(convex) { planes[1].cross(pos[0], pos[2], pos[3]).normalize(); numplanes++; }
520 }
521 else return;
522
523 stainbuffer &buf = verts[mat || cu.material&MAT_ALPHA ? STAINBUF_TRANSPARENT : STAINBUF_OPAQUE];
524 loopl(numplanes)
525 {
526 const vec &n = planes[l];
527 float facing = n.dot(stainnormal);
528 if(facing <= 0) continue;
529 vec p = vec(pos[0]).sub(staincenter);
530 #if 0
531 // intersect ray along stain normal with plane
532 float dist = n.dot(p) / facing;
533 if(fabs(dist) > stainradius) continue;
534 vec pcenter = vec(stainnormal).mul(dist).add(staincenter);
535 #else
536 // travel back along plane normal from the stain center
537 float dist = n.dot(p);
538 if(fabs(dist) > stainradius) continue;
539 vec pcenter = vec(n).mul(dist).add(staincenter);
540 #endif
541 vec ft, fb;
542 ft.orthogonal(n);
543 ft.normalize();
544 fb.cross(ft, n);
545 vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
546 pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
547 vec v1[MAXFACEVERTS+4], v2[MAXFACEVERTS+4];
548 float ptc = pt.dot(pcenter), pbc = pb.dot(pcenter);
549 int numv;
550 if(numplanes >= 2)
551 {
552 if(l) { pos[1] = pos[2]; pos[2] = pos[3]; }
553 numv = polyclip(pos, 3, pt, ptc - stainradius, ptc + stainradius, v1);
554 if(numv<3) continue;
555 }
556 else
557 {
558 numv = polyclip(pos, numverts, pt, ptc - stainradius, ptc + stainradius, v1);
559 if(numv<3) continue;
560 }
561 numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
562 if(numv<3) continue;
563 float tsz = flags&SF_RND4 ? 0.5f : 1.0f, scale = tsz*0.5f/stainradius,
564 tu = stainu + tsz*0.5f - ptc*scale, tv = stainv + tsz*0.5f - pbc*scale;
565 pt.mul(scale); pb.mul(scale);
566 stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
567 dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
568 int totalverts = 3*(numv-2);
569 if(totalverts > buf.maxverts-3) return;
570 while(buf.availverts < totalverts)
571 {
572 if(!freestain()) return;
573 }
574 loopk(numv-2)
575 {
576 stainvert *tri = buf.addtri();
577 tri[0] = dv1;
578 tri[1] = dv2;
579 dv2.pos = v2[k+2];
580 dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
581 tri[2] = dv2;
582 }
583 }
584 }
585
findmaterialsstainrenderer586 void findmaterials(vtxarray *va)
587 {
588 materialsurface *matbuf = va->matbuf;
589 int matsurfs = va->matsurfs;
590 loopi(matsurfs)
591 {
592 materialsurface &m = matbuf[i];
593 if(!isclipped(m.material&MATF_VOLUME)) { i += m.skip; continue; }
594 int dim = dimension(m.orient), dc = dimcoord(m.orient);
595 if(dc ? stainnormal[dim] <= 0 : stainnormal[dim] >= 0) { i += m.skip; continue; }
596 int c = C[dim], r = R[dim];
597 for(;;)
598 {
599 materialsurface &m = matbuf[i];
600 if(m.o[dim] >= bbmin[dim] && m.o[dim] <= bbmax[dim] &&
601 m.o[c] + m.csize >= bbmin[c] && m.o[c] <= bbmax[c] &&
602 m.o[r] + m.rsize >= bbmin[r] && m.o[r] <= bbmax[r])
603 {
604 static cube dummy;
605 gentris(dummy, m.orient, m.o, max(m.csize, m.rsize), &m);
606 }
607 if(i+1 >= matsurfs) break;
608 materialsurface &n = matbuf[i+1];
609 if(n.material != m.material || n.orient != m.orient) break;
610 i++;
611 }
612 }
613 }
614
findescapedstainrenderer615 void findescaped(cube *c, const ivec &o, int size, int escaped)
616 {
617 loopi(8)
618 {
619 cube &cu = c[i];
620 if(escaped&(1<<i))
621 {
622 ivec co(i, o, size);
623 if(cu.children) findescaped(cu.children, co, size>>1, cu.escaped);
624 else
625 {
626 int vismask = cu.merged;
627 if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu, j, co, size);
628 }
629 }
630 }
631 }
632
genmmtristainrenderer633 void genmmtri(const vec v[3])
634 {
635 vec n;
636 n.cross(v[0], v[1], v[2]).normalize();
637 float facing = n.dot(stainnormal);
638 if(facing <= 0) return;
639
640 vec p = vec(v[0]).sub(staincenter);
641 #if 0
642 float dist = n.dot(p) / facing;
643 if(fabs(dist) > stainradius) return;
644 vec pcenter = vec(stainnormal).mul(dist).add(staincenter);
645 #else
646 float dist = n.dot(p);
647 if(fabs(dist) > stainradius) return;
648 vec pcenter = vec(n).mul(dist).add(staincenter);
649 #endif
650
651 vec ft, fb;
652 ft.orthogonal(n);
653 ft.normalize();
654 fb.cross(ft, n);
655 vec pt = vec(ft).mul(ft.dot(staintangent)).add(vec(fb).mul(fb.dot(staintangent))).normalize(),
656 pb = vec(ft).mul(ft.dot(stainbitangent)).add(vec(fb).mul(fb.dot(stainbitangent))).project(pt).normalize();
657 vec v1[3+4], v2[3+4];
658 float ptc = pt.dot(pcenter), pbc = pb.dot(pcenter);
659 int numv = polyclip(v, 3, pt, ptc - stainradius, ptc + stainradius, v1);
660 if(numv<3) return;
661 numv = polyclip(v1, numv, pb, pbc - stainradius, pbc + stainradius, v2);
662 if(numv<3) return;
663 float tsz = flags&SF_RND4 ? 0.5f : 1.0f, scale = tsz*0.5f/stainradius,
664 tu = stainu + tsz*0.5f - ptc*scale, tv = stainv + tsz*0.5f - pbc*scale;
665 pt.mul(scale); pb.mul(scale);
666 stainvert dv1 = { v2[0], staincolor, vec2(pt.dot(v2[0]) + tu, pb.dot(v2[0]) + tv) },
667 dv2 = { v2[1], staincolor, vec2(pt.dot(v2[1]) + tu, pb.dot(v2[1]) + tv) };
668 int totalverts = 3*(numv-2);
669 stainbuffer &buf = verts[STAINBUF_MAPMODEL];
670 if(totalverts > buf.maxverts-3) return;
671 while(buf.availverts < totalverts)
672 {
673 if(!freestain()) return;
674 }
675 loopk(numv-2)
676 {
677 stainvert *tri = buf.addtri();
678 tri[0] = dv1;
679 tri[1] = dv2;
680 dv2.pos = v2[k+2];
681 dv2.tc = vec2(pt.dot(v2[k+2]) + tu, pb.dot(v2[k+2]) + tv);
682 tri[2] = dv2;
683 }
684 }
685
genmmtrisstainrenderer686 void genmmtris(octaentities &oe)
687 {
688 const vector<extentity *> &ents = entities::getents();
689 loopv(oe.mapmodels)
690 {
691 extentity &e = *ents[oe.mapmodels[i]];
692 model *m = loadmapmodel(e.attrs[0]);
693 if(!m) continue;
694
695 vec center, radius;
696 float rejectradius = m->collisionbox(center, radius), scale = e.attrs[5] > 0 ? e.attrs[5]/100.0f : 1;
697 center.mul(scale);
698 if(staincenter.reject(vec(e.o).add(center), stainradius + rejectradius*scale)) continue;
699
700 if(m->animated() || (!m->bih && !m->setBIH())) continue;
701
702 int yaw = e.attrs[1], pitch = e.attrs[2], roll = e.attrs[3];
703
704 m->bih->genstaintris(this, staincenter, stainradius, e.o, yaw, pitch, roll, scale);
705 }
706 }
707
gentrisstainrenderer708 void gentris(cube *c, const ivec &o, int size, int escaped = 0)
709 {
710 int overlap = octaboxoverlap(o, size, bbmin, bbmax);
711 loopi(8)
712 {
713 cube &cu = c[i];
714 if(overlap&(1<<i))
715 {
716 ivec co(i, o, size);
717 if(cu.ext)
718 {
719 if(cu.ext->va && cu.ext->va->matsurfs) findmaterials(cu.ext->va);
720 if(cu.ext->ents && cu.ext->ents->mapmodels.length()) genmmtris(*cu.ext->ents);
721 }
722 if(cu.children) gentris(cu.children, co, size>>1, cu.escaped);
723 else
724 {
725 int vismask = cu.visible;
726 if(vismask&0xC0)
727 {
728 if(vismask&0x80) loopj(6) gentris(cu, j, co, size, NULL, vismask);
729 else loopj(6) if(vismask&(1<<j)) gentris(cu, j, co, size);
730 }
731 }
732 }
733 else if(escaped&(1<<i))
734 {
735 ivec co(i, o, size);
736 if(cu.children) findescaped(cu.children, co, size>>1, cu.escaped);
737 else
738 {
739 int vismask = cu.merged;
740 if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu, j, co, size);
741 }
742 }
743 }
744 }
745 };
746
747 stainrenderer stains[] =
748 {
749 stainrenderer("<grey>particles/scorch", SF_ROTATE, 500, 1000, 10000),
750 stainrenderer("<grey>particles/scorch", SF_ROTATE, 500, 1000, 2000),
751 stainrenderer("<grey>particles/blood", SF_RND4|SF_ROTATE|SF_INVMOD, 0, 1000, 10000),
752 stainrenderer("<grey>particles/bullet", SF_OVERBRIGHT, 0, 1000, 10000),
753 stainrenderer("<grey>particles/energy", SF_ROTATE|SF_GLOW|SF_SATURATE, 150, 500, 3000),
754 stainrenderer("<grey>particles/stain", SF_SATURATE, 100, 900, 1000),
755 stainrenderer("<grey>particles/smoke", SF_ROTATE, 500, 1000, 10000)
756 };
757
initstains()758 void initstains()
759 {
760 if(initing) return;
761 loopi(sizeof(stains)/sizeof(stains[0])) stains[i].init(maxstaintris);
762 loopi(sizeof(stains)/sizeof(stains[0]))
763 {
764 loadprogress = float(i+1)/(sizeof(stains)/sizeof(stains[0]));
765 stains[i].preload();
766 }
767 loadprogress = 0;
768 }
769
clearstains()770 void clearstains()
771 {
772 loopi(sizeof(stains)/sizeof(stains[0])) stains[i].clearstains();
773 }
774
775 VARN(IDF_PERSIST, stains, showstains, 0, 1, 1);
776
renderstains(int sbuf,bool gbuf,int layer)777 bool renderstains(int sbuf, bool gbuf, int layer)
778 {
779 bool rendered = false;
780 loopi(sizeof(stains)/sizeof(stains[0]))
781 {
782 stainrenderer &d = stains[i];
783 if(d.usegbuffer() != gbuf) continue;
784 if(sbuf == STAINBUF_OPAQUE)
785 {
786 d.clearfadedstains();
787 d.fadeinstains();
788 d.fadeoutstains();
789 }
790 if(!showstains || !d.hasstains(sbuf)) continue;
791 if(!rendered)
792 {
793 rendered = true;
794 stainrenderer::setuprenderstate(sbuf, gbuf, layer);
795 }
796 d.render(sbuf);
797 }
798 if(!rendered) return false;
799 stainrenderer::cleanuprenderstate(sbuf, gbuf, layer);
800 return true;
801 }
802
cleanupstains()803 void cleanupstains()
804 {
805 loopi(sizeof(stains)/sizeof(stains[0])) stains[i].cleanup();
806 }
807
808 VAR(IDF_PERSIST, maxstaindistance, 1, 512, 10000);
809
addstain(int type,const vec & center,const vec & surface,float radius,const bvec & color,int info)810 void addstain(int type, const vec ¢er, const vec &surface, float radius, const bvec &color, int info)
811 {
812 int id = type-1;
813 if(!showstains || type<=0 || (size_t)type>sizeof(stains)/sizeof(stains[0]) || center.dist(camera1->o) - radius > maxstaindistance) return;
814 stainrenderer &d = stains[id];
815 d.addstain(center, surface, radius, color, info);
816 }
817
genstainmmtri(stainrenderer * s,const vec v[3])818 void genstainmmtri(stainrenderer *s, const vec v[3])
819 {
820 s->genmmtri(v);
821 }
822
823