Initial sauer
[SauerbratenRemote.git] / src / engine / water.cpp
blobe6e43146ef61ff757b73dada136fb8e128164f92
1 #include "pch.h"
2 #include "engine.h"
4 VARFP(waterreflect, 0, 1, 1, cleanreflections());
5 VARFP(waterrefract, 0, 1, 1, cleanreflections());
6 VARP(refractfog, 0, 1, 1);
8 /* vertex water */
9 VARP(watersubdiv, 0, 2, 3);
10 VARP(waterlod, 0, 1, 3);
12 static int wx1, wy1, wx2, wy2, wsize;
13 float wcol[4];
15 #define VERTW(vertw, body) \
16 inline void vertw(float v1, float v2, float v3, float t) \
17 { \
18 float angle = (v1-wx1)/wsize*(v2-wy1)/wsize*(v1-wx2)*(v2-wy2)*59/23+t; \
19 float s = sinf(angle), h = WATER_AMPLITUDE*s-WATER_OFFSET; \
20 body; \
21 glVertex3f(v1, v2, v3+h); \
23 #define VERTWN(vertw, body) \
24 inline void vertw(float v1, float v2, float v3) \
25 { \
26 float h = -WATER_OFFSET; \
27 body; \
28 glVertex3f(v1, v2, v3+h); \
30 #define VERTWT(vertwt, body) VERTW(vertwt, { float v = cosf(angle); float duv = 0.5f*v; body; })
31 VERTW(vertwt, {
32 glTexCoord2f(v1/8.0f, v2/8.0f);
34 VERTWN(vertwtn, {
35 glTexCoord2f(v1/8.0f, v2/8.0f);
37 VERTW(vertwc, {
38 glColor4f(wcol[0], wcol[1], wcol[2], wcol[3] + fabs(s)*0.1f);
40 VERTWN(vertwcn, {
41 glColor4f(wcol[0], wcol[1], wcol[2], wcol[3]);
43 VERTWT(vertwtc, {
44 glColor4f(1, 1, 1, 0.2f + fabs(s)*0.1f);
45 glTexCoord3f(v1+duv, v2+duv, v3+h);
47 VERTWN(vertwtcn, {
48 glColor4f(1, 1, 1, 0.2f);
49 glTexCoord3f(v1, v2, v3+h);
51 VERTWT(vertwmtc, {
52 glColor4f(1, 1, 1, 0.2f + fabs(s)*0.1f);
53 glMultiTexCoord3f_(GL_TEXTURE0_ARB, v1-duv, v2+duv, v3+h);
54 glMultiTexCoord3f_(GL_TEXTURE1_ARB, v1+duv, v2+duv, v3+h);
56 VERTWN(vertwmtcn, {
57 glColor4f(1, 1, 1, 0.2f);
58 glMultiTexCoord3f_(GL_TEXTURE0_ARB, v1, v2, v3+h);
59 glMultiTexCoord3f_(GL_TEXTURE1_ARB, v1, v2, v3+h);
62 static float lavaxk = 1.0f, lavayk = 1.0f, lavascroll = 0.0f;
64 VERTW(vertl, {
65 glTexCoord2f(lavaxk*(v1+lavascroll), lavayk*(v2+lavascroll));
67 VERTWN(vertln, {
68 glTexCoord2f(lavaxk*(v1+lavascroll), lavayk*(v2+lavascroll));
71 #define renderwaterstrips(vertw, z, t) \
72 for(int x = wx1; x<wx2; x += subdiv) \
73 { \
74 glBegin(GL_TRIANGLE_STRIP); \
75 vertw(x, wy1, z, t); \
76 vertw(x+subdiv, wy1, z, t); \
77 for(int y = wy1; y<wy2; y += subdiv) \
78 { \
79 vertw(x, y+subdiv, z, t); \
80 vertw(x+subdiv, y+subdiv, z, t); \
81 } \
82 glEnd(); \
83 int n = (wy2-wy1-1)/subdiv; \
84 n = (n+2)*2; \
85 xtraverts += n; \
88 void rendervertwater(uint subdiv, int xo, int yo, int z, uint size, uchar mat = MAT_WATER)
90 wx1 = xo;
91 wy1 = yo;
92 wx2 = wx1 + size,
93 wy2 = wy1 + size;
94 wsize = size;
96 ASSERT((wx1 & (subdiv - 1)) == 0);
97 ASSERT((wy1 & (subdiv - 1)) == 0);
99 switch(mat)
101 case MAT_WATER:
103 float t = lastmillis/(renderpath!=R_FIXEDFUNCTION ? 600.0f : 300.0f);
104 if(renderpath!=R_FIXEDFUNCTION) { renderwaterstrips(vertwt, z, t); }
105 else if(nowater || (!waterrefract && !waterreflect)) { renderwaterstrips(vertwc, z, t); }
106 else if(waterrefract) { renderwaterstrips(vertwmtc, z, t); }
107 else { renderwaterstrips(vertwtc, z, t); }
108 break;
111 case MAT_LAVA:
113 float t = lastmillis/2000.0f;
114 renderwaterstrips(vertl, z, t);
115 break;
120 uint calcwatersubdiv(int x, int y, int z, uint size)
122 float dist;
123 if(camera1->o.x >= x && camera1->o.x < x + size &&
124 camera1->o.y >= y && camera1->o.y < y + size)
125 dist = fabs(camera1->o.z - float(z));
126 else
128 vec t(x + size/2, y + size/2, z + size/2);
129 dist = t.dist(camera1->o) - size*1.42f/2;
131 uint subdiv = watersubdiv + int(dist) / (32 << waterlod);
132 if(subdiv >= 8*sizeof(subdiv))
133 subdiv = ~0;
134 else
135 subdiv = 1 << subdiv;
136 return subdiv;
139 uint renderwaterlod(int x, int y, int z, uint size, uchar mat = MAT_WATER)
141 if(size <= (uint)(32 << waterlod))
143 uint subdiv = calcwatersubdiv(x, y, z, size);
144 if(subdiv < size * 2) rendervertwater(min(subdiv, size), x, y, z, size, mat);
145 return subdiv;
147 else
149 uint subdiv = calcwatersubdiv(x, y, z, size);
150 if(subdiv >= size)
152 if(subdiv < size * 2) rendervertwater(size, x, y, z, size, mat);
153 return subdiv;
155 uint childsize = size / 2,
156 subdiv1 = renderwaterlod(x, y, z, childsize, mat),
157 subdiv2 = renderwaterlod(x + childsize, y, z, childsize, mat),
158 subdiv3 = renderwaterlod(x + childsize, y + childsize, z, childsize, mat),
159 subdiv4 = renderwaterlod(x, y + childsize, z, childsize, mat),
160 minsubdiv = subdiv1;
161 minsubdiv = min(minsubdiv, subdiv2);
162 minsubdiv = min(minsubdiv, subdiv3);
163 minsubdiv = min(minsubdiv, subdiv4);
164 if(minsubdiv < size * 2)
166 if(minsubdiv >= size) rendervertwater(size, x, y, z, size, mat);
167 else
169 if(subdiv1 >= size) rendervertwater(childsize, x, y, z, childsize, mat);
170 if(subdiv2 >= size) rendervertwater(childsize, x + childsize, y, z, childsize, mat);
171 if(subdiv3 >= size) rendervertwater(childsize, x + childsize, y + childsize, z, childsize, mat);
172 if(subdiv4 >= size) rendervertwater(childsize, x, y + childsize, z, childsize, mat);
175 return minsubdiv;
179 #define renderwaterquad(vertwn, z) \
181 vertwn(x, y, z); \
182 vertwn(x+rsize, y, z); \
183 vertwn(x+rsize, y+csize, z); \
184 vertwn(x, y+csize, z); \
185 xtraverts += 4; \
188 void renderflatwater(int x, int y, int z, uint rsize, uint csize, uchar mat = MAT_WATER)
190 switch(mat)
192 case MAT_WATER:
193 if(renderpath!=R_FIXEDFUNCTION) { renderwaterquad(vertwtn, z); }
194 else if(nowater || (!waterrefract && !waterreflect)) { renderwaterquad(vertwcn, z); }
195 else if(waterrefract) { renderwaterquad(vertwmtcn, z); }
196 else { renderwaterquad(vertwtcn, z); }
197 break;
199 case MAT_LAVA:
200 renderwaterquad(vertln, z);
201 break;
205 VARFP(vertwater, 0, 1, 1, allchanged());
207 void renderlava(materialsurface &m, Texture *tex, float scale)
209 lavaxk = 8.0f/(tex->xs*scale);
210 lavayk = 8.0f/(tex->ys*scale);
211 lavascroll = lastmillis/1000.0f;
212 if(vertwater)
214 if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize, MAT_LAVA) >= (uint)m.csize * 2)
215 rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize, MAT_LAVA);
217 else renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize, MAT_LAVA);
220 /* reflective/refractive water */
222 #define MAXREFLECTIONS 16
224 struct Reflection
226 GLuint fb, refractfb;
227 GLuint tex, refracttex;
228 int height, lastupdate, lastused;
229 GLfloat tm[16];
230 occludequery *query;
231 vector<materialsurface *> matsurfs;
233 Reflection() : fb(0), refractfb(0), height(-1), lastused(0), query(NULL)
236 Reflection *findreflection(int height);
238 VARP(reflectdist, 0, 2000, 10000);
239 VAR(waterfog, 0, 150, 10000);
241 void getwatercolour(uchar *wcol)
243 static const uchar defaultwcol[3] = { 20, 70, 80};
244 if(hdr.watercolour[0] || hdr.watercolour[1] || hdr.watercolour[2]) memcpy(wcol, hdr.watercolour, 3);
245 else memcpy(wcol, defaultwcol, 3);
248 void watercolour(int *r, int *g, int *b)
250 hdr.watercolour[0] = *r;
251 hdr.watercolour[1] = *g;
252 hdr.watercolour[2] = *b;
255 COMMAND(watercolour, "iii");
257 VAR(lavafog, 0, 50, 10000);
259 void getlavacolour(uchar *lcol)
261 static const uchar defaultlcol[3] = { 255, 64, 0 };
262 if(hdr.lavacolour[0] || hdr.lavacolour[1] || hdr.lavacolour[2]) memcpy(lcol, hdr.lavacolour, 3);
263 else memcpy(lcol, defaultlcol, 3);
266 void lavacolour(int *r, int *g, int *b)
268 hdr.lavacolour[0] = *r;
269 hdr.lavacolour[1] = *g;
270 hdr.lavacolour[2] = *b;
273 COMMAND(lavacolour, "iii");
275 Shader *watershader = NULL, *waterreflectshader = NULL, *waterrefractshader = NULL, *waterfadeshader = NULL;
277 void setprojtexmatrix(Reflection &ref, bool init = true)
279 if(init && ref.lastupdate==totalmillis)
281 GLfloat pm[16], mm[16];
282 glGetFloatv(GL_PROJECTION_MATRIX, pm);
283 glGetFloatv(GL_MODELVIEW_MATRIX, mm);
285 glLoadIdentity();
286 glTranslatef(0.5f, 0.5f, 0);
287 glScalef(0.5f, 0.5f, 1);
288 glMultMatrixf(pm);
289 glMultMatrixf(mm);
291 glGetFloatv(GL_TEXTURE_MATRIX, ref.tm);
293 else glLoadMatrixf(ref.tm);
296 void setuprefractTMUs()
298 if(!refractfog) setuptmu(0, "K , T @ Ka");
300 glActiveTexture_(GL_TEXTURE1_ARB);
301 glEnable(GL_TEXTURE_2D);
303 setuptmu(1, "P , T @ C~a");
305 glActiveTexture_(GL_TEXTURE0_ARB);
308 void setupreflectTMUs()
310 setuptmu(0, "T , K @ Ca", "Ka * P~a");
312 glDepthMask(GL_FALSE);
313 glEnable(GL_BLEND);
314 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
317 void cleanupwaterTMUs(bool refract)
319 resettmu(0);
321 if(refract)
323 glActiveTexture_(GL_TEXTURE1_ARB);
324 resettmu(1);
325 glLoadIdentity();
326 glDisable(GL_TEXTURE_2D);
327 glActiveTexture_(GL_TEXTURE0_ARB);
329 else
331 glDisable(GL_BLEND);
332 glDepthMask(GL_TRUE);
336 VAR(waterspec, 0, 150, 1000);
338 Reflection reflections[MAXREFLECTIONS];
339 GLuint reflectiondb = 0;
341 VAR(oqwater, 0, 1, 1);
343 extern int oqfrags;
345 void renderwaterff()
347 glDisable(GL_CULL_FACE);
349 if(!nowater && (waterreflect || waterrefract))
351 if(waterrefract) setuprefractTMUs();
352 else setupreflectTMUs();
354 glMatrixMode(GL_TEXTURE);
356 else
358 glDisable(GL_TEXTURE_2D);
359 glDepthMask(GL_FALSE);
360 glEnable(GL_BLEND);
361 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364 int lastdepth = -1;
365 float offset = -WATER_OFFSET;
367 uchar wcolub[3];
368 getwatercolour(wcolub);
369 loopi(3) wcol[i] = wcolub[i]/255.0f;
371 bool blended = true;
372 loopi(MAXREFLECTIONS)
374 Reflection &ref = reflections[i];
375 if(ref.height<0 || ref.lastused<totalmillis || ref.matsurfs.empty()) continue;
377 if(!nowater && (waterreflect || waterrefract))
379 if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
380 if(waterrefract) glActiveTexture_(GL_TEXTURE1_ARB);
381 glBindTexture(GL_TEXTURE_2D, ref.tex);
382 setprojtexmatrix(ref);
384 if(waterrefract)
386 glActiveTexture_(GL_TEXTURE0_ARB);
387 glBindTexture(GL_TEXTURE_2D, camera1->o.z>=ref.height+offset ? ref.refracttex : ref.tex);
388 setprojtexmatrix(ref, !waterreflect);
390 else
392 if(camera1->o.z < ref.height+offset) { if(blended) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); blended = false; } }
393 else if(!blended) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); blended = true; }
396 bool begin = false;
397 loopvj(ref.matsurfs)
399 materialsurface &m = *ref.matsurfs[j];
401 if(m.depth!=lastdepth)
403 float depth = !waterfog ? 1.0f : min(0.75f*m.depth/waterfog, 0.95f);
404 if(nowater || !waterrefract) depth = max(depth, nowater || !waterreflect ? 0.6f : 0.3f);
405 wcol[3] = depth;
406 if(!nowater && (waterreflect || waterrefract))
408 if(begin) { glEnd(); begin = false; }
409 float ec[4] = { wcol[0], wcol[1], wcol[2], depth };
410 if(!waterrefract) { loopk(3) ec[k] *= depth; ec[3] = 1-ec[3]; }
411 colortmu(0, ec[0], ec[1], ec[2], ec[3]);
413 lastdepth = m.depth;
416 if(!vertwater)
418 if(!begin) { glBegin(GL_QUADS); begin = true; }
419 renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize);
421 else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize) >= (uint)m.csize * 2)
422 rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize);
424 if(begin) glEnd();
427 if(!nowater && (waterreflect || waterrefract))
429 cleanupwaterTMUs(waterrefract!=0);
430 glLoadIdentity();
431 glMatrixMode(GL_MODELVIEW);
433 else
435 glEnable(GL_TEXTURE_2D);
436 glDepthMask(GL_TRUE);
437 glDisable(GL_BLEND);
440 glEnable(GL_CULL_FACE);
443 VARFP(waterfade, 0, 1, 1, cleanreflections());
445 void renderwater()
447 if(editmode && showmat) return;
448 if(!rplanes) return;
450 if(renderpath==R_FIXEDFUNCTION) { renderwaterff(); return; }
452 glDisable(GL_CULL_FACE);
454 uchar wcol[3] = { 20, 70, 80 };
455 if(hdr.watercolour[0] || hdr.watercolour[1] || hdr.watercolour[2]) memcpy(wcol, hdr.watercolour, 3);
456 glColor3ubv(wcol);
458 Slot &s = lookuptexture(-MAT_WATER);
460 glActiveTexture_(GL_TEXTURE1_ARB);
461 glEnable(GL_TEXTURE_2D);
462 glBindTexture(GL_TEXTURE_2D, s.sts[2].t->gl);
463 glActiveTexture_(GL_TEXTURE2_ARB);
464 glEnable(GL_TEXTURE_2D);
465 glBindTexture(GL_TEXTURE_2D, s.sts[3].t->gl);
466 if(waterrefract)
468 glActiveTexture_(GL_TEXTURE3_ARB);
469 glEnable(GL_TEXTURE_2D);
470 if(hasFBO && renderpath!=R_FIXEDFUNCTION && waterfade)
472 glEnable(GL_BLEND);
473 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
476 else
478 glDepthMask(GL_FALSE);
479 glEnable(GL_BLEND);
480 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
482 glActiveTexture_(GL_TEXTURE0_ARB);
484 setenvparamf("camera", SHPARAM_VERTEX, 0, camera1->o.x, camera1->o.y, camera1->o.z);
485 setenvparamf("millis", SHPARAM_VERTEX, 1, lastmillis/1000.0f, lastmillis/1000.0f, lastmillis/1000.0f);
487 if(!watershader) watershader = lookupshaderbyname("water");
488 if(!waterreflectshader) waterreflectshader = lookupshaderbyname("waterreflect");
489 if(!waterrefractshader) waterrefractshader = lookupshaderbyname("waterrefract");
490 if(!waterfadeshader) waterfadeshader = lookupshaderbyname("waterfade");
492 (waterrefract ? (waterfade && hasFBO ? waterfadeshader : waterrefractshader) : (waterreflect ? waterreflectshader : watershader))->set();
494 if(waterreflect || waterrefract) glMatrixMode(GL_TEXTURE);
496 GLfloat oldfogc[4];
497 glGetFloatv(GL_FOG_COLOR, oldfogc);
498 vec ambient(max(hdr.skylight[0], hdr.ambient), max(hdr.skylight[1], hdr.ambient), max(hdr.skylight[2], hdr.ambient));
499 entity *lastlight = (entity *)-1;
500 int lastdepth = -1;
501 float offset = -WATER_OFFSET;
502 bool blended = true;
503 loopi(MAXREFLECTIONS)
505 Reflection &ref = reflections[i];
506 if(ref.height<0 || ref.lastused<totalmillis || ref.matsurfs.empty()) continue;
508 if(waterreflect || waterrefract)
510 if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
511 glBindTexture(GL_TEXTURE_2D, ref.tex);
512 setprojtexmatrix(ref);
515 if(waterrefract)
517 glActiveTexture_(GL_TEXTURE3_ARB);
518 glBindTexture(GL_TEXTURE_2D, camera1->o.z>=ref.height+offset ? ref.refracttex : ref.tex);
519 glActiveTexture_(GL_TEXTURE0_ARB);
520 if(waterfade) setlocalparamf("waterheight", SHPARAM_VERTEX, 7, ref.height+offset+2, ref.height+offset+2, ref.height+offset+2);
522 else if(waterreflect)
524 GLfloat fogc[4] = { 0, 0, 0, 1 };
525 if(camera1->o.z < ref.height+offset)
527 if(blended) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); blended = false; }
528 loopk(3) fogc[k] = oldfogc[k];
530 else if(!blended) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); blended = true; }
531 setlocalparamfv("rgbafog", SHPARAM_PIXEL, 6, fogc);
534 bool begin = false;
535 loopvj(ref.matsurfs)
537 materialsurface &m = *ref.matsurfs[j];
539 entity *light = (m.light && m.light->type==ET_LIGHT ? m.light : NULL);
540 if(light!=lastlight)
542 if(begin) { glEnd(); begin = false; }
543 const vec &lightpos = light ? light->o : vec(hdr.worldsize/2, hdr.worldsize/2, hdr.worldsize);
544 float lightrad = light && light->attr1 ? light->attr1 : hdr.worldsize*8.0f;
545 const vec &lightcol = (light ? vec(light->attr2, light->attr3, light->attr4) : vec(ambient)).div(255.0f).mul(waterspec/100.0f);
546 setlocalparamf("lightpos", SHPARAM_VERTEX, 2, lightpos.x, lightpos.y, lightpos.z);
547 setlocalparamf("lightcolor", SHPARAM_PIXEL, 3, lightcol.x, lightcol.y, lightcol.z);
548 setlocalparamf("lightradius", SHPARAM_PIXEL, 4, lightrad, lightrad, lightrad);
549 lastlight = light;
552 if(!waterrefract && m.depth!=lastdepth)
554 if(begin) { glEnd(); begin = false; }
555 float depth = !waterfog ? 1.0f : min(0.75f*m.depth/waterfog, 0.95f);
556 depth = max(depth, waterreflect ? 0.3f : 0.6f);
557 setlocalparamf("depth", SHPARAM_PIXEL, 5, depth, 1.0f-depth);
558 lastdepth = m.depth;
561 if(!vertwater)
563 if(!begin) { glBegin(GL_QUADS); begin = true; }
564 renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize);
566 else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize) >= (uint)m.csize * 2)
567 rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize);
569 if(begin) glEnd();
572 if(waterreflect || waterrefract)
574 glLoadIdentity();
575 glMatrixMode(GL_MODELVIEW);
578 if(waterrefract)
580 glActiveTexture_(GL_TEXTURE3_ARB);
581 glDisable(GL_TEXTURE_2D);
582 if(hasFBO && renderpath!=R_FIXEDFUNCTION && waterfade) glDisable(GL_BLEND);
584 else
586 glDepthMask(GL_TRUE);
587 glDisable(GL_BLEND);
590 loopi(2)
592 glActiveTexture_(GL_TEXTURE1_ARB+i);
593 glDisable(GL_TEXTURE_2D);
595 glActiveTexture_(GL_TEXTURE0_ARB);
597 glEnable(GL_CULL_FACE);
600 Reflection *findreflection(int height)
602 loopi(MAXREFLECTIONS)
604 if(reflections[i].height==height) return &reflections[i];
606 return NULL;
609 void cleanreflections()
611 loopi(MAXREFLECTIONS)
613 Reflection &ref = reflections[i];
614 if(ref.fb)
616 glDeleteFramebuffers_(1, &ref.fb);
617 ref.fb = 0;
619 if(ref.tex)
621 glDeleteTextures(1, &ref.tex);
622 ref.tex = 0;
623 ref.height = -1;
624 ref.lastupdate = 0;
626 if(ref.refractfb)
628 glDeleteFramebuffers_(1, &ref.refractfb);
629 ref.refractfb = 0;
631 if(ref.refracttex)
633 glDeleteTextures(1, &ref.refracttex);
634 ref.refracttex = 0;
637 if(reflectiondb)
639 glDeleteRenderbuffers_(1, &reflectiondb);
640 reflectiondb = 0;
644 VARFP(reflectsize, 6, 8, 10, cleanreflections());
646 void invalidatereflections()
648 if(hasFBO) return;
649 loopi(MAXREFLECTIONS) reflections[i].matsurfs.setsizenodelete(0);
652 void addreflection(materialsurface &m)
654 int height = m.o.z;
655 Reflection *ref = NULL, *oldest = NULL;
656 loopi(MAXREFLECTIONS)
658 Reflection &r = reflections[i];
659 if(r.height<0)
661 if(!ref) ref = &r;
663 else if(r.height==height)
665 r.matsurfs.add(&m);
666 if(r.lastused==totalmillis) return;
667 ref = &r;
668 break;
670 else if(!oldest || r.lastused<oldest->lastused) oldest = &r;
672 if(!ref)
674 if(!oldest || oldest->lastused==totalmillis) return;
675 ref = oldest;
677 if(ref->height!=height) ref->height = height;
678 rplanes++;
679 ref->lastused = totalmillis;
680 ref->matsurfs.setsizenodelete(0);
681 ref->matsurfs.add(&m);
682 if(nowater) return;
684 static const GLenum colorfmts[] = { GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE },
685 depthfmts[] = { GL_DEPTH_STENCIL_EXT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
686 const int stencilfmts = 2;
687 static GLenum reflectfmt = GL_FALSE, refractfmt = GL_FALSE, depthfmt = GL_FALSE, stencilfmt = GL_FALSE;
688 char *buf = NULL;
689 int size = 1<<reflectsize;
690 if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
691 while(size>hwtexsize) size /= 2;
692 if((waterreflect || waterrefract) && !ref->tex)
694 glGenTextures(1, &ref->tex);
695 buf = new char[size*size*4];
696 memset(buf, 0, size*size*4);
697 int find = 2;
698 if(hasFBO)
700 if(!ref->fb) glGenFramebuffers_(1, &ref->fb);
701 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, ref->fb);
705 createtexture(ref->tex, size, size, buf, 3, false, reflectfmt ? reflectfmt : colorfmts[find]);
706 if(!hasFBO) break;
707 else
709 glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ref->tex, 0);
710 if(glCheckFramebufferStatus_(GL_FRAMEBUFFER_EXT)==GL_FRAMEBUFFER_COMPLETE_EXT) break;
713 while(!reflectfmt && colorfmts[++find]);
714 if(!reflectfmt) reflectfmt = colorfmts[find];
716 if(hasFBO)
718 if(!reflectiondb) { glGenRenderbuffers_(1, &reflectiondb); depthfmt = stencilfmt = GL_FALSE; }
719 if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER_EXT, reflectiondb);
720 find = hasstencil && hasDS ? 0 : stencilfmts;
723 if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER_EXT, depthfmts[find], size, size);
724 glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, reflectiondb);
725 if(depthfmt ? stencilfmt : find<stencilfmts) glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, reflectiondb);
726 if(glCheckFramebufferStatus_(GL_FRAMEBUFFER_EXT)==GL_FRAMEBUFFER_COMPLETE_EXT) break;
728 while(!depthfmt && depthfmts[++find]);
729 if(!depthfmt)
731 glBindRenderbuffer_(GL_RENDERBUFFER_EXT, 0);
732 depthfmt = depthfmts[find];
733 stencilfmt = find<stencilfmts ? depthfmt : GL_FALSE;
736 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
739 if(waterrefract && !ref->refracttex)
741 glGenTextures(1, &ref->refracttex);
743 int find = hasFBO && renderpath!=R_FIXEDFUNCTION && waterrefract && waterfade ? 0 : 2;
744 if(hasFBO)
746 if(!ref->refractfb) glGenFramebuffers_(1, &ref->refractfb);
747 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, ref->refractfb);
751 createtexture(ref->refracttex, size, size, buf, 3, false, refractfmt ? refractfmt : colorfmts[find]);
752 if(!hasFBO) break;
753 else
755 glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ref->refracttex, 0);
756 if(glCheckFramebufferStatus_(GL_FRAMEBUFFER_EXT)==GL_FRAMEBUFFER_COMPLETE_EXT) break;
759 while(!refractfmt && colorfmts[++find]);
760 if(!refractfmt) refractfmt = colorfmts[find];
762 if(hasFBO)
764 glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, reflectiondb);
765 if(stencilfmt) glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, reflectiondb);
766 glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
769 if(buf) delete[] buf;
772 extern vtxarray *visibleva;
773 extern void drawreflection(float z, bool refract, bool clear);
775 int rplanes = 0;
777 static int lastquery = 0;
779 void queryreflections()
781 rplanes = 0;
783 static int lastsize = 0, size = 1<<reflectsize;
784 if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
785 if(size!=lastsize) { if(lastsize) cleanreflections(); lastsize = size; }
787 for(vtxarray *va = visibleva; va; va = va->next)
789 lodlevel &lod = va->l0;
790 if(!lod.matsurfs && va->occluded >= OCCLUDE_BB) continue;
791 loopi(lod.matsurfs)
793 materialsurface &m = lod.matbuf[i];
794 if(m.material==MAT_WATER && m.orient==O_TOP) addreflection(m);
798 lastquery = totalmillis;
800 if((editmode && showmat) || !hasOQ || !oqfrags || !oqwater || nowater || (!waterreflect && !waterrefract)) return;
802 float offset = vertwater ? 0.1f : WATER_OFFSET;
803 int refs = 0;
804 loopi(MAXREFLECTIONS)
806 Reflection &ref = reflections[i];
807 if(ref.height<0 || ref.lastused<totalmillis || ref.matsurfs.empty())
809 ref.query = NULL;
810 continue;
812 ref.query = newquery(&ref);
813 if(!ref.query) continue;
815 if(!refs)
817 nocolorshader->set();
818 glDepthMask(GL_FALSE);
819 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
820 glDisable(GL_CULL_FACE);
822 refs++;
823 startquery(ref.query);
824 glBegin(GL_QUADS);
825 loopvj(ref.matsurfs)
827 materialsurface &m = *ref.matsurfs[j];
828 drawmaterial(m.orient, m.o.x, m.o.y, m.o.z, m.csize, m.rsize, offset);
830 glEnd();
831 endquery(ref.query);
834 if(refs)
836 defaultshader->set();
837 glDepthMask(GL_TRUE);
838 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
839 glEnable(GL_CULL_FACE);
843 VARP(maxreflect, 1, 1, 8);
845 float reflecting = 0, refracting = 0;
847 VAR(maskreflect, 0, 2, 16);
849 void maskreflection(Reflection &ref, float offset, bool reflect)
851 if(!maskreflect)
853 glClear(GL_DEPTH_BUFFER_BIT | (hasstencil && hasDS ? GL_STENCIL_BUFFER_BIT : 0));
854 return;
856 glClearDepth(0);
857 glClear(GL_DEPTH_BUFFER_BIT | (hasstencil && hasDS ? GL_STENCIL_BUFFER_BIT : 0));
858 glClearDepth(1);
859 glDepthRange(1, 1);
860 glDepthFunc(GL_ALWAYS);
861 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
862 glDisable(GL_TEXTURE_2D);
863 glDisable(GL_CULL_FACE);
864 nocolorshader->set();
865 if(reflect)
867 glPushMatrix();
868 glTranslatef(0, 0, 2*(ref.height+offset));
869 glScalef(1, 1, -1);
871 int border = maskreflect;
872 glBegin(GL_QUADS);
873 loopv(ref.matsurfs)
875 materialsurface &m = *ref.matsurfs[i];
876 ivec o(m.o);
877 o[R[dimension(m.orient)]] -= border;
878 o[C[dimension(m.orient)]] -= border;
879 drawmaterial(m.orient, o.x, o.y, o.z, m.csize+2*border, m.rsize+2*border, -offset);
881 glEnd();
882 if(reflect) glPopMatrix();
883 defaultshader->set();
884 glEnable(GL_CULL_FACE);
885 glEnable(GL_TEXTURE_2D);
886 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
887 glDepthFunc(GL_LESS);
888 glDepthRange(0, 1);
891 void drawreflections()
893 if(editmode && showmat) return;
894 if(nowater || (!waterreflect && !waterrefract)) return;
896 static int lastdrawn = 0;
897 int refs = 0, n = lastdrawn;
898 float offset = -WATER_OFFSET;
899 loopi(MAXREFLECTIONS)
901 Reflection &ref = reflections[++n%MAXREFLECTIONS];
902 if(ref.height<0 || ref.lastused<lastquery || ref.matsurfs.empty()) continue;
903 if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
905 bool hasbottom = true;
906 loopvj(ref.matsurfs)
908 materialsurface &m = *ref.matsurfs[j];
909 if(m.depth>=10000) hasbottom = false;
912 int size = 1<<reflectsize;
913 if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
915 if(!refs) glViewport(hasFBO ? 0 : screen->w-size, hasFBO ? 0 : screen->h-size, size, size);
917 refs++;
918 ref.lastupdate = totalmillis;
919 lastdrawn = n;
921 if(waterreflect && ref.tex)
923 if(ref.fb) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, ref.fb);
924 maskreflection(ref, offset, camera1->o.z >= ref.height+offset);
925 drawreflection(ref.height+offset, false, false);
926 if(!ref.fb)
928 glBindTexture(GL_TEXTURE_2D, ref.tex);
929 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screen->w-size, screen->h-size, size, size);
933 if(waterrefract && ref.refracttex && camera1->o.z >= ref.height+offset)
935 if(ref.refractfb) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, ref.refractfb);
936 maskreflection(ref, offset, false);
937 drawreflection(ref.height+offset, true, !hasbottom);
938 if(!ref.refractfb)
940 glBindTexture(GL_TEXTURE_2D, ref.refracttex);
941 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screen->w-size, screen->h-size, size, size);
945 if(refs>=maxreflect) break;
948 if(!refs) return;
949 glViewport(0, 0, screen->w, screen->h);
950 if(hasFBO) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
952 defaultshader->set();