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