1 #include "engine.h"
2 
3 VARFP(waterreflect, 0, 1, 1, { cleanreflections(); preloadwatershaders(); });
4 VARFP(waterrefract, 0, 1, 1, { cleanreflections(); preloadwatershaders(); });
5 VARFP(waterenvmap, 0, 1, 1, { cleanreflections(); preloadwatershaders(); });
6 VARFP(waterfallrefract, 0, 0, 1, { cleanreflections(); preloadwatershaders(); });
7 
8 VARW(refractfog, 0, 1, 1);
9 VARW(watersubdiv, 0, 3, 3);
10 VARW(waterlod, 0, 1, 3);
11 
12 static int wx1, wy1, wx2, wy2, wsize;
13 float wcol[4];
14 
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); \
22 	}
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); \
29 	}
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);
33 })
34 VERTWN(vertwtn, {
35 	glTexCoord2f(v1/8.0f, v2/8.0f);
36 })
37 VERTW(vertwc, {
38 	glColor4f(wcol[0], wcol[1], wcol[2], wcol[3] + fabs(s)*0.1f);
39 })
40 VERTWN(vertwcn, {
41 	glColor4f(wcol[0], wcol[1], wcol[2], wcol[3]);
42 })
43 VERTWT(vertwtc, {
44 	glColor4f(1, 1, 1, 0.2f + fabs(s)*0.1f);
45 	glTexCoord3f(v1+duv, v2+duv, v3+h);
46 })
47 VERTWN(vertwtcn, {
48 	glColor4f(1, 1, 1, 0.2f);
49 	glTexCoord3f(v1, v2, v3+h);
50 })
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);
55 })
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);
60 })
61 VERTWT(vertwetc, {
62     glColor4f(1, 1, 1, 0.2f + fabs(s)*0.1f);
63     glTexCoord3f(v1+duv-camera1->o.x, v2+duv-camera1->o.y, camera1->o.z-(v3+h));
64 })
65 VERTWN(vertwetcn, {
66     glColor4f(1, 1, 1, 0.2f);
67     glTexCoord3f(v1-camera1->o.x, v2-camera1->o.y, camera1->o.z-(v3+h));
68 })
69 VERTWT(vertwemtc, {
70     glColor4f(1, 1, 1, 0.2f + fabs(s)*0.1f);
71     glMultiTexCoord3f_(GL_TEXTURE0_ARB, v1-duv, v2+duv, v3+h);
72     glMultiTexCoord3f_(GL_TEXTURE1_ARB, v1+duv-camera1->o.x, v2+duv-camera1->o.y, camera1->o.z-(v3+h));
73 })
74 VERTWN(vertwemtcn, {
75     glColor4f(1, 1, 1, 0.2f);
76     glMultiTexCoord3f_(GL_TEXTURE0_ARB, v1, v2, v3+h);
77     glMultiTexCoord3f_(GL_TEXTURE1_ARB, v1-camera1->o.x, v2-camera1->o.y, camera1->o.z-(v3+h));
78 })
79 
80 static float lavaxk = 1.0f, lavayk = 1.0f, lavascroll = 0.0f;
81 
82 VERTW(vertl, {
83 	glTexCoord2f(lavaxk*(v1+lavascroll), lavayk*(v2+lavascroll));
84 })
85 VERTWN(vertln, {
86 	glTexCoord2f(lavaxk*(v1+lavascroll), lavayk*(v2+lavascroll));
87 })
88 
89 #define renderwaterstrips(vertw, z, t) \
90 	for(int x = wx1; x<wx2; x += subdiv) \
91 	{ \
92 		glBegin(GL_TRIANGLE_STRIP); \
93 		vertw(x,		wy1, z, t); \
94 		vertw(x+subdiv, wy1, z, t); \
95 		for(int y = wy1; y<wy2; y += subdiv) \
96 		{ \
97 			vertw(x,		y+subdiv, z, t); \
98 			vertw(x+subdiv, y+subdiv, z, t); \
99 		} \
100 		glEnd(); \
101 		int n = (wy2-wy1-1)/subdiv; \
102 		n = (n+2)*2; \
103 		xtraverts += n; \
104 	}
105 
rendervertwater(uint subdiv,int xo,int yo,int z,uint size,uchar mat=MAT_WATER)106 void rendervertwater(uint subdiv, int xo, int yo, int z, uint size, uchar mat = MAT_WATER)
107 {
108 	wx1 = xo;
109 	wy1 = yo;
110 	wx2 = wx1 + size,
111 	wy2 = wy1 + size;
112 	wsize = size;
113 
114 	ASSERT((wx1 & (subdiv - 1)) == 0);
115 	ASSERT((wy1 & (subdiv - 1)) == 0);
116 
117 	switch(mat)
118 	{
119 		case MAT_WATER:
120 		{
121 			float t = lastmillis/(renderpath!=R_FIXEDFUNCTION ? 600.0f : 300.0f);
122 			if(renderpath!=R_FIXEDFUNCTION) { renderwaterstrips(vertwt, z, t); }
123             else
124             {
125                 bool below = camera1->o.z < z-WATER_OFFSET;
126                 if(nowater) { renderwaterstrips(vertwc, z, t); }
127                 else if(waterrefract)
128                 {
129                     if(waterreflect && !below) { renderwaterstrips(vertwmtc, z, t); }
130                     else if(waterenvmap && hasCM && !below) { renderwaterstrips(vertwemtc, z, t); }
131                     else { renderwaterstrips(vertwtc, z, t); }
132                 }
133                 else if(waterreflect && !below) { renderwaterstrips(vertwtc, z, t); }
134                 else if(waterenvmap && hasCM && !below) { renderwaterstrips(vertwetc, z, t); }
135                 else { renderwaterstrips(vertwc, z, t); }
136             }
137 			break;
138 		}
139 
140 		case MAT_LAVA:
141 		{
142 			float t = lastmillis/2000.0f;
143 			renderwaterstrips(vertl, z, t);
144 			break;
145 		}
146 	}
147 }
148 
calcwatersubdiv(int x,int y,int z,uint size)149 uint calcwatersubdiv(int x, int y, int z, uint size)
150 {
151 	float dist;
152 	if(camera1->o.x >= x && camera1->o.x < x + size &&
153 		camera1->o.y >= y && camera1->o.y < y + size)
154 		dist = fabs(camera1->o.z - float(z));
155 	else
156 	{
157 		vec t(x + size/2, y + size/2, z + size/2);
158 		dist = t.dist(camera1->o) - size*1.42f/2;
159 	}
160 	uint subdiv = watersubdiv + int(dist) / (32 << waterlod);
161 	if(subdiv >= 8*sizeof(subdiv))
162 		subdiv = ~0;
163 	else
164 		subdiv = 1 << subdiv;
165 	return subdiv;
166 }
167 
renderwaterlod(int x,int y,int z,uint size,uchar mat=MAT_WATER)168 uint renderwaterlod(int x, int y, int z, uint size, uchar mat = MAT_WATER)
169 {
170 	if(size <= (uint)(32 << waterlod))
171 	{
172 		uint subdiv = calcwatersubdiv(x, y, z, size);
173 		if(subdiv < size * 2) rendervertwater(min(subdiv, size), x, y, z, size, mat);
174 		return subdiv;
175 	}
176 	else
177 	{
178 		uint subdiv = calcwatersubdiv(x, y, z, size);
179 		if(subdiv >= size)
180 		{
181 			if(subdiv < size * 2) rendervertwater(size, x, y, z, size, mat);
182 			return subdiv;
183 		}
184 		uint childsize = size / 2,
185 			 subdiv1 = renderwaterlod(x, y, z, childsize, mat),
186 			 subdiv2 = renderwaterlod(x + childsize, y, z, childsize, mat),
187 			 subdiv3 = renderwaterlod(x + childsize, y + childsize, z, childsize, mat),
188 			 subdiv4 = renderwaterlod(x, y + childsize, z, childsize, mat),
189 			 minsubdiv = subdiv1;
190 		minsubdiv = min(minsubdiv, subdiv2);
191 		minsubdiv = min(minsubdiv, subdiv3);
192 		minsubdiv = min(minsubdiv, subdiv4);
193 		if(minsubdiv < size * 2)
194 		{
195 			if(minsubdiv >= size) rendervertwater(size, x, y, z, size, mat);
196 			else
197 			{
198 				if(subdiv1 >= size) rendervertwater(childsize, x, y, z, childsize, mat);
199 				if(subdiv2 >= size) rendervertwater(childsize, x + childsize, y, z, childsize, mat);
200 				if(subdiv3 >= size) rendervertwater(childsize, x + childsize, y + childsize, z, childsize, mat);
201 				if(subdiv4 >= size) rendervertwater(childsize, x, y + childsize, z, childsize, mat);
202 			}
203 		}
204 		return minsubdiv;
205 	}
206 }
207 
208 #define renderwaterquad(vertwn, z) \
209 	{ \
210 		vertwn(x, y, z); \
211 		vertwn(x+rsize, y, z); \
212 		vertwn(x+rsize, y+csize, z); \
213 		vertwn(x, y+csize, z); \
214 		xtraverts += 4; \
215 	}
216 
renderflatwater(int x,int y,int z,uint rsize,uint csize,uchar mat=MAT_WATER)217 void renderflatwater(int x, int y, int z, uint rsize, uint csize, uchar mat = MAT_WATER)
218 {
219 	switch(mat)
220 	{
221 		case MAT_WATER:
222 			if(renderpath!=R_FIXEDFUNCTION) { renderwaterquad(vertwtn, z); }
223             else
224             {
225                 bool below = camera1->o.z < z-WATER_OFFSET;
226                 if(nowater) { renderwaterquad(vertwcn, z); }
227                 else if(waterrefract)
228                 {
229                     if(waterreflect && !below) { renderwaterquad(vertwmtcn, z); }
230                     else if(waterenvmap && hasCM && !below) { renderwaterquad(vertwemtcn, z); }
231                     else { renderwaterquad(vertwtcn, z); }
232                 }
233                 else if(waterreflect && !below) { renderwaterquad(vertwtcn, z); }
234                 else if(waterenvmap && hasCM && !below) { renderwaterquad(vertwetcn, z); }
235                 else { renderwaterquad(vertwcn, z); }
236             }
237 			break;
238 
239 		case MAT_LAVA:
240 			renderwaterquad(vertln, z);
241 			break;
242 	}
243 }
244 
245 VARFW(vertwater, 0, 1, 1, if(!worldidents) allchanged());
246 
renderlava(materialsurface & m,Texture * tex,float scale)247 void renderlava(materialsurface &m, Texture *tex, float scale)
248 {
249 	lavaxk = 8.0f/(tex->xs*scale);
250 	lavayk = 8.0f/(tex->ys*scale);
251 	lavascroll = lastmillis/1000.0f;
252 	if(vertwater)
253 	{
254 		if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize, MAT_LAVA) >= (uint)m.csize * 2)
255 			rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize, MAT_LAVA);
256 	}
257 	else renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize, MAT_LAVA);
258 }
259 
260 /* reflective/refractive water */
261 
262 #define MAXREFLECTIONS 16
263 
264 struct Reflection
265 {
266 	GLuint tex, refracttex;
267 	int height, depth, lastupdate, lastused;
268     glmatrixf projmat;
269 	occludequery *query;
270 	vector<materialsurface *> matsurfs;
271 
ReflectionReflection272 	Reflection() : tex(0), refracttex(0), height(-1), depth(0), lastused(0), query(NULL)
273 	{}
274 };
275 Reflection *findreflection(int height);
276 
277 VARA(reflectdist, 0, 2000, 10000);
278 VARW(waterfog, 0, 150, 10000);
279 bvec watercol(0x10, 0x30, 0x60), waterfallcol(0, 0, 0);
280 HVARFW(watercolour, 0, 0x103060, 0xFFFFFF,
281 {
282     watercol = bvec((watercolour>>16)&0xFF, (watercolour>>8)&0xFF, watercolour&0xFF);
283 });
284 HVARFW(waterfallcolour, 0, 0, 0xFFFFFF,
285 {
286     waterfallcol = bvec((waterfallcolour>>16)&0xFF, (waterfallcolour>>8)&0xFF, waterfallcolour&0xFF);
287 });
288 VARW(lavafog, 0, 50, 10000);
289 bvec lavacol(0xFF, 0x44, 0x00);
290 HVARFW(lavacolour, 0, 0xFF4400, 0xFFFFFF,
291 {
292     lavacol = bvec((lavacolour>>16)&0xFF, (lavacolour>>8)&0xFF, lavacolour&0xFF);
293 });
294 
setprojtexmatrix(Reflection & ref,bool init=true)295 void setprojtexmatrix(Reflection &ref, bool init = true)
296 {
297     if(init && ref.lastupdate==totalmillis) (ref.projmat = mvpmatrix).projective();
298 
299     glLoadMatrixf(ref.projmat.v);
300 }
301 
setuprefractTMUs()302 void setuprefractTMUs()
303 {
304     if(!refractfog) setuptmu(0, "K , T @ Ka");
305 
306     if(waterreflect || (waterenvmap && hasCM))
307     {
308         glActiveTexture_(GL_TEXTURE1_ARB);
309         glEnable(waterreflect ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB);
310         if(!waterreflect) glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, lookupenvmap(lookupmaterialslot(MAT_WATER)));
311 
312         setuptmu(1, "P , T @ C~a");
313 
314         glActiveTexture_(GL_TEXTURE0_ARB);
315     }
316 }
317 
setupreflectTMUs()318 void setupreflectTMUs()
319 {
320 	setuptmu(0, "T , K @ Ca", "Ka * P~a");
321 
322 	glDepthMask(GL_FALSE);
323 	glEnable(GL_BLEND);
324 	glBlendFunc(GL_ONE, GL_SRC_ALPHA);
325 
326     if(!waterreflect)
327     {
328         glDisable(GL_TEXTURE_2D);
329         glEnable(GL_TEXTURE_CUBE_MAP_ARB);
330         glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, lookupenvmap(lookupmaterialslot(MAT_WATER)));
331     }
332 }
333 
cleanupwaterTMUs(bool refract)334 void cleanupwaterTMUs(bool refract)
335 {
336 	resettmu(0);
337 
338     if(refract)
339     {
340         if(waterrefract || (waterenvmap && hasCM))
341         {
342             glActiveTexture_(GL_TEXTURE1_ARB);
343             resettmu(1);
344             glLoadIdentity();
345             glDisable(waterreflect ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB);
346             glActiveTexture_(GL_TEXTURE0_ARB);
347         }
348     }
349     else
350     {
351         glDisable(GL_BLEND);
352         glDepthMask(GL_TRUE);
353     }
354 }
355 
356 VARW(waterspec, 0, 150, 1000);
357 
358 Reflection reflections[MAXREFLECTIONS];
359 Reflection waterfallrefraction;
360 GLuint reflectionfb = 0, reflectiondb = 0;
361 
getwaterfalltex()362 GLuint getwaterfalltex() { return waterfallrefraction.refracttex ? waterfallrefraction.refracttex : notexture->id; }
363 
364 VAR(oqwater, 0, 1, 1);
365 
366 extern int oqfrags;
367 
renderwaterff()368 void renderwaterff()
369 {
370 	glDisable(GL_CULL_FACE);
371 
372     if(!nowater && (waterreflect || waterrefract || (waterenvmap && hasCM)))
373     {
374         if(waterrefract) setuprefractTMUs();
375         else setupreflectTMUs();
376 
377         glMatrixMode(GL_TEXTURE);
378     }
379     else
380     {
381         glDisable(GL_TEXTURE_2D);
382         glDepthMask(GL_FALSE);
383         glEnable(GL_BLEND);
384         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
385     }
386 
387     float offset = -WATER_OFFSET;
388 
389 	loopi(3) wcol[i] = watercol[i]/255.0f;
390 
391     bool wasbelow = false;
392 	loopi(MAXREFLECTIONS)
393 	{
394 		Reflection &ref = reflections[i];
395 		if(ref.height<0 || ref.lastused<totalmillis || ref.matsurfs.empty()) continue;
396 
397         bool below = camera1->o.z < ref.height + offset;
398         if(!nowater && (waterrefract || waterreflect || (waterenvmap && hasCM)))
399         {
400             if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
401             bool projtex = false;
402             if(waterreflect || (waterenvmap && hasCM))
403             {
404                 bool tmu1 = waterrefract && (!below || !wasbelow);
405                 if(tmu1) glActiveTexture_(GL_TEXTURE1_ARB);
406                 if(!below)
407                 {
408                     if(wasbelow)
409                     {
410                         wasbelow = false;
411                         glEnable(waterreflect ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB);
412                         if(!waterrefract) glBlendFunc(GL_ONE, GL_SRC_ALPHA);
413                     }
414                     if(waterreflect)
415                     {
416                         glBindTexture(GL_TEXTURE_2D, ref.tex);
417                         setprojtexmatrix(ref);
418                         projtex = true;
419                     }
420                 }
421                 else if(!wasbelow)
422                 {
423                     wasbelow = true;
424                     glDisable(waterreflect ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB);
425                     if(!waterrefract) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
426                 }
427                 if(tmu1) glActiveTexture_(GL_TEXTURE0_ARB);
428             }
429             if(waterrefract)
430             {
431                 glBindTexture(GL_TEXTURE_2D, ref.refracttex);
432                 setprojtexmatrix(ref, !projtex);
433             }
434         }
435 
436         int lastdepth = -1;
437         bool begin = false;
438         loopvj(ref.matsurfs)
439         {
440             materialsurface &m = *ref.matsurfs[j];
441 
442             if(m.depth!=lastdepth)
443             {
444                 float depth = !waterfog ? 1.0f : min(0.75f*m.depth/waterfog, 0.95f);
445                 if(nowater || !waterrefract) depth = max(depth, nowater || (!waterreflect && (!waterenvmap || !hasCM)) || below ? 0.6f : 0.3f);
446                 wcol[3] = depth;
447                 if(!nowater && (waterrefract || ((waterreflect || (waterenvmap && hasCM)) && !below)))
448                 {
449                     if(begin) { glEnd(); begin = false; }
450                     float ec[4] = { wcol[0], wcol[1], wcol[2], depth };
451                     if(!waterrefract) { loopk(3) ec[k] *= depth; ec[3] = 1-ec[3]; }
452                     colortmu(0, ec[0], ec[1], ec[2], ec[3]);
453                 }
454                 lastdepth = m.depth;
455             }
456 
457             if(!vertwater)
458             {
459                 if(!begin) { glBegin(GL_QUADS); begin = true; }
460                 renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize);
461             }
462             else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize) >= (uint)m.csize * 2)
463                 rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize);
464         }
465 		if(begin) glEnd();
466 	}
467 
468     if(!nowater && (waterrefract || waterreflect || (waterenvmap && hasCM)))
469     {
470         if(!waterrefract && (wasbelow || !waterreflect))
471         {
472             if(!waterreflect && !wasbelow) glDisable(GL_TEXTURE_CUBE_MAP_ARB);
473             glEnable(GL_TEXTURE_2D);
474         }
475         cleanupwaterTMUs(waterrefract!=0);
476         glLoadIdentity();
477         glMatrixMode(GL_MODELVIEW);
478     }
479     else
480 	{
481 		glEnable(GL_TEXTURE_2D);
482 		glDepthMask(GL_TRUE);
483 		glDisable(GL_BLEND);
484 	}
485 
486 	glEnable(GL_CULL_FACE);
487 }
488 
489 VARFP(waterfade, 0, 1, 1, { cleanreflections(); preloadwatershaders(); });
490 
preloadwatershaders(bool force)491 void preloadwatershaders(bool force)
492 {
493     static bool needwater = false;
494     if(force) needwater = true;
495     if(!needwater) return;
496 
497     useshaderbyname("waterglare");
498 
499     if(waterenvmap && !waterreflect && hasCM)
500         useshaderbyname(waterrefract ? (waterfade && hasFBO ? "waterenvfade" : "waterenvrefract") : "waterenv");
501     else useshaderbyname(waterrefract ? (waterfade && hasFBO ? "waterfade" : "waterrefract") : (waterreflect ? "waterreflect" : "water"));
502 
503     useshaderbyname(waterrefract ? (waterfade && hasFBO ? "underwaterfade" : "underwaterrefract") : "underwater");
504 
505     extern int waterfallenv;
506     if(waterfallenv && hasCM) useshaderbyname("waterfallenv");
507     if(waterfallrefract) useshaderbyname(waterfallenv && hasCM ? "waterfallenvrefract" : "waterfallrefract");
508 }
509 
renderwater()510 void renderwater()
511 {
512     if(editmode && showmat && !envmapping) return;
513 	if(!rplanes) return;
514 
515 	if(renderpath==R_FIXEDFUNCTION) { renderwaterff(); return; }
516 
517 	glDisable(GL_CULL_FACE);
518 
519 	uchar wcol[3] = { watercolour>>16, (watercolour>>8)&0xFF, watercolour&0xFF };
520 	glColor3ubv(wcol);
521 
522 	Slot &s = lookupmaterialslot(MAT_WATER);
523 
524 	glActiveTexture_(GL_TEXTURE1_ARB);
525 	glEnable(GL_TEXTURE_2D);
526     glBindTexture(GL_TEXTURE_2D, s.sts.inrange(2) ? s.sts[2].t->id : notexture->id);
527 	glActiveTexture_(GL_TEXTURE2_ARB);
528 	glEnable(GL_TEXTURE_2D);
529     glBindTexture(GL_TEXTURE_2D, s.sts.inrange(3) ? s.sts[3].t->id : notexture->id);
530 
531     if(!glaring)
532     {
533         if(waterrefract)
534         {
535             glActiveTexture_(GL_TEXTURE3_ARB);
536             glEnable(GL_TEXTURE_2D);
537             if(waterfade && hasFBO)
538             {
539                 glEnable(GL_BLEND);
540                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
541             }
542         }
543         else
544         {
545             glDepthMask(GL_FALSE);
546             glEnable(GL_BLEND);
547             glBlendFunc(GL_ONE, GL_SRC_ALPHA);
548         }
549     }
550 	glActiveTexture_(GL_TEXTURE0_ARB);
551 
552     if(!glaring && waterenvmap && !waterreflect && hasCM)
553     {
554         glDisable(GL_TEXTURE_2D);
555         glEnable(GL_TEXTURE_CUBE_MAP_ARB);
556         glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, lookupenvmap(s));
557     }
558 
559 	setenvparamf("camera", SHPARAM_VERTEX, 0, camera1->o.x, camera1->o.y, camera1->o.z);
560 	setenvparamf("millis", SHPARAM_VERTEX, 1, lastmillis/1000.0f, lastmillis/1000.0f, lastmillis/1000.0f);
561 
562     #define SETWATERSHADER(which, name) \
563     do { \
564         static Shader *name##shader = NULL; \
565         if(!name##shader) name##shader = lookupshaderbyname(#name); \
566         which##shader = name##shader; \
567     } while(0)
568 
569     Shader *aboveshader = NULL;
570     if(glaring) SETWATERSHADER(above, waterglare);
571     else if(waterenvmap && !waterreflect && hasCM)
572     {
573         if(waterrefract)
574         {
575             if(waterfade && hasFBO) SETWATERSHADER(above, waterenvfade);
576             else SETWATERSHADER(above, waterenvrefract);
577         }
578         else SETWATERSHADER(above, waterenv);
579     }
580     else if(waterrefract)
581     {
582         if(waterfade && hasFBO) SETWATERSHADER(above, waterfade);
583         else SETWATERSHADER(above, waterrefract);
584     }
585     else if(waterreflect) SETWATERSHADER(above, waterreflect);
586     else SETWATERSHADER(above, water);
587 
588     Shader *belowshader = NULL;
589     if(!glaring)
590     {
591         if(waterrefract)
592         {
593             if(waterfade && hasFBO) SETWATERSHADER(below, underwaterfade);
594             else SETWATERSHADER(below, underwaterrefract);
595         }
596         else SETWATERSHADER(below, underwater);
597 
598         if(waterreflect || waterrefract) glMatrixMode(GL_TEXTURE);
599     }
600 
601 	vec amb(max(skylightcolor[0], ambientcolor[0]), max(skylightcolor[1], ambientcolor[1]), max(skylightcolor[2], ambientcolor[2]));
602     float offset = -WATER_OFFSET;
603 	loopi(MAXREFLECTIONS)
604 	{
605 		Reflection &ref = reflections[i];
606 		if(ref.height<0 || ref.lastused<totalmillis || ref.matsurfs.empty()) continue;
607         if(!glaring && hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
608 
609         bool below = camera1->o.z < ref.height+offset;
610         if(below)
611         {
612             if(!belowshader) continue;
613             belowshader->set();
614         }
615         else aboveshader->set();
616 
617         if(!glaring)
618         {
619             if(waterreflect || waterrefract)
620             {
621                 if(waterreflect || !waterenvmap || !hasCM) glBindTexture(GL_TEXTURE_2D, waterreflect ? ref.tex : ref.refracttex);
622                 setprojtexmatrix(ref);
623             }
624 
625             if(waterrefract)
626             {
627                 glActiveTexture_(GL_TEXTURE3_ARB);
628                 glBindTexture(GL_TEXTURE_2D, ref.refracttex);
629                 glActiveTexture_(GL_TEXTURE0_ARB);
630                 if(waterfade)
631                 {
632                     float fadeheight = ref.height+offset+(below ? -2 : 2);
633                     setlocalparamf("waterheight", SHPARAM_VERTEX, 7, fadeheight, fadeheight, fadeheight);
634                 }
635             }
636         }
637 
638         const extentity *lastlight = (const extentity *)-1;
639         int lastdepth = -1;
640 		bool begin = false;
641 		loopvj(ref.matsurfs)
642 		{
643 			materialsurface &m = *ref.matsurfs[j];
644 
645 			const extentity *light = (m.light && m.light->type==ET_LIGHT ? m.light : NULL);
646 			if(light!=lastlight)
647 			{
648 				if(begin) { glEnd(); begin = false; }
649 				const vec &lightpos = light ? light->o : vec(hdr.worldsize/2, hdr.worldsize/2, hdr.worldsize);
650 				float lightrad = light && light->type == ET_LIGHT && light->attrs[0] ? light->attrs[0] : hdr.worldsize*8.0f;
651 				const vec &lightcol = (light ? (light->type == ET_LIGHT ? vec(light->attrs[1], light->attrs[2], light->attrs[3]) : vec(light->attrs[2], light->attrs[3], light->attrs[4])) : vec(amb)).div(255.0f).mul(waterspec/100.0f);
652 				setlocalparamf("lightpos", SHPARAM_VERTEX, 2, lightpos.x, lightpos.y, lightpos.z);
653 				setlocalparamf("lightcolor", SHPARAM_PIXEL, 3, lightcol.x, lightcol.y, lightcol.z);
654 				setlocalparamf("lightradius", SHPARAM_PIXEL, 4, lightrad, lightrad, lightrad);
655 				lastlight = light;
656 			}
657 
658 			if(!glaring && !waterrefract && m.depth!=lastdepth)
659 			{
660 				if(begin) { glEnd(); begin = false; }
661 				float depth = !waterfog ? 1.0f : min(0.75f*m.depth/waterfog, 0.95f);
662                 depth = max(depth, !below && (waterreflect || (waterenvmap && hasCM)) ? 0.3f : 0.6f);
663 				setlocalparamf("depth", SHPARAM_PIXEL, 5, depth, 1.0f-depth);
664 				lastdepth = m.depth;
665 			}
666 
667 			if(!vertwater)
668 			{
669 				if(!begin) { glBegin(GL_QUADS); begin = true; }
670 				renderflatwater(m.o.x, m.o.y, m.o.z, m.rsize, m.csize);
671 			}
672 			else if(renderwaterlod(m.o.x, m.o.y, m.o.z, m.csize) >= (uint)m.csize * 2)
673 				rendervertwater(m.csize, m.o.x, m.o.y, m.o.z, m.csize);
674 		}
675 		if(begin) glEnd();
676 	}
677 
678     if(!glaring)
679     {
680         if(waterreflect || waterrefract)
681         {
682             glLoadIdentity();
683             glMatrixMode(GL_MODELVIEW);
684         }
685         if(waterrefract)
686         {
687             glActiveTexture_(GL_TEXTURE3_ARB);
688             glDisable(GL_TEXTURE_2D);
689             if(hasFBO && renderpath!=R_FIXEDFUNCTION && waterfade) glDisable(GL_BLEND);
690         }
691         else
692         {
693             glDepthMask(GL_TRUE);
694             glDisable(GL_BLEND);
695         }
696     }
697 
698 	loopi(2)
699 	{
700 		glActiveTexture_(GL_TEXTURE1_ARB+i);
701 		glDisable(GL_TEXTURE_2D);
702 	}
703 	glActiveTexture_(GL_TEXTURE0_ARB);
704 
705     if(!glaring && waterenvmap && !waterreflect && hasCM)
706     {
707         glDisable(GL_TEXTURE_CUBE_MAP_ARB);
708         glEnable(GL_TEXTURE_2D);
709     }
710 
711 	glEnable(GL_CULL_FACE);
712 }
713 
setupwaterfallrefract(GLenum tmu1,GLenum tmu2)714 void setupwaterfallrefract(GLenum tmu1, GLenum tmu2)
715 {
716     glActiveTexture_(tmu1);
717     glBindTexture(GL_TEXTURE_2D, waterfallrefraction.refracttex ? waterfallrefraction.refracttex : notexture->id);
718     glActiveTexture_(tmu2);
719     glMatrixMode(GL_TEXTURE);
720     setprojtexmatrix(waterfallrefraction);
721     glMatrixMode(GL_MODELVIEW);
722 }
723 
findreflection(int height)724 Reflection *findreflection(int height)
725 {
726 	loopi(MAXREFLECTIONS)
727 	{
728 		if(reflections[i].height==height) return &reflections[i];
729 	}
730 	return NULL;
731 }
732 
cleanreflection(Reflection & ref)733 void cleanreflection(Reflection &ref)
734 {
735     ref.height = -1;
736     ref.lastupdate = 0;
737     ref.query = NULL;
738     if(ref.tex)
739     {
740         glDeleteTextures(1, &ref.tex);
741         ref.tex = 0;
742     }
743     if(ref.refracttex)
744     {
745         glDeleteTextures(1, &ref.refracttex);
746         ref.refracttex = 0;
747     }
748 }
749 
cleanreflections()750 void cleanreflections()
751 {
752     loopi(MAXREFLECTIONS) cleanreflection(reflections[i]);
753     cleanreflection(waterfallrefraction);
754     if(reflectionfb)
755     {
756         glDeleteFramebuffers_(1, &reflectionfb);
757         reflectionfb = 0;
758     }
759     if(reflectiondb)
760     {
761         glDeleteRenderbuffers_(1, &reflectiondb);
762         reflectiondb = 0;
763     }
764 }
765 
766 VARFP(reflectsize, 6, 8, 10, cleanreflections());
767 
genwatertex(GLuint & tex,GLuint & fb,GLuint & db,bool refract=false)768 void genwatertex(GLuint &tex, GLuint &fb, GLuint &db, bool refract = false)
769 {
770     static const GLenum colorfmts[] = { GL_RGBA, GL_RGBA8, GL_RGB, GL_RGB8, GL_FALSE },
771                         depthfmts[] = { GL_DEPTH_STENCIL_EXT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_FALSE };
772     const int stencilfmts = 2;
773     static GLenum reflectfmt = GL_FALSE, refractfmt = GL_FALSE, depthfmt = GL_FALSE, stencilfmt = GL_FALSE;
774     static bool usingalpha = false;
775     bool needsalpha = refract && hasFBO && renderpath!=R_FIXEDFUNCTION && waterrefract && waterfade;
776     if(refract && usingalpha!=needsalpha)
777     {
778         usingalpha = needsalpha;
779         refractfmt = GL_FALSE;
780     }
781     int size = 1<<reflectsize;
782     if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
783     while(size>hwtexsize) size /= 2;
784 
785     glGenTextures(1, &tex);
786     char *buf = new char[size*size*4];
787     memset(buf, 0, size*size*4);
788 
789     GLenum &colorfmt = refract ? refractfmt : reflectfmt;
790     if(colorfmt && (!hasFBO || (fb && db)))
791     {
792         createtexture(tex, size, size, buf, 3, 1, colorfmt);
793         delete[] buf;
794         return;
795     }
796 
797     if(hasFBO)
798     {
799         if(!fb) glGenFramebuffers_(1, &fb);
800         glBindFramebuffer_(GL_FRAMEBUFFER_EXT, fb);
801     }
802 
803     int find = needsalpha ? 0 : 2;
804     do
805     {
806         createtexture(tex, size, size, buf, 3, 1, colorfmt ? colorfmt : colorfmts[find]);
807         if(!hasFBO) break;
808         else
809         {
810             glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
811             if(glCheckFramebufferStatus_(GL_FRAMEBUFFER_EXT)==GL_FRAMEBUFFER_COMPLETE_EXT) break;
812         }
813     }
814     while(!colorfmt && colorfmts[++find]);
815     if(!colorfmt) colorfmt = colorfmts[find];
816 
817     delete[] buf;
818 
819     if(hasFBO)
820     {
821         if(!db) { glGenRenderbuffers_(1, &db); depthfmt = stencilfmt = GL_FALSE; }
822         if(!depthfmt) glBindRenderbuffer_(GL_RENDERBUFFER_EXT, db);
823         find = hasstencil && hasDS ? 0 : stencilfmts;
824         do
825         {
826             if(!depthfmt) glRenderbufferStorage_(GL_RENDERBUFFER_EXT, depthfmts[find], size, size);
827             glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, db);
828             if(depthfmt ? stencilfmt : find<stencilfmts) glFramebufferRenderbuffer_(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, db);
829             if(glCheckFramebufferStatus_(GL_FRAMEBUFFER_EXT)==GL_FRAMEBUFFER_COMPLETE_EXT) break;
830         }
831         while(!depthfmt && depthfmts[++find]);
832         if(!depthfmt)
833         {
834             glBindRenderbuffer_(GL_RENDERBUFFER_EXT, 0);
835             depthfmt = depthfmts[find];
836             stencilfmt = find<stencilfmts ? depthfmt : GL_FALSE;
837         }
838 
839         glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
840     }
841 }
842 
addwaterfallrefraction(materialsurface & m)843 void addwaterfallrefraction(materialsurface &m)
844 {
845     Reflection &ref = waterfallrefraction;
846     if(ref.lastused!=totalmillis)
847     {
848         ref.lastused = totalmillis;
849         ref.matsurfs.setsizenodelete(0);
850         ref.height = INT_MAX;
851     }
852     ref.matsurfs.add(&m);
853 
854     if(!ref.refracttex) genwatertex(ref.refracttex, reflectionfb, reflectiondb);
855 }
856 
addreflection(materialsurface & m)857 void addreflection(materialsurface &m)
858 {
859 	int height = m.o.z;
860 	Reflection *ref = NULL, *oldest = NULL;
861 	loopi(MAXREFLECTIONS)
862 	{
863 		Reflection &r = reflections[i];
864 		if(r.height<0)
865 		{
866 			if(!ref) ref = &r;
867 		}
868 		else if(r.height==height)
869 		{
870 			r.matsurfs.add(&m);
871             r.depth = max(r.depth, int(m.depth));
872 			if(r.lastused==totalmillis) return;
873 			ref = &r;
874 			break;
875 		}
876 		else if(!oldest || r.lastused<oldest->lastused) oldest = &r;
877 	}
878 	if(!ref)
879 	{
880 		if(!oldest || oldest->lastused==totalmillis) return;
881 		ref = oldest;
882 	}
883 	if(ref->height!=height) ref->height = height;
884 	rplanes++;
885 	ref->lastused = totalmillis;
886 	ref->matsurfs.setsizenodelete(0);
887 	ref->matsurfs.add(&m);
888     ref->depth = m.depth;
889 	if(nowater) return;
890 
891     if(waterreflect && !ref->tex) genwatertex(ref->tex, reflectionfb, reflectiondb);
892     if(waterrefract && !ref->refracttex) genwatertex(ref->refracttex, reflectionfb, reflectiondb, true);
893 }
894 
895 extern vtxarray *visibleva;
896 extern void drawreflection(float z, bool refract, bool clear);
897 
898 int rplanes = 0;
899 
900 static int lastquery = 0;
901 
queryreflection(Reflection & ref,bool init)902 void queryreflection(Reflection &ref, bool init)
903 {
904     if(init)
905     {
906         nocolorshader->set();
907         glDepthMask(GL_FALSE);
908         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
909         glDisable(GL_CULL_FACE);
910     }
911     startquery(ref.query);
912     glBegin(GL_QUADS);
913     loopvj(ref.matsurfs)
914     {
915         materialsurface &m = *ref.matsurfs[j];
916         float offset = 0.1f;
917         if(m.orient==O_TOP)
918         {
919             offset = WATER_OFFSET +
920                 (vertwater ? WATER_AMPLITUDE*(camera1->pitch > 0 || m.depth < WATER_AMPLITUDE+0.5f ? -1 : 1) : 0);
921             if(fabs(m.o.z-offset - camera1->o.z) < 0.5f && m.depth > WATER_AMPLITUDE+1.5f)
922                 offset += camera1->pitch > 0 ? -1 : 1;
923         }
924         drawmaterial(m.orient, m.o.x, m.o.y, m.o.z, m.csize, m.rsize, offset);
925     }
926     glEnd();
927     endquery(ref.query);
928 }
929 
queryreflections()930 void queryreflections()
931 {
932 	rplanes = 0;
933 
934 	static int lastsize = 0;
935     int size = 1<<reflectsize;
936 	if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
937     while(size>hwtexsize) size /= 2;
938 	if(size!=lastsize) { if(lastsize) cleanreflections(); lastsize = size; }
939 
940     bool shouldrefract = waterfallrefract && renderpath!=R_FIXEDFUNCTION;
941     for(vtxarray *va = visibleva; va; va = va->next)
942     {
943         if(!va->matsurfs || va->occluded >= OCCLUDE_BB || va->curvfc >= VFC_FOGGED) continue;
944         loopi(va->matsurfs)
945         {
946             materialsurface &m = va->matbuf[i];
947             if(m.material==MAT_WATER)
948             {
949                 if(m.orient==O_TOP) addreflection(m);
950                 else if(m.orient!=O_BOTTOM && shouldrefract) addwaterfallrefraction(m);
951             }
952         }
953     }
954 
955     loopi(MAXREFLECTIONS)
956     {
957         Reflection &ref = reflections[i];
958         if(ref.height>=0 && ref.lastused>=totalmillis && ref.matsurfs.length())
959         {
960             if(waterpvsoccluded(ref.height)) ref.matsurfs.setsizenodelete(0);
961         }
962     }
963     if(renderpath!=R_FIXEDFUNCTION && waterfallrefract)
964     {
965         Reflection &ref = waterfallrefraction;
966         if(ref.height>=0 && ref.lastused>=totalmillis && ref.matsurfs.length())
967         {
968             if(waterpvsoccluded(-1)) ref.matsurfs.setsizenodelete(0);
969         }
970     }
971 
972 	lastquery = totalmillis;
973 
974     if((editmode && showmat && !envmapping) || !hasOQ || !oqfrags || !oqwater || nowater) return;
975 
976 	int refs = 0;
977     if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
978     {
979         Reflection &ref = reflections[i];
980         ref.query = ref.height>=0 && ref.lastused>=totalmillis && ref.matsurfs.length() ? newquery(&ref) : NULL;
981         if(ref.query) queryreflection(ref, !refs++);
982     }
983     if(renderpath!=R_FIXEDFUNCTION && waterfallrefract)
984     {
985         Reflection &ref = waterfallrefraction;
986         ref.query = ref.height>=0 && ref.lastused>=totalmillis && ref.matsurfs.length() ? newquery(&ref) : NULL;
987         if(ref.query) queryreflection(ref, !refs++);
988     }
989 
990 	if(refs)
991 	{
992 		defaultshader->set();
993 		glDepthMask(GL_TRUE);
994 		glColorMask(COLORMASK, GL_TRUE);
995 		glEnable(GL_CULL_FACE);
996 	}
997 }
998 
999 VARA(maxreflect, 1, 1, 8);
1000 
1001 int refracting = 0;
1002 bool reflecting = false, fading = false, fogging = false;
1003 float reflectz = 1e16f;
1004 
1005 VAR(maskreflect, 0, 2, 16);
1006 
maskreflection(Reflection & ref,float offset,bool reflect)1007 void maskreflection(Reflection &ref, float offset, bool reflect)
1008 {
1009 	if(!maskreflect)
1010 	{
1011 		glClear(GL_DEPTH_BUFFER_BIT | (hasstencil && hasDS ? GL_STENCIL_BUFFER_BIT : 0));
1012 		return;
1013 	}
1014 	glClearDepth(0);
1015 	glClear(GL_DEPTH_BUFFER_BIT | (hasstencil && hasDS ? GL_STENCIL_BUFFER_BIT : 0));
1016 	glClearDepth(1);
1017 	glDepthRange(1, 1);
1018 	glDepthFunc(GL_ALWAYS);
1019 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1020 	glDisable(GL_TEXTURE_2D);
1021 	glDisable(GL_CULL_FACE);
1022 	nocolorshader->set();
1023 	if(reflect)
1024 	{
1025 		glPushMatrix();
1026 		glTranslatef(0, 0, 2*(ref.height+offset));
1027 		glScalef(1, 1, -1);
1028 	}
1029 	int border = maskreflect;
1030 	glBegin(GL_QUADS);
1031 	loopv(ref.matsurfs)
1032 	{
1033 		materialsurface &m = *ref.matsurfs[i];
1034 		ivec o(m.o);
1035 		o[R[dimension(m.orient)]] -= border;
1036 		o[C[dimension(m.orient)]] -= border;
1037 		drawmaterial(m.orient, o.x, o.y, o.z, m.csize+2*border, m.rsize+2*border, -offset);
1038 	}
1039 	glEnd();
1040 	if(reflect) glPopMatrix();
1041 	defaultshader->set();
1042 	glEnable(GL_CULL_FACE);
1043 	glEnable(GL_TEXTURE_2D);
1044 	glColorMask(COLORMASK, GL_TRUE);
1045 	glDepthFunc(GL_LESS);
1046 	glDepthRange(0, 1);
1047 }
1048 
1049 VAR(reflectscissor, 0, 1, 1);
1050 VAR(reflectvfc, 0, 1, 1);
1051 
calcscissorbox(Reflection & ref,int size,float & minyaw,float & maxyaw,float & minpitch,float & maxpitch,int & sx,int & sy,int & sw,int & sh)1052 static bool calcscissorbox(Reflection &ref, int size, float &minyaw, float &maxyaw, float &minpitch, float &maxpitch, int &sx, int &sy, int &sw, int &sh)
1053 {
1054     materialsurface &m0 = *ref.matsurfs[0];
1055     int dim = dimension(m0.orient), r = R[dim], c = C[dim];
1056     ivec bbmin = m0.o, bbmax = bbmin;
1057     bbmax[r] += m0.rsize;
1058     bbmax[c] += m0.csize;
1059     loopvj(ref.matsurfs)
1060     {
1061         materialsurface &m = *ref.matsurfs[j];
1062         bbmin[r] = min(bbmin[r], m.o[r]);
1063         bbmin[c] = min(bbmin[c], m.o[c]);
1064         bbmax[r] = max(bbmax[r], m.o[r] + m.rsize);
1065         bbmax[c] = max(bbmax[c], m.o[c] + m.csize);
1066         bbmin[dim] = min(bbmin[dim], m.o[dim]);
1067         bbmax[dim] = max(bbmax[dim], m.o[dim]);
1068     }
1069 
1070     vec4 v[8];
1071     float sx1 = 1, sy1 = 1, sx2 = -1, sy2 = -1;
1072     loopi(8)
1073     {
1074         vec4 &p = v[i];
1075         mvpmatrix.transform(vec(i&1 ? bbmax.x : bbmin.x, i&2 ? bbmax.y : bbmin.y, (i&4 ? bbmax.z + WATER_AMPLITUDE : bbmin.z - WATER_AMPLITUDE) - WATER_OFFSET), p);
1076         if(p.z >= 0)
1077         {
1078             float x = p.x / p.w, y = p.y / p.w;
1079             sx1 = min(sx1, x);
1080             sy1 = min(sy1, y);
1081             sx2 = max(sx2, x);
1082             sy2 = max(sy2, y);
1083         }
1084     }
1085     if(sx1 >= sx2 || sy1 >= sy2) return false;
1086     loopi(8)
1087     {
1088         const vec4 &p = v[i];
1089         if(p.z >= 0) continue;
1090         loopj(3)
1091         {
1092             const vec4 &o = v[i^(1<<j)];
1093             if(o.z <= 0) continue;
1094             float t = p.z/(p.z - o.z),
1095                   w = p.w + t*(o.w - p.w),
1096                   x = (p.x + t*(o.x - p.x))/w,
1097                   y = (p.y + t*(o.y - p.y))/w;
1098             sx1 = min(sx1, x);
1099             sy1 = min(sy1, y);
1100             sx2 = max(sx2, x);
1101             sy2 = max(sy2, y);
1102         }
1103     }
1104     sx1 = max(sx1, -1.0f);
1105     sy1 = max(sy1, -1.0f);
1106     sx2 = min(sx2, 1.0f);
1107     sy2 = min(sy2, 1.0f);
1108     if(reflectvfc)
1109     {
1110         minyaw = atan2(sx1, projmatrix[0]);
1111         maxyaw = atan2(sx2, projmatrix[0]);
1112         minpitch = atan2(sy1, projmatrix[5]);
1113         maxpitch = atan2(sy2, projmatrix[5]);
1114     }
1115     sx = int(floor((hasFBO ? 0 : screen->w-size) + (sx1+1)*0.5f*size));
1116     sy = int(floor((hasFBO ? 0 : screen->h-size) + (sy1+1)*0.5f*size));
1117     sw = max(int(ceil((hasFBO ? 0 : screen->w-size) + (sx2+1)*0.5f*size)) - sx, 0);
1118     sh = max(int(ceil((hasFBO ? 0 : screen->h-size) + (sy2+1)*0.5f*size)) - sy, 0);
1119     return sx1 > -1 || sy1 > -1 || sx2 < 1 || sy2 < 1;
1120 }
1121 
drawreflections()1122 void drawreflections()
1123 {
1124     if((editmode && showmat && !envmapping) || nowater) return;
1125 
1126     extern int nvidia_scissor_bug;
1127 
1128 	static int lastdrawn = 0;
1129 	int refs = 0, n = lastdrawn;
1130     float offset = -WATER_OFFSET;
1131     int size = 1<<reflectsize;
1132     if(!hasFBO) while(size>screen->w || size>screen->h) size /= 2;
1133     while(size>hwtexsize) size /= 2;
1134 
1135     if(waterreflect || waterrefract) loopi(MAXREFLECTIONS)
1136 	{
1137 		Reflection &ref = reflections[++n%MAXREFLECTIONS];
1138 		if(ref.height<0 || ref.lastused<lastquery || ref.matsurfs.empty()) continue;
1139 		if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) continue;
1140 
1141         if(!refs)
1142         {
1143             glViewport(hasFBO ? 0 : screen->w-size, hasFBO ? 0 : screen->h-size, size, size);
1144             if(hasFBO) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, reflectionfb);
1145         }
1146         refs++;
1147         ref.lastupdate = totalmillis;
1148         lastdrawn = n;
1149 
1150         float minyaw = -M_PI, maxyaw = M_PI, minpitch = -M_PI, maxpitch = M_PI;
1151         int sx, sy, sw, sh;
1152         bool scissor = reflectscissor && calcscissorbox(ref, size, minyaw, maxyaw, minpitch, maxpitch, sx, sy, sw, sh);
1153         if(scissor) glScissor(sx, sy, sw, sh);
1154         else
1155         {
1156             sx = hasFBO ? 0 : screen->w-size;
1157             sy = hasFBO ? 0 : screen->h-size;
1158             sw = sh = size;
1159         }
1160 
1161         if(waterreflect && ref.tex && camera1->o.z >= ref.height+offset)
1162         {
1163             if(hasFBO) glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ref.tex, 0);
1164             if(scissor && !nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1165             maskreflection(ref, offset, true);
1166             if(scissor && nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1167             reflectvfcP(ref.height+offset, minyaw, maxyaw, minpitch, maxpitch);
1168             drawreflection(ref.height+offset, false, false);
1169             restorevfcP();
1170             if(scissor) glDisable(GL_SCISSOR_TEST);
1171             if(!hasFBO)
1172             {
1173                 glBindTexture(GL_TEXTURE_2D, ref.tex);
1174                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, sx-(screen->w-size), sy-(screen->h-size), sx, sy, sw, sh);
1175             }
1176         }
1177 
1178         if(waterrefract && ref.refracttex)
1179         {
1180             if(hasFBO) glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ref.refracttex, 0);
1181             if(scissor && !nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1182             maskreflection(ref, offset, false);
1183             if(scissor && nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1184             reflectvfcP(-1, minyaw, maxyaw, minpitch, maxpitch);
1185             drawreflection(ref.height+offset, true, ref.depth>=10000);
1186             restorevfcP();
1187             if(scissor) glDisable(GL_SCISSOR_TEST);
1188             if(!hasFBO)
1189             {
1190                 glBindTexture(GL_TEXTURE_2D, ref.refracttex);
1191                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, sx-(screen->w-size), sy-(screen->h-size), sx, sy, sw, sh);
1192             }
1193         }
1194 
1195 		if(refs>=maxreflect) break;
1196 	}
1197 
1198     if(renderpath!=R_FIXEDFUNCTION && waterfallrefract && waterfallrefraction.refracttex)
1199     {
1200         Reflection &ref = waterfallrefraction;
1201 
1202         if(ref.height<0 || ref.lastused<lastquery || ref.matsurfs.empty()) goto nowaterfall;
1203         if(hasOQ && oqfrags && oqwater && ref.query && ref.query->owner==&ref && checkquery(ref.query)) goto nowaterfall;
1204 
1205         if(!refs)
1206         {
1207             glViewport(hasFBO ? 0 : screen->w-size, hasFBO ? 0 : screen->h-size, size, size);
1208             if(hasFBO) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, reflectionfb);
1209         }
1210         refs++;
1211         ref.lastupdate = totalmillis;
1212 
1213         float minyaw = -M_PI, maxyaw = M_PI, minpitch = -M_PI, maxpitch = M_PI;
1214         int sx, sy, sw, sh;
1215         bool scissor = reflectscissor && calcscissorbox(ref, size, minyaw, maxyaw, minpitch, maxpitch, sx, sy, sw, sh);
1216         if(scissor) glScissor(sx, sy, sw, sh);
1217         else
1218         {
1219             sx = hasFBO ? 0 : screen->w-size;
1220             sy = hasFBO ? 0 : screen->h-size;
1221             sw = sh = size;
1222         }
1223 
1224         if(hasFBO) glFramebufferTexture2D_(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ref.refracttex, 0);
1225         if(scissor && !nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1226         maskreflection(ref, -0.1f, false);
1227         if(scissor && nvidia_scissor_bug) glEnable(GL_SCISSOR_TEST);
1228         reflectvfcP(-1, minyaw, maxyaw, minpitch, maxpitch);
1229         drawreflection(-1, true, false);
1230         restorevfcP();
1231         if(scissor) glDisable(GL_SCISSOR_TEST);
1232         if(!hasFBO)
1233         {
1234             glBindTexture(GL_TEXTURE_2D, ref.refracttex);
1235             glCopyTexSubImage2D(GL_TEXTURE_2D, 0, sx-(screen->w-size), sy-(screen->h-size), sx, sy, sw, sh);
1236         }
1237     }
1238 nowaterfall:
1239 
1240 	if(!refs) return;
1241 	glViewport(0, 0, screen->w, screen->h);
1242 	if(hasFBO) glBindFramebuffer_(GL_FRAMEBUFFER_EXT, 0);
1243 
1244 	defaultshader->set();
1245 }
1246 
1247