1 #include "engine.h"
2 
3 extern int intel_mapbufferrange_bug;
4 
5 VARNP(blobs, showblobs, 0, 1, 1);
6 VARFP(blobintensity, 0, 60, 100, resetblobs());
7 VARFP(blobheight, 1, 32, 128, resetblobs());
8 VARFP(blobfadelow, 1, 8, 32, resetblobs());
9 VARFP(blobfadehigh, 1, 8, 32, resetblobs());
10 VARFP(blobmargin, 0, 1, 16, resetblobs());
11 
12 VAR(dbgblob, 0, 0, 1);
13 
14 enum
15 {
16     BL_DUP    = 1<<0,
17     BL_RENDER = 1<<1
18 };
19 
20 struct blobinfo
21 {
22     vec o;
23     float radius;
24     int millis;
25     ushort startindex, endindex, startvert, endvert, next, flags;
26 };
27 
28 struct blobvert
29 {
30     vec pos;
31     bvec4 color;
32     vec2 tc;
33 };
34 
35 struct blobrenderer
36 {
37     const char *texname;
38     Texture *tex;
39     ushort *cache;
40     int cachesize;
41     blobinfo *blobs;
42     int maxblobs, startblob, endblob;
43     blobvert *verts;
44     int maxverts, startvert, endvert, availverts;
45     ushort *indexes;
46     int maxindexes, startindex, endindex, availindexes;
47     GLuint ebo, vbo;
48     ushort *edata;
49     blobvert *vdata;
50     int numedata, numvdata;
51     blobinfo *startrender, *endrender;
52 
53     blobinfo *lastblob;
54 
55     vec blobmin, blobmax;
56     ivec bbmin, bbmax;
57     float blobalphalow, blobalphahigh;
58     uchar blobalpha;
59 
blobrendererblobrenderer60     blobrenderer(const char *texname)
61       : texname(texname), tex(NULL),
62         cache(NULL), cachesize(0),
63         blobs(NULL), maxblobs(0), startblob(0), endblob(0),
64         verts(NULL), maxverts(0), startvert(0), endvert(0), availverts(0),
65         indexes(NULL), maxindexes(0), startindex(0), endindex(0), availindexes(0),
66         ebo(0), vbo(0), edata(NULL), vdata(NULL), numedata(0), numvdata(0),
67         startrender(NULL), endrender(NULL), lastblob(NULL)
68     {}
69 
~blobrendererblobrenderer70     ~blobrenderer()
71     {
72         DELETEA(cache);
73         DELETEA(blobs);
74         DELETEA(verts);
75         DELETEA(indexes);
76     }
77 
cleanupblobrenderer78     void cleanup()
79     {
80         if(ebo) { glDeleteBuffers_(1, &ebo); ebo = 0; }
81         if(vbo) { glDeleteBuffers_(1, &vbo); vbo = 0; }
82         DELETEA(edata);
83         DELETEA(vdata);
84         numedata = numvdata = 0;
85         startrender = endrender = NULL;
86     }
87 
initblobrenderer88     void init(int tris)
89     {
90         cleanup();
91         if(cache)
92         {
93             DELETEA(cache);
94             cachesize = 0;
95         }
96         if(blobs)
97         {
98             DELETEA(blobs);
99             maxblobs = startblob = endblob = 0;
100         }
101         if(verts)
102         {
103             DELETEA(verts);
104             maxverts = startvert = endvert = availverts = 0;
105         }
106         if(indexes)
107         {
108             DELETEA(indexes);
109             maxindexes = startindex = endindex = availindexes = 0;
110         }
111         if(!tris) return;
112         tex = textureload(texname, 3);
113         cachesize = tris/2;
114         cache = new ushort[cachesize];
115         memset(cache, 0xFF, cachesize * sizeof(ushort));
116         maxblobs = tris/2;
117         blobs = new blobinfo[maxblobs];
118         memclear(blobs, maxblobs);
119         maxindexes = tris*3 + 3;
120         availindexes = maxindexes - 3;
121         indexes = new ushort[maxindexes];
122         maxverts = min(tris*3/2 + 1, (1<<16)-1);
123         availverts = maxverts - 1;
124         verts = new blobvert[maxverts];
125     }
126 
freeblobblobrenderer127     bool freeblob()
128     {
129         blobinfo &b = blobs[startblob];
130         if(&b == lastblob) return false;
131 
132         if(b.flags & BL_RENDER) flushblobs();
133 
134         startblob++;
135         if(startblob >= maxblobs) startblob = 0;
136 
137         startvert = b.endvert;
138         if(startvert>=maxverts) startvert = 0;
139         availverts += b.endvert - b.startvert;
140 
141         startindex = b.endindex;
142         if(startindex>=maxindexes) startindex = 0;
143         availindexes += b.endindex - b.startindex;
144 
145         b.millis = lastreset;
146         b.flags = 0;
147 
148         return true;
149     }
150 
newblobblobrenderer151     blobinfo &newblob(const vec &o, float radius)
152     {
153         blobinfo &b = blobs[endblob];
154         int next = endblob + 1;
155         if(next>=maxblobs) next = 0;
156         if(next==startblob)
157         {
158             lastblob = &b;
159             freeblob();
160         }
161         endblob = next;
162         b.o = o;
163         b.radius = radius;
164         b.millis = totalmillis;
165         b.flags = 0;
166         b.next = 0xFFFF;
167         b.startindex = b.endindex = endindex;
168         b.startvert = b.endvert = endvert;
169         lastblob = &b;
170         return b;
171     }
172 
173     template<int C>
splitblobrenderer174     static int split(const vec *in, int numin, float below, float above, vec *out)
175     {
176         int numout = 0;
177         const vec *p = &in[numin-1];
178         float pc = (*p)[C];
179         loopi(numin)
180         {
181             const vec &v = in[i];
182             float c = v[C];
183             if(c < below)
184             {
185                 if(pc > above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
186                 if(pc > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
187             }
188             else if(c > above)
189             {
190                 if(pc < below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
191                 if(pc < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
192             }
193             else if(pc < below)
194             {
195                 if(c > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
196             }
197             else if(pc > above && c < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
198             out[numout++] = v;
199             p = &v;
200             pc = c;
201         }
202         return numout;
203     }
204 
205     template<int C>
clipblobrenderer206     static int clip(const vec *in, int numin, float below, float above, vec *out)
207     {
208         int numout = 0;
209         const vec *p = &in[numin-1];
210         float pc = (*p)[C];
211         loopi(numin)
212         {
213             const vec &v = in[i];
214             float c = v[C];
215             if(c < below)
216             {
217                 if(pc > above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
218                 if(pc > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
219             }
220             else if(c > above)
221             {
222                 if(pc < below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
223                 if(pc < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
224             }
225             else
226             {
227                 if(pc < below)
228                 {
229                     if(c > below) out[numout++] = vec(*p).sub(v).mul((below - c)/(pc - c)).add(v);
230                 }
231                 else if(pc > above && c < above) out[numout++] = vec(*p).sub(v).mul((above - c)/(pc - c)).add(v);
232                 out[numout++] = v;
233             }
234             p = &v;
235             pc = c;
236         }
237         return numout;
238     }
239 
dupblobblobrenderer240     void dupblob()
241     {
242         if(lastblob->startvert >= lastblob->endvert)
243         {
244             lastblob->startindex = lastblob->endindex = endindex;
245             lastblob->startvert = lastblob->endvert = endvert;
246             return;
247         }
248         blobinfo &b = newblob(lastblob->o, lastblob->radius);
249         b.flags |= BL_DUP;
250     }
251 
addvertblobrenderer252     inline int addvert(const vec &pos)
253     {
254         blobvert &v = verts[endvert];
255         v.pos = pos;
256         v.tc = vec2((pos.x - blobmin.x) / (blobmax.x - blobmin.x),
257                     (pos.y - blobmin.y) / (blobmax.y - blobmin.y));
258         uchar alpha;
259         if(pos.z < blobmin.z + blobfadelow) alpha = uchar(blobalphalow * (pos.z - blobmin.z));
260         else if(pos.z > blobmax.z - blobfadehigh) alpha = uchar(blobalphahigh * (blobmax.z - pos.z));
261         else alpha = blobalpha;
262         v.color = bvec4(255, 255, 255, alpha);
263         return endvert++;
264     }
265 
addtrisblobrenderer266     void addtris(const vec *v, int numv)
267     {
268         if(endvert != int(lastblob->endvert) || endindex != int(lastblob->endindex)) dupblob();
269         for(const vec *cur = &v[2], *end = &v[numv];;)
270         {
271             int limit = maxverts - endvert - 2;
272             if(limit <= 0)
273             {
274                 while(availverts < limit+2) if(!freeblob()) return;
275                 availverts -= limit+2;
276                 lastblob->endvert = maxverts;
277                 endvert = 0;
278                 dupblob();
279                 limit = maxverts - 2;
280             }
281             limit = min(int(end - cur), min(limit, (maxindexes - endindex)/3));
282             while(availverts < limit+2) if(!freeblob()) return;
283             while(availindexes < limit*3) if(!freeblob()) return;
284 
285             int i1 = addvert(v[0]), i2 = addvert(cur[-1]);
286             loopk(limit)
287             {
288                 indexes[endindex++] = i1;
289                 indexes[endindex++] = i2;
290                 i2 = addvert(*cur++);
291                 indexes[endindex++] = i2;
292             }
293 
294             availverts -= endvert - lastblob->endvert;
295             availindexes -= endindex - lastblob->endindex;
296             lastblob->endvert = endvert;
297             lastblob->endindex = endindex;
298             if(endvert >= maxverts) endvert = 0;
299             if(endindex >= maxindexes) endindex = 0;
300 
301             if(cur >= end) break;
302             dupblob();
303         }
304     }
305 
gentrisblobrenderer306     void gentris(cube &cu, int orient, const ivec &o, int size, materialsurface *mat = NULL, int vismask = 0)
307     {
308         vec pos[MAXFACEVERTS+8];
309         int dim = dimension(orient), numverts = 0, numplanes = 1, flat = -1;
310         if(mat)
311         {
312             switch(orient)
313             {
314             #define GENFACEORIENT(orient, v0, v1, v2, v3) \
315                 case orient: v0 v1 v2 v3 break;
316             #define GENFACEVERT(orient, vert, x,y,z, xv,yv,zv) \
317                     pos[numverts++] = vec(x xv, y yv, z zv);
318                 GENFACEVERTS(o.x, o.x, o.y, o.y, o.z, o.z, , + mat->csize, , + mat->rsize, + 0.1f, - 0.1f);
319             #undef GENFACEORIENT
320             #undef GENFACEVERT
321                 default:
322                     return;
323             }
324             flat = dim;
325         }
326         else if(cu.texture[orient] == DEFAULT_SKY) return;
327         else if(cu.ext && (numverts = cu.ext->surfaces[orient].numverts&MAXFACEVERTS))
328         {
329             vertinfo *verts = cu.ext->verts() + cu.ext->surfaces[orient].verts;
330             ivec vo = ivec(o).mask(~0xFFF).shl(3);
331             loopj(numverts) pos[j] = vec(verts[j].getxyz().add(vo)).mul(1/8.0f);
332             if(numverts >= 4 && !(cu.merged&(1<<orient)) && !flataxisface(cu, orient) && faceconvexity(verts, numverts, size)) numplanes++;
333             else flat = dim;
334         }
335         else if(cu.merged&(1<<orient)) return;
336         else if(!vismask || (vismask&0x40 && visibleface(cu, orient, o, size, MAT_AIR, (cu.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA)))
337         {
338             ivec v[4];
339             genfaceverts(cu, orient, v);
340             int vis = 3, convex = faceconvexity(v, vis), order = convex < 0 ? 1 : 0;
341             vec vo(o);
342             pos[numverts++] = vec(v[order]).mul(size/8.0f).add(vo);
343             if(vis&1) pos[numverts++] = vec(v[order+1]).mul(size/8.0f).add(vo);
344             pos[numverts++] = vec(v[order+2]).mul(size/8.0f).add(vo);
345             if(vis&2) pos[numverts++] = vec(v[(order+3)&3]).mul(size/8.0f).add(vo);
346             if(convex) numplanes++;
347             else flat = dim;
348         }
349         else return;
350 
351         if(flat >= 0)
352         {
353             float offset = pos[0][dim];
354             if(offset < blobmin[dim] || offset > blobmax[dim]) return;
355             flat = dim;
356         }
357 
358         vec vmin = pos[0], vmax = pos[0];
359         for(int i = 1; i < numverts; i++) { vmin.min(pos[i]); vmax.max(pos[i]); }
360         if(vmax.x < blobmin.x || vmin.x > blobmax.x || vmax.y < blobmin.y || vmin.y > blobmax.y ||
361            vmax.z < blobmin.z || vmin.z > blobmax.z)
362             return;
363 
364         vec v1[MAXFACEVERTS+6+4], v2[MAXFACEVERTS+6+4];
365         loopl(numplanes)
366         {
367             vec *v = pos;
368             int numv = numverts;
369             if(numplanes >= 2)
370             {
371                 if(l) { pos[1] = pos[2]; pos[2] = pos[3]; }
372                 numv = 3;
373             }
374             if(vec().cross(v[0], v[1], v[2]).z <= 0) continue;
375             #define CLIPSIDE(clip, below, above) \
376                 { \
377                     vec *in = v; \
378                     v = in==v1 ? v2 : v1; \
379                     numv = clip(in, numv, below, above, v); \
380                     if(numv < 3) continue; \
381                 }
382             if(flat!=0) CLIPSIDE(clip<0>, blobmin.x, blobmax.x);
383             if(flat!=1) CLIPSIDE(clip<1>, blobmin.y, blobmax.y);
384             if(flat!=2)
385             {
386                 CLIPSIDE(clip<2>, blobmin.z, blobmax.z);
387                 CLIPSIDE(split<2>, blobmin.z + blobfadelow, blobmax.z - blobfadehigh);
388             }
389             addtris(v, numv);
390         }
391     }
392 
findmaterialsblobrenderer393     void findmaterials(vtxarray *va)
394     {
395         materialsurface *matbuf = va->matbuf;
396         int matsurfs = va->matsurfs;
397         loopi(matsurfs)
398         {
399             materialsurface &m = matbuf[i];
400             if(!isclipped(m.material&MATF_VOLUME) || m.orient == O_BOTTOM) { i += m.skip; continue; }
401             int dim = dimension(m.orient), c = C[dim], r = R[dim];
402             for(;;)
403             {
404                 materialsurface &m = matbuf[i];
405                 if(m.o[dim] >= blobmin[dim] && m.o[dim] <= blobmax[dim] &&
406                    m.o[c] + m.csize >= blobmin[c] && m.o[c] <= blobmax[c] &&
407                    m.o[r] + m.rsize >= blobmin[r] && m.o[r] <= blobmax[r])
408                 {
409                     static cube dummy;
410                     gentris(dummy, m.orient, m.o, max(m.csize, m.rsize), &m);
411                 }
412                 if(i+1 >= matsurfs) break;
413                 materialsurface &n = matbuf[i+1];
414                 if(n.material != m.material || n.orient != m.orient) break;
415                 i++;
416             }
417         }
418     }
419 
findescapedblobrenderer420     void findescaped(cube *cu, const ivec &o, int size, int escaped)
421     {
422         loopi(8)
423         {
424             if(escaped&(1<<i))
425             {
426                 ivec co(i, o, size);
427                 if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
428                 else
429                 {
430                     int vismask = cu[i].merged;
431                     if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
432                 }
433             }
434         }
435     }
436 
gentrisblobrenderer437     void gentris(cube *cu, const ivec &o, int size, int escaped = 0)
438     {
439         int overlap = octaboxoverlap(o, size, bbmin, bbmax);
440         loopi(8)
441         {
442             if(overlap&(1<<i))
443             {
444                 ivec co(i, o, size);
445                 if(cu[i].ext && cu[i].ext->va && cu[i].ext->va->matsurfs)
446                     findmaterials(cu[i].ext->va);
447                 if(cu[i].children) gentris(cu[i].children, co, size>>1, cu[i].escaped);
448                 else
449                 {
450                     int vismask = cu[i].visible;
451                     if(vismask&0xC0)
452                     {
453                         if(vismask&0x80) loopj(6) gentris(cu[i], j, co, size, NULL, vismask);
454                         else loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
455                     }
456                 }
457             }
458             else if(escaped&(1<<i))
459             {
460                 ivec co(i, o, size);
461                 if(cu[i].children) findescaped(cu[i].children, co, size>>1, cu[i].escaped);
462                 else
463                 {
464                     int vismask = cu[i].merged;
465                     if(vismask) loopj(6) if(vismask&(1<<j)) gentris(cu[i], j, co, size);
466                 }
467             }
468         }
469     }
470 
addblobblobrenderer471     blobinfo *addblob(const vec &o, float radius, float fade)
472     {
473         lastblob = &blobs[endblob];
474         blobinfo &b = newblob(o, radius);
475         blobmin = blobmax = o;
476         blobmin.x -= radius;
477         blobmin.y -= radius;
478         blobmin.z -= blobheight + blobfadelow;
479         blobmax.x += radius;
480         blobmax.y += radius;
481         blobmax.z += blobfadehigh;
482         (bbmin = ivec(blobmin)).sub(2);
483         (bbmax = ivec(blobmax)).add(2);
484         float scale =  fade*blobintensity*255/100.0f;
485         blobalphalow = scale / blobfadelow;
486         blobalphahigh = scale / blobfadehigh;
487         blobalpha = uchar(scale);
488         gentris(worldroot, ivec(0, 0, 0), worldsize>>1);
489         return !(b.flags & BL_DUP) ? &b : NULL;
490     }
491 
setuprenderstateblobrenderer492     static void setuprenderstate()
493     {
494         foggedshader->set();
495 
496         enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
497 
498         glDepthMask(GL_FALSE);
499         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
500         if(!dbgblob) glEnable(GL_BLEND);
501 
502         gle::enablevertex();
503         gle::enabletexcoord0();
504         gle::enablecolor();
505     }
506 
cleanuprenderstateblobrenderer507     static void cleanuprenderstate()
508     {
509         gle::disablevertex();
510         gle::disabletexcoord0();
511         gle::disablecolor();
512 
513         gle::clearvbo();
514         if(glversion >= 300) gle::clearebo();
515 
516         glDepthMask(GL_TRUE);
517         glDisable(GL_BLEND);
518 
519         disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
520     }
521 
522     static int lastreset;
523 
resetblobrenderer524     static void reset()
525     {
526         lastreset = totalmillis;
527     }
528 
529     static blobrenderer *lastrender;
530 
fadeblobblobrenderer531     void fadeblob(blobinfo *b, float fade)
532     {
533         float minz = b->o.z - (blobheight + blobfadelow), maxz = b->o.z + blobfadehigh,
534               scale = fade*blobintensity*255/100.0f, scalelow = scale / blobfadelow, scalehigh = scale / blobfadehigh;
535         uchar alpha = uchar(scale);
536         b->millis = totalmillis;
537         do
538         {
539             if(b->endvert - b->startvert >= 3) for(blobvert *v = &verts[b->startvert], *end = &verts[b->endvert]; v < end; v++)
540             {
541                 float z = v->pos.z;
542                 if(z < minz + blobfadelow) v->color.a = uchar(scalelow * (z - minz));
543                 else if(z > maxz - blobfadehigh) v->color.a = uchar(scalehigh * (maxz - z));
544                 else v->color.a = alpha;
545             }
546             int offset = b - &blobs[0] + 1;
547             if(offset >= maxblobs) offset = 0;
548             if(offset < endblob ? offset > startblob || startblob > endblob : offset > startblob) b = &blobs[offset];
549             else break;
550         } while(b->flags & BL_DUP);
551     }
552 
renderblobblobrenderer553     void renderblob(const vec &o, float radius, float fade)
554     {
555         if(!blobs) initblobs();
556 
557         if(glversion < 300 && lastrender != this)
558         {
559             if(!lastrender) setuprenderstate();
560             gle::vertexpointer(sizeof(blobvert), verts->pos.v);
561             gle::texcoord0pointer(sizeof(blobvert), verts->tc.v);
562             gle::colorpointer(sizeof(blobvert), verts->color.v);
563             if(!lastrender || lastrender->tex != tex) glBindTexture(GL_TEXTURE_2D, tex->id);
564             lastrender = this;
565         }
566 
567         union { int i; float f; } ox, oy;
568         ox.f = o.x; oy.f = o.y;
569         uint hash = uint(ox.i^~oy.i^(INT_MAX-oy.i)^uint(radius));
570         hash %= cachesize;
571         blobinfo *b = &blobs[cache[hash]];
572         if(b >= &blobs[maxblobs] || b->millis - lastreset <= 0 || b->o!=o || b->radius!=radius)
573         {
574             b = addblob(o, radius, fade);
575             cache[hash] = ushort(b - blobs);
576             if(!b) return;
577         }
578         else if(fade < 1 && totalmillis - b->millis > 0) fadeblob(b, fade);
579         do
580         {
581             if(b->endvert - b->startvert >= 3)
582             {
583                 if(glversion >= 300)
584                 {
585                     if(!startrender) { numedata = numvdata = 0; startrender = endrender = b; }
586                     else { endrender->next = ushort(b - blobs); endrender = b; }
587                     b->flags |= BL_RENDER;
588                     b->next = 0xFFFF;
589                     numedata += b->endindex - b->startindex;
590                     numvdata += b->endvert - b->startvert;
591                 }
592                 else
593                 {
594                     glDrawRangeElements_(GL_TRIANGLES, b->startvert, b->endvert-1, b->endindex - b->startindex, GL_UNSIGNED_SHORT, &indexes[b->startindex]);
595                     xtravertsva += b->endvert - b->startvert;
596                 }
597             }
598             int offset = b - &blobs[0] + 1;
599             if(offset >= maxblobs) offset = 0;
600             if(offset < endblob ? offset > startblob || startblob > endblob : offset > startblob) b = &blobs[offset];
601             else break;
602         } while(b->flags & BL_DUP);
603     }
604 
flushblobsblobrenderer605     void flushblobs()
606     {
607         if(glversion < 300 || !startrender) return;
608 
609         if(lastrender != this)
610         {
611             if(!lastrender) setuprenderstate();
612             lastrender = this;
613         }
614 
615         if(!ebo) glGenBuffers_(1, &ebo);
616         if(!vbo) glGenBuffers_(1, &vbo);
617 
618         gle::bindebo(ebo);
619         glBufferData_(GL_ELEMENT_ARRAY_BUFFER, maxindexes*sizeof(ushort), NULL, GL_STREAM_DRAW);
620         gle::bindvbo(vbo);
621         glBufferData_(GL_ARRAY_BUFFER, maxverts*sizeof(blobvert), NULL, GL_STREAM_DRAW);
622 
623         ushort *estart;
624         blobvert *vstart;
625         if(intel_mapbufferrange_bug)
626         {
627             if(!edata) edata = new ushort[maxindexes];
628             if(!vdata) vdata = new blobvert[maxverts];
629             estart = edata;
630             vstart = vdata;
631         }
632         else
633         {
634             estart = (ushort *)glMapBufferRange_(GL_ELEMENT_ARRAY_BUFFER, 0, numedata*sizeof(ushort), GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT);
635             vstart = (blobvert *)glMapBufferRange_(GL_ARRAY_BUFFER, 0, numvdata*sizeof(blobvert), GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT);
636             if(!estart || !vstart)
637             {
638                 if(estart) glUnmapBuffer_(GL_ELEMENT_ARRAY_BUFFER);
639                 if(vstart) glUnmapBuffer_(GL_ARRAY_BUFFER);
640                 for(blobinfo *b = startrender;; b = &blobs[b->next])
641                 {
642                     b->flags &= ~BL_RENDER;
643                     if(b->next >= maxblobs) break;
644                 }
645                 startrender = endrender = NULL;
646                 return;
647             }
648         }
649 
650         ushort *edst = estart;
651         blobvert *vdst = vstart;
652         for(blobinfo *b = startrender;; b = &blobs[b->next])
653         {
654             b->flags &= ~BL_RENDER;
655             ushort offset = ushort(vdst - vstart) - b->startvert;
656             for(int i = b->startindex; i < b->endindex; ++i)
657                 *edst++ = indexes[i] + offset;
658             memcpy(vdst, &verts[b->startvert], (b->endvert - b->startvert)*sizeof(blobvert));
659             vdst += b->endvert - b->startvert;
660             if(b->next >= maxblobs) break;
661         }
662         startrender = endrender = NULL;
663 
664         if(intel_mapbufferrange_bug)
665         {
666             glBufferSubData_(GL_ELEMENT_ARRAY_BUFFER, 0, numedata*sizeof(ushort), estart);
667             glBufferSubData_(GL_ARRAY_BUFFER, 0, numvdata*sizeof(blobvert), vstart);
668         }
669         else
670         {
671             glUnmapBuffer_(GL_ELEMENT_ARRAY_BUFFER);
672             glUnmapBuffer_(GL_ARRAY_BUFFER);
673         }
674 
675         const blobvert *ptr = 0;
676         gle::vertexpointer(sizeof(blobvert), ptr->pos.v);
677         gle::texcoord0pointer(sizeof(blobvert), ptr->tc.v);
678         gle::colorpointer(sizeof(blobvert), ptr->color.v);
679 
680         glBindTexture(GL_TEXTURE_2D, tex->id);
681 
682         glDrawRangeElements_(GL_TRIANGLES, 0, numvdata-1, numedata, GL_UNSIGNED_SHORT, (ushort *)0);
683     }
684 };
685 
686 int blobrenderer::lastreset = 0;
687 blobrenderer *blobrenderer::lastrender = NULL;
688 
689 VARFP(blobstattris, 128, 4096, 16384, initblobs(BLOB_STATIC));
690 VARFP(blobdyntris, 128, 4096, 16384, initblobs(BLOB_DYNAMIC));
691 
692 static blobrenderer blobs[] =
693 {
694     blobrenderer("<grey>packages/particles/blob.png"),
695     blobrenderer("<grey>packages/particles/blob.png")
696 };
697 
initblobs(int type)698 void initblobs(int type)
699 {
700     if(type < 0 || (type==BLOB_STATIC && blobs[BLOB_STATIC].blobs)) blobs[BLOB_STATIC].init(showblobs ? blobstattris : 0);
701     if(type < 0 || (type==BLOB_DYNAMIC && blobs[BLOB_DYNAMIC].blobs)) blobs[BLOB_DYNAMIC].init(showblobs ? blobdyntris : 0);
702 }
703 
resetblobs()704 void resetblobs()
705 {
706     blobrenderer::reset();
707 }
708 
renderblob(int type,const vec & o,float radius,float fade)709 void renderblob(int type, const vec &o, float radius, float fade)
710 {
711     if(!showblobs) return;
712     if(refracting < 0 && o.z - blobheight - blobfadelow >= reflectz) return;
713     blobs[type].renderblob(o, radius + blobmargin, fade);
714 }
715 
flushblobs()716 void flushblobs()
717 {
718     loopi(sizeof(blobs)/sizeof(blobs[0])) blobs[i].flushblobs();
719     if(blobrenderer::lastrender) blobrenderer::cleanuprenderstate();
720     blobrenderer::lastrender = NULL;
721 }
722 
cleanupblobs()723 void cleanupblobs()
724 {
725     loopi(sizeof(blobs)/sizeof(blobs[0])) blobs[i].cleanup();
726 }
727 
728