1 // renderva.cpp: handles the occlusion and rendering of vertex arrays
2 
3 #include "engine.h"
4 
drawtris(GLsizei numindices,const GLvoid * indices,ushort minvert,ushort maxvert)5 static inline void drawtris(GLsizei numindices, const GLvoid *indices, ushort minvert, ushort maxvert)
6 {
7     if(hasDRE) glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, numindices, GL_UNSIGNED_SHORT, indices);
8     else glDrawElements(GL_TRIANGLES, numindices, GL_UNSIGNED_SHORT, indices);
9     glde++;
10 }
11 
drawvatris(vtxarray * va,GLsizei numindices,const GLvoid * indices)12 static inline void drawvatris(vtxarray *va, GLsizei numindices, const GLvoid *indices)
13 {
14     drawtris(numindices, indices, va->minvert, va->maxvert);
15 }
16 
17 ///////// view frustrum culling ///////////////////////
18 
19 plane vfcP[5];  // perpindictular vectors to view frustrum bounding planes
20 float vfcDfog;  // far plane culling distance (fog limit).
21 float vfcDnear[5], vfcDfar[5];
22 float vfcfov, vfcfovy;
23 
24 vtxarray *visibleva;
25 
isfoggedsphere(float rad,const vec & cv)26 bool isfoggedsphere(float rad, const vec &cv)
27 {
28     loopi(4) if(vfcP[i].dist(cv) < -rad) return true;
29     float dist = vfcP[4].dist(cv);
30     return dist < -rad || dist > vfcDfog + rad;
31 }
32 
isvisiblesphere(float rad,const vec & cv)33 int isvisiblesphere(float rad, const vec &cv)
34 {
35 	int v = VFC_FULL_VISIBLE;
36 	float dist;
37 
38 	loopi(5)
39 	{
40 		dist = vfcP[i].dist(cv);
41 		if(dist < -rad) return VFC_NOT_VISIBLE;
42 		if(dist < rad) v = VFC_PART_VISIBLE;
43 	}
44 
45 	dist -= vfcDfog;
46 	if(dist > rad) return VFC_FOGGED;  //VFC_NOT_VISIBLE;	// culling when fog is closer than size of world results in HOM
47 	if(dist > -rad) v = VFC_PART_VISIBLE;
48 
49 	return v;
50 }
51 
ishiddencube(const ivec & o,int size)52 static inline int ishiddencube(const ivec &o, int size)
53 {
54     loopi(5) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
55     return false;
56 }
57 
isfoggedcube(const ivec & o,int size)58 static inline int isfoggedcube(const ivec &o, int size)
59 {
60     loopi(4) if(o.dist(vfcP[i]) < -vfcDfar[i]*size) return true;
61     float dist = o.dist(vfcP[4]);
62     return dist < -vfcDfar[4]*size || dist > vfcDfog - vfcDnear[4]*size;
63 }
64 
isvisiblecube(const ivec & o,int size)65 int isvisiblecube(const ivec &o, int size)
66 {
67     int v = VFC_FULL_VISIBLE;
68     float dist;
69 
70     loopi(5)
71     {
72         dist = o.dist(vfcP[i]);
73         if(dist < -vfcDfar[i]*size) return VFC_NOT_VISIBLE;
74         if(dist < -vfcDnear[i]*size) v = VFC_PART_VISIBLE;
75     }
76 
77     dist -= vfcDfog;
78     if(dist > -vfcDnear[4]*size) return VFC_FOGGED;
79     if(dist > -vfcDfar[4]*size) v = VFC_PART_VISIBLE;
80 
81     return v;
82 }
83 
vadist(vtxarray * va,const vec & p)84 float vadist(vtxarray *va, const vec &p)
85 {
86     return p.dist_to_bb(va->bbmin, va->bbmax);
87 }
88 
89 #define VASORTSIZE 64
90 
91 static vtxarray *vasort[VASORTSIZE];
92 
addvisibleva(vtxarray * va)93 void addvisibleva(vtxarray *va)
94 {
95 	float dist = vadist(va, camera1->o);
96 	va->distance = int(dist); /*cv.dist(camera1->o) - va->size*SQRT3/2*/
97 
98 	int hash = min(int(dist*VASORTSIZE/hdr.worldsize), VASORTSIZE-1);
99 	vtxarray **prev = &vasort[hash], *cur = vasort[hash];
100 
101     while(cur && va->distance >= cur->distance)
102 	{
103 		prev = &cur->next;
104 		cur = cur->next;
105 	}
106 
107 	va->next = *prev;
108 	*prev = va;
109 }
110 
sortvisiblevas()111 void sortvisiblevas()
112 {
113 	visibleva = NULL;
114 	vtxarray **last = &visibleva;
115 	loopi(VASORTSIZE) if(vasort[i])
116 	{
117 		vtxarray *va = vasort[i];
118 		*last = va;
119 		while(va->next) va = va->next;
120 		last = &va->next;
121 	}
122 }
123 
findvisiblevas(vector<vtxarray * > & vas,bool resetocclude=false)124 void findvisiblevas(vector<vtxarray *> &vas, bool resetocclude = false)
125 {
126     loopv(vas)
127     {
128         vtxarray &v = *vas[i];
129         int prevvfc = resetocclude ? VFC_NOT_VISIBLE : v.curvfc;
130         v.curvfc = isvisiblecube(v.o, v.size);
131         if(v.curvfc!=VFC_NOT_VISIBLE)
132         {
133             if(pvsoccluded(v.o, v.size))
134             {
135                 v.curvfc += PVS_FULL_VISIBLE - VFC_FULL_VISIBLE;
136                 continue;
137             }
138             addvisibleva(&v);
139             if(v.children.length()) findvisiblevas(v.children, prevvfc>=VFC_NOT_VISIBLE);
140             if(prevvfc>=VFC_NOT_VISIBLE)
141             {
142                 v.occluded = !v.texs || pvsoccluded(v.geommin, v.geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
143                 v.query = NULL;
144             }
145         }
146     }
147 }
148 
calcvfcD()149 void calcvfcD()
150 {
151     loopi(5)
152     {
153         plane &p = vfcP[i];
154         vfcDnear[i] = vfcDfar[i] = 0;
155         loopk(3) if(p[k] > 0) vfcDfar[i] += p[k];
156         else vfcDnear[i] += p[k];
157     }
158 }
159 
setvfcP(float yaw,float pitch,const vec & camera,float minyaw=-M_PI,float maxyaw=M_PI,float minpitch=-M_PI,float maxpitch=M_PI)160 void setvfcP(float yaw, float pitch, const vec &camera, float minyaw = -M_PI, float maxyaw = M_PI, float minpitch = -M_PI, float maxpitch = M_PI)
161 {
162     yaw *= RAD;
163     pitch *= RAD;
164     vfcP[0].toplane(vec(yaw + M_PI/2 - min(vfcfov, -minyaw), pitch), camera);   // left plane
165     vfcP[1].toplane(vec(yaw - M_PI/2 + min(vfcfov,  maxyaw), pitch), camera);   // right plane
166     vfcP[2].toplane(vec(yaw, pitch + M_PI/2 - min(vfcfovy, -minpitch)), camera); // top plane
167     vfcP[3].toplane(vec(yaw, pitch - M_PI/2 + min(vfcfovy,  maxpitch)), camera); // bottom plane
168     vfcP[4].toplane(vec(yaw, pitch), camera);          // near/far planes
169     extern int fog;
170     vfcDfog = fog;
171     calcvfcD();
172 }
173 
174 plane oldvfcP[5];
175 
reflectvfcP(float z,float minyaw,float maxyaw,float minpitch,float maxpitch)176 void reflectvfcP(float z, float minyaw, float maxyaw, float minpitch, float maxpitch)
177 {
178     memcpy(oldvfcP, vfcP, sizeof(vfcP));
179 
180     if(z < 0) setvfcP(camera1->yaw, camera1->pitch, camera1->o, minyaw, maxyaw, minpitch, maxpitch);
181     else
182     {
183         vec o(camera1->o);
184         o.z = z-(camera1->o.z-z);
185         setvfcP(camera1->yaw, -camera1->pitch, o, minyaw, maxyaw, -maxpitch, -minpitch);
186     }
187 }
188 
restorevfcP()189 void restorevfcP()
190 {
191 	memcpy(vfcP, oldvfcP, sizeof(vfcP));
192     calcvfcD();
193 }
194 
195 extern vector<vtxarray *> varoot;
196 
visiblecubes(float fov,float fovy)197 void visiblecubes(float fov, float fovy)
198 {
199     memset(vasort, 0, sizeof(vasort));
200 
201     vfcfov = fov*0.5f*RAD;
202     vfcfovy = fovy*0.5f*RAD;
203 
204     // Calculate view frustrum: Only changes if resize, but...
205     setvfcP(camera1->yaw, camera1->pitch, camera1->o);
206 
207     findvisiblevas(varoot);
208     sortvisiblevas();
209 }
210 
insideva(const vtxarray * va,const vec & v,int margin=1)211 static inline bool insideva(const vtxarray *va, const vec &v, int margin = 1)
212 {
213     int size = va->size + margin;
214     return v.x>=va->o.x-margin && v.y>=va->o.y-margin && v.z>=va->o.z-margin &&
215            v.x<=va->o.x+size && v.y<=va->o.y+size && v.z<=va->o.z+size;
216 }
217 
218 static ivec vaorigin;
219 
resetorigin()220 static void resetorigin()
221 {
222 	vaorigin = ivec(-1, -1, -1);
223 }
224 
setorigin(vtxarray * va,bool shadowmatrix=false)225 static bool setorigin(vtxarray *va, bool shadowmatrix = false)
226 {
227     ivec o = floatvtx ? ivec(0, 0, 0) : ivec(va->o).mask(~VVEC_INT_MASK).add(0x8000>>VVEC_FRAC);
228     if(o != vaorigin)
229     {
230         vaorigin = o;
231         glPopMatrix();
232         glPushMatrix();
233         glTranslatef(o.x, o.y, o.z);
234         static const float scale = 1.0f / (1<<VVEC_FRAC);
235         glScalef(scale, scale, scale);
236 
237         if(shadowmatrix) adjustshadowmatrix(o, scale);
238         return true;
239     }
240     return false;
241 }
242 
243 ///////// occlusion queries /////////////
244 
245 #define MAXQUERY 2048
246 
247 struct queryframe
248 {
249 	int cur, max;
250 	occludequery queries[MAXQUERY];
251 };
252 
253 static queryframe queryframes[2] = {{0, 0}, {0, 0}};
254 static uint flipquery = 0;
255 
getnumqueries()256 int getnumqueries()
257 {
258 	return queryframes[flipquery].cur;
259 }
260 
flipqueries()261 void flipqueries()
262 {
263 	flipquery = (flipquery + 1) % 2;
264 	queryframe &qf = queryframes[flipquery];
265 	loopi(qf.cur) qf.queries[i].owner = NULL;
266 	qf.cur = 0;
267 }
268 
newquery(void * owner)269 occludequery *newquery(void *owner)
270 {
271 	queryframe &qf = queryframes[flipquery];
272 	if(qf.cur >= qf.max)
273 	{
274 		if(qf.max >= MAXQUERY) return NULL;
275 		glGenQueries_(1, &qf.queries[qf.max++].id);
276 	}
277 	occludequery *query = &qf.queries[qf.cur++];
278 	query->owner = owner;
279 	query->fragments = -1;
280 	return query;
281 }
282 
resetqueries()283 void resetqueries()
284 {
285 	loopi(2) loopj(queryframes[i].max) queryframes[i].queries[j].owner = NULL;
286 }
287 
clearqueries()288 void clearqueries()
289 {
290     loopi(2)
291     {
292         queryframe &qf = queryframes[i];
293         loopj(qf.max)
294         {
295             glDeleteQueries_(1, &qf.queries[j].id);
296             qf.queries[j].owner = NULL;
297         }
298         qf.cur = qf.max = 0;
299     }
300 }
301 
302 VAR(oqfrags, 0, 8, 64);
303 VAR(oqreflect, 0, 4, 64);
304 
checkquery(occludequery * query,bool nowait)305 bool checkquery(occludequery *query, bool nowait)
306 {
307 	GLuint fragments;
308 	if(query->fragments >= 0) fragments = query->fragments;
309 	else
310 	{
311 		if(nowait)
312 		{
313 			GLint avail;
314 			glGetQueryObjectiv_(query->id, GL_QUERY_RESULT_AVAILABLE, &avail);
315 			if(!avail) return false;
316 		}
317 		glGetQueryObjectuiv_(query->id, GL_QUERY_RESULT_ARB, &fragments);
318 		query->fragments = fragments;
319 	}
320 	return fragments < (uint)(reflecting || refracting ? oqreflect : oqfrags);
321 }
322 
drawbb(const ivec & bo,const ivec & br,const vec & camera,int scale,const ivec & origin)323 void drawbb(const ivec &bo, const ivec &br, const vec &camera, int scale, const ivec &origin)
324 {
325     glBegin(GL_QUADS);
326 
327     loopi(6)
328     {
329         int dim = dimension(i), coord = dimcoord(i);
330 
331         if(coord)
332         {
333             if(camera[dim] < bo[dim] + br[dim]) continue;
334         }
335         else if(camera[dim] > bo[dim]) continue;
336 
337         loopj(4)
338         {
339             const ivec &cc = cubecoords[fv[i][j]];
340             glVertex3f(((cc.x ? bo.x+br.x : bo.x) - origin.x) << scale,
341                        ((cc.y ? bo.y+br.y : bo.y) - origin.y) << scale,
342                        ((cc.z ? bo.z+br.z : bo.z) - origin.z) << scale);
343         }
344 
345         xtraverts += 4;
346     }
347 
348     glEnd();
349 }
350 
351 extern int octaentsize;
352 
353 static octaentities *visiblemms, **lastvisiblemms;
354 
findvisiblemms(const vector<extentity * > & ents)355 void findvisiblemms(const vector<extentity *> &ents)
356 {
357 	for(vtxarray *va = visibleva; va; va = va->next)
358 	{
359 		if(!va->mapmodels || va->curvfc >= VFC_FOGGED || va->occluded >= OCCLUDE_BB) continue;
360 		loopv(*va->mapmodels)
361 		{
362 			octaentities *oe = (*va->mapmodels)[i];
363             if(isfoggedcube(oe->o, oe->size) || pvsoccluded(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin))) continue;
364 
365 			bool occluded = oe->query && oe->query->owner == oe && checkquery(oe->query);
366 			if(occluded)
367 			{
368 				oe->distance = -1;
369 
370 				oe->next = NULL;
371 				*lastvisiblemms = oe;
372 				lastvisiblemms = &oe->next;
373 			}
374 			else
375 			{
376 				int visible = 0;
377 				loopv(oe->mapmodels)
378 				{
379 					extentity &e = *ents[oe->mapmodels[i]];
380 					if(e.lastemit && e.spawned && e.attrs[5]&MMT_HIDE) continue;
381                     e.visible = true;
382 					++visible;
383 				}
384 				if(!visible) continue;
385 
386 				oe->distance = int(camera1->o.dist_to_bb(oe->o, oe->size));
387 
388 				octaentities **prev = &visiblemms, *cur = visiblemms;
389 				while(cur && cur->distance >= 0 && oe->distance > cur->distance)
390 				{
391 					prev = &cur->next;
392 					cur = cur->next;
393 				}
394 
395 				if(*prev == NULL) lastvisiblemms = &oe->next;
396 				oe->next = *prev;
397 				*prev = oe;
398 			}
399 		}
400 	}
401 }
402 
403 VAR(oqmm, 0, 4, 8);
404 
405 extern bool getentboundingbox(extentity &e, ivec &o, ivec &r);
406 
rendermapmodel(extentity & e)407 void rendermapmodel(extentity &e)
408 {
409 	int anim = ANIM_MAPMODEL|ANIM_LOOP, basetime = 0, flags = MDL_CULL_VFC|MDL_CULL_DIST|MDL_DYNLIGHT;
410     if(e.lastemit)
411     {
412     	if(e.attrs[5]&MMT_HIDE && e.spawned) return;
413 		anim = e.spawned ? ANIM_TRIGGER_ON : ANIM_TRIGGER_OFF;
414 		if(e.lastemit > 0 && lastmillis-e.lastemit < entities::triggertime(e)) basetime = e.lastemit;
415 		else anim |= ANIM_END;
416     }
417 	if((e.lastemit || e.attrs[5]&MMT_NOSHADOW) && !(e.attrs[5]&MMT_NODYNSHADOW)) flags |= MDL_SHADOW;
418 	mapmodelinfo &mmi = getmminfo(e.attrs[0]);
419 	if(&mmi) rendermodel(&e.light, mmi.name, anim, e.o, (float)(e.attrs[1]%360), 0, (float)(e.attrs[2]%360), flags, NULL, NULL, basetime, 0, e.attrs[3] ? min(e.attrs[3]/100.f, 1.f) : 1.f, e.attrs[4] ? max(e.attrs[4]/100.f, 1e-3f) : 1.f);
420 }
421 
422 extern int reflectdist;
423 
424 vtxarray *reflectedva;
425 
renderreflectedmapmodels()426 void renderreflectedmapmodels()
427 {
428     const vector<extentity *> &ents = entities::getents();
429 
430     octaentities *mms = visiblemms;
431     if(reflecting)
432     {
433         octaentities **lastmms = &mms;
434         for(vtxarray *va = reflectedva; va; va = va->rnext)
435         {
436             if(!va->mapmodels || va->distance > reflectdist) continue;
437             loopv(*va->mapmodels)
438             {
439                 octaentities *oe = (*va->mapmodels)[i];
440                 *lastmms = oe;
441                 lastmms = &oe->rnext;
442             }
443         }
444         *lastmms = NULL;
445     }
446     for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
447     {
448         if(reflecting || refracting>0 ? oe->bbmax.z <= reflectz : oe->bbmin.z >= reflectz) continue;
449         if(isfoggedcube(oe->o, oe->size)) continue;
450         loopv(oe->mapmodels)
451         {
452            extentity &e = *ents[oe->mapmodels[i]];
453            if(e.visible || (e.lastemit && e.spawned && e.attrs[4]&MMT_HIDE)) continue;
454            e.visible = true;
455         }
456     }
457     if(mms)
458     {
459         startmodelbatches();
460         for(octaentities *oe = mms; oe; oe = reflecting ? oe->rnext : oe->next)
461         {
462             loopv(oe->mapmodels)
463             {
464                 extentity &e = *ents[oe->mapmodels[i]];
465                 if(!e.visible) continue;
466                 rendermapmodel(e);
467                 e.visible = false;
468             }
469         }
470         endmodelbatches();
471     }
472 }
473 
rendermapmodels()474 void rendermapmodels()
475 {
476 	const vector<extentity *> &ents = entities::getents();
477 
478 	visiblemms = NULL;
479 	lastvisiblemms = &visiblemms;
480 	findvisiblemms(ents);
481 
482 	static int skipoq = 0;
483 	bool doquery = hasOQ && oqfrags && oqmm;
484 
485 	startmodelbatches();
486 	for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance>=0)
487 	{
488         bool rendered = false;
489         loopv(oe->mapmodels)
490         {
491             extentity &e = *ents[oe->mapmodels[i]];
492             if(!e.visible) continue;
493             if(!rendered)
494             {
495                 rendered = true;
496                 oe->query = doquery && oe->distance>0 && !(++skipoq%oqmm) ? newquery(oe) : NULL;
497                 if(oe->query) startmodelquery(oe->query);
498             }
499             rendermapmodel(e);
500             e.visible = false;
501         }
502         if(rendered && oe->query) endmodelquery();
503 	}
504 	endmodelbatches();
505 
506 	bool queried = false;
507 	for(octaentities *oe = visiblemms; oe; oe = oe->next) if(oe->distance<0)
508 	{
509 		oe->query = doquery ? newquery(oe) : NULL;
510 		if(!oe->query) continue;
511 		if(!queried)
512 		{
513 			glDepthMask(GL_FALSE);
514 			glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
515 			nocolorshader->set();
516             queried = true;
517 		}
518 		startquery(oe->query);
519 		drawbb(oe->bbmin, ivec(oe->bbmax).sub(oe->bbmin));
520 		endquery(oe->query);
521 	}
522 	if(queried)
523 	{
524 		glDepthMask(GL_TRUE);
525         glColorMask(COLORMASK, fading ? GL_FALSE : GL_TRUE);
526 	}
527 }
528 
bbinsideva(const ivec & bo,const ivec & br,vtxarray * va)529 static inline bool bbinsideva(const ivec &bo, const ivec &br, vtxarray *va)
530 {
531     return bo.x >= va->bbmin.x && bo.y >= va->bbmin.y && va->o.z >= va->bbmin.z &&
532         bo.x + br.x <= va->bbmax.x && bo.y + br.y <= va->bbmax.y && bo.z + br.z <= va->bbmax.z;
533 }
534 
bboccluded(const ivec & bo,const ivec & br,cube * c,const ivec & o,int size)535 static inline bool bboccluded(const ivec &bo, const ivec &br, cube *c, const ivec &o, int size)
536 {
537     loopoctabox(o, size, bo, br)
538     {
539         ivec co(i, o.x, o.y, o.z, size);
540         if(c[i].ext && c[i].ext->va)
541         {
542             vtxarray *va = c[i].ext->va;
543             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) continue;
544         }
545         if(c[i].children && bboccluded(bo, br, c[i].children, co, size>>1)) continue;
546         return false;
547     }
548     return true;
549 }
550 
bboccluded(const ivec & bo,const ivec & br)551 bool bboccluded(const ivec &bo, const ivec &br)
552 {
553     int diff = (bo.x^(bo.x+br.x)) | (bo.y^(bo.y+br.y)) | (bo.z^(bo.z+br.z));
554     if(diff&~((1<<worldscale)-1)) return false;
555     int scale = worldscale-1;
556     if(diff&(1<<scale)) return bboccluded(bo, br, worldroot, ivec(0, 0, 0), 1<<scale);
557     cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
558     if(c->ext && c->ext->va)
559     {
560         vtxarray *va = c->ext->va;
561         if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
562     }
563     scale--;
564     while(c->children && !(diff&(1<<scale)))
565     {
566         c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
567         if(c->ext && c->ext->va)
568         {
569             vtxarray *va = c->ext->va;
570             if(va->curvfc >= VFC_FOGGED || (va->occluded >= OCCLUDE_BB && bbinsideva(bo, br, va))) return true;
571         }
572         scale--;
573     }
574     if(c->children) return bboccluded(bo, br, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
575     return false;
576 }
577 
578 extern int ati_texgen_bug;
579 
setuptexgen(int dims=2)580 static void setuptexgen(int dims = 2)
581 {
582     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
583     glEnable(GL_TEXTURE_GEN_S);
584     if(dims>=2)
585     {
586         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
587         glEnable(GL_TEXTURE_GEN_T);
588         if(ati_texgen_bug) glEnable(GL_TEXTURE_GEN_R);     // should not be needed, but apparently makes some ATI drivers happy
589     }
590 }
591 
disabletexgen(int dims=2)592 static void disabletexgen(int dims = 2)
593 {
594     glDisable(GL_TEXTURE_GEN_S);
595     if(dims>=2)
596     {
597         glDisable(GL_TEXTURE_GEN_T);
598         if(ati_texgen_bug) glDisable(GL_TEXTURE_GEN_R);
599     }
600 }
601 
602 HVARP(outline, 0, 1, 0xFFFFFF);
603 VAR(dtoutline, 0, 1, 1);
604 
renderoutline()605 void renderoutline()
606 {
607 	notextureshader->set();
608 
609 	glDisable(GL_TEXTURE_2D);
610 	glEnableClientState(GL_VERTEX_ARRAY);
611 
612 	glPushMatrix();
613 
614     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
615 	glColor3ub((outline>>16)&0xFF, (outline>>8)&0xFF, outline&0xFF);
616 
617     enablepolygonoffset(GL_POLYGON_OFFSET_LINE);
618 
619 	if(!dtoutline) glDisable(GL_DEPTH_TEST);
620 
621 	resetorigin();
622     vtxarray *prev = NULL;
623     for(vtxarray *va = visibleva; va; va = va->next)
624     {
625         if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
626 
627         if(!prev || va->vbuf != prev->vbuf)
628         {
629             setorigin(va);
630             if(hasVBO)
631             {
632                 glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
633                 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
634             }
635             glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
636         }
637 
638         drawvatris(va, 3*va->tris, va->edata);
639         xtravertsva += va->verts;
640 
641         prev = va;
642     }
643 
644 	if(!dtoutline) glEnable(GL_DEPTH_TEST);
645 
646     disablepolygonoffset(GL_POLYGON_OFFSET_LINE);
647 
648     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
649 
650 	glPopMatrix();
651 
652 	if(hasVBO)
653 	{
654 		glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
655 		glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
656 	}
657 	glDisableClientState(GL_VERTEX_ARRAY);
658 	glEnable(GL_TEXTURE_2D);
659 
660 	defaultshader->set();
661 }
662 
663 HVAR(blendbrushcolor, 0, 0x0000C0, 0xFFFFFF);
664 
renderblendbrush(GLuint tex,float x,float y,float w,float h)665 void renderblendbrush(GLuint tex, float x, float y, float w, float h)
666 {
667     static Shader *blendbrushshader = NULL;
668     if(!blendbrushshader) blendbrushshader = lookupshaderbyname("blendbrush");
669     blendbrushshader->set();
670 
671     glEnableClientState(GL_VERTEX_ARRAY);
672 
673     glPushMatrix();
674 
675     glDepthFunc(GL_LEQUAL);
676 
677     glEnable(GL_BLEND);
678     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
679 
680     glEnable(GL_TEXTURE_2D);
681     glBindTexture(GL_TEXTURE_2D, tex);
682     glColor4ub((blendbrushcolor>>16)&0xFF, (blendbrushcolor>>8)&0xFF, blendbrushcolor&0xFF, 0x40);
683 
684     GLfloat s[4] = { 0, 0, 0, 0 }, t[4] = { 0, 0, 0, 0 };
685     if(renderpath==R_FIXEDFUNCTION) setuptexgen();
686 
687     resetorigin();
688     vtxarray *prev = NULL;
689     for(vtxarray *va = visibleva; va; va = va->next)
690     {
691         if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
692         if(va->o.x + va->size <= x || va->o.y + va->size <= y || va->o.x >= x + w || va->o.y >= y + h) continue;
693 
694         if(!prev || va->vbuf != prev->vbuf)
695         {
696             if(setorigin(va))
697             {
698                 s[0] = 1.0f / (w*(1<<VVEC_FRAC));
699                 s[3] = (vaorigin.x - x) / w;
700                 t[1] = 1.0f / (h*(1<<VVEC_FRAC));
701                 t[3] = (vaorigin.y - y) / h;
702                 if(renderpath==R_FIXEDFUNCTION)
703                 {
704                     glTexGenfv(GL_S, GL_OBJECT_PLANE, s);
705                     glTexGenfv(GL_T, GL_OBJECT_PLANE, t);
706                 }
707                 else
708                 {
709                     setlocalparamfv("texgenS", SHPARAM_VERTEX, 0, s);
710                     setlocalparamfv("texgenT", SHPARAM_VERTEX, 1, t);
711                 }
712             }
713 
714             if(hasVBO)
715             {
716                 glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
717                 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
718             }
719             glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
720         }
721 
722         drawvatris(va, 3*va->tris, va->edata);
723         xtravertsva += va->verts;
724 
725         prev = va;
726     }
727 
728     if(renderpath==R_FIXEDFUNCTION) disabletexgen();
729 
730     glDisable(GL_TEXTURE_2D);
731     glDisable(GL_BLEND);
732 
733     glDepthFunc(GL_LESS);
734 
735     glPopMatrix();
736 
737     if(hasVBO)
738     {
739         glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
740         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
741     }
742     glDisableClientState(GL_VERTEX_ARRAY);
743 
744     notextureshader->set();
745 }
746 
rendershadowmapreceivers()747 void rendershadowmapreceivers()
748 {
749     if(!hasBE) return;
750 
751     static Shader *shadowmapshader = NULL;
752     if(!shadowmapshader) shadowmapshader = lookupshaderbyname("shadowmapreceiver");
753     shadowmapshader->set();
754 
755     glDisable(GL_TEXTURE_2D);
756     glEnableClientState(GL_VERTEX_ARRAY);
757 
758     glCullFace(GL_BACK);
759     glDepthMask(GL_FALSE);
760     glDepthFunc(GL_GREATER);
761 
762     extern int ati_minmax_bug;
763     if(!ati_minmax_bug) glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
764 
765     glEnable(GL_BLEND);
766     glBlendEquation_(GL_MAX_EXT);
767     glBlendFunc(GL_ONE, GL_ONE);
768 
769     glPushMatrix();
770 
771     resetorigin();
772     vtxarray *prev = NULL;
773     for(vtxarray *va = visibleva; va; va = va->next)
774     {
775         if(!va->texs || va->curvfc >= VFC_FOGGED || !isshadowmapreceiver(va)) continue;
776 
777         if(!prev || va->vbuf != prev->vbuf)
778         {
779             setorigin(va);
780             if(hasVBO)
781             {
782                 glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
783                 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
784             }
785             glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
786         }
787 
788         drawvatris(va, 3*va->tris, va->edata);
789         xtravertsva += va->verts;
790 
791         prev = va;
792     }
793 
794     glPopMatrix();
795 
796     glDisable(GL_BLEND);
797     glBlendEquation_(GL_FUNC_ADD_EXT);
798 
799     glCullFace(GL_FRONT);
800     glDepthMask(GL_TRUE);
801     glDepthFunc(GL_LESS);
802 
803     if(!ati_minmax_bug) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
804 
805     if(hasVBO)
806     {
807         glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
808         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
809     }
810     glDisableClientState(GL_VERTEX_ARRAY);
811     glEnable(GL_TEXTURE_2D);
812 }
813 
renderdepthobstacles(const vec & bbmin,const vec & bbmax,float scale,float * ranges,int numranges)814 void renderdepthobstacles(const vec &bbmin, const vec &bbmax, float scale, float *ranges, int numranges)
815 {
816     float scales[4] = { 0, 0, 0, 0 }, offsets[4] = { 0, 0, 0, 0 };
817     if(numranges < 0)
818     {
819         SETSHADER(depthfxsplitworld);
820 
821         loopi(-numranges)
822         {
823             if(!i) scales[i] = 1.0f/scale;
824             else scales[i] = scales[i-1]*256;
825         }
826     }
827     else
828     {
829         SETSHADER(depthfxworld);
830 
831         if(!numranges) loopi(4) scales[i] = 1.0f/scale;
832         else loopi(numranges)
833         {
834             scales[i] = 1.0f/scale;
835             offsets[i] = -ranges[i]/scale;
836         }
837     }
838     setlocalparamfv("depthscale", SHPARAM_VERTEX, 0, scales);
839     setlocalparamfv("depthoffsets", SHPARAM_VERTEX, 1, offsets);
840 
841     glDisable(GL_TEXTURE_2D);
842     glEnableClientState(GL_VERTEX_ARRAY);
843 
844     glPushMatrix();
845 
846     resetorigin();
847     vtxarray *prev = NULL;
848     for(vtxarray *va = visibleva; va; va = va->next)
849     {
850         if(!va->texs || va->occluded >= OCCLUDE_GEOM ||
851            va->o.x > bbmax.x || va->o.y > bbmax.y || va->o.z > bbmax.z ||
852            va->o.x + va->size < bbmin.x || va->o.y + va->size < bbmin.y || va->o.z + va->size < bbmin.z)
853            continue;
854 
855         if(!prev || va->vbuf != prev->vbuf)
856         {
857             setorigin(va);
858             if(hasVBO)
859             {
860                 glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
861                 glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
862             }
863             glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
864         }
865 
866         drawvatris(va, 3*va->tris, va->edata);
867         xtravertsva += va->verts;
868 
869         prev = va;
870     }
871     glPopMatrix();
872 
873     if(hasVBO)
874     {
875         glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
876         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
877     }
878     glDisableClientState(GL_VERTEX_ARRAY);
879     glEnable(GL_TEXTURE_2D);
880 
881     defaultshader->set();
882 }
883 
884 // [rotation][dimension] = vec4
885 float orientation_tangent [6][3][4] =
886 {
887     { { 0,  1,  0, 0 }, {  1, 0,  0, 0 }, {  1,  0, 0, 0 } },
888     { { 0,  0, -1, 0 }, {  0, 0, -1, 0 }, {  0,  1, 0, 0 } },
889     { { 0, -1,  0, 0 }, { -1, 0,  0, 0 }, { -1,  0, 0, 0 } },
890     { { 0,  0,  1, 0 }, {  0, 0,  1, 0 }, {  0, -1, 0, 0 } },
891     { { 0, -1,  0, 0 }, { -1, 0,  0, 0 }, { -1,  0, 0, 0 } },
892     { { 0,  1,  0, 0 }, {  1, 0,  0, 0 }, {  1,  0, 0, 0 } },
893 };
894 float orientation_binormal[6][3][4] =
895 {
896     { { 0,  0, -1, 0 }, {  0, 0, -1, 0 }, {  0,  1, 0, 0 } },
897     { { 0, -1,  0, 0 }, { -1, 0,  0, 0 }, { -1,  0, 0, 0 } },
898     { { 0,  0,  1, 0 }, {  0, 0,  1, 0 }, {  0, -1, 0, 0 } },
899     { { 0,  1,  0, 0 }, {  1, 0,  0, 0 }, {  1,  0, 0, 0 } },
900     { { 0,  0, -1, 0 }, {  0, 0, -1, 0 }, {  0,  1, 0, 0 } },
901     { { 0,  0,  1, 0 }, {  0, 0,  1, 0 }, {  0, -1, 0, 0 } },
902 };
903 
904 struct renderstate
905 {
906     bool colormask, depthmask, blending, mtglow, skippedglow;
907     GLuint vbuf;
908     float fogplane;
909     int diffusetmu, lightmaptmu, glowtmu, fogtmu, causticstmu;
910     GLfloat color[4];
911     vec glowcolor;
912     GLuint textures[8];
913     Slot *slot;
914     float texgenSk, texgenSoff, texgenTk, texgenToff;
915     int texgendim;
916     bool mttexgen;
917     int visibledynlights;
918     uint dynlightmask;
919     vec dynlightpos;
920     float dynlightradius;
921 
renderstaterenderstate922     renderstate() : colormask(true), depthmask(true), blending(false), mtglow(false), skippedglow(false), vbuf(0), fogplane(-1), diffusetmu(0), lightmaptmu(1), glowtmu(-1), fogtmu(-1), causticstmu(-1), glowcolor(1, 1, 1), slot(NULL), texgendim(-1), mttexgen(false), visibledynlights(0), dynlightmask(0)
923     {
924         loopk(4) color[k] = 1;
925         loopk(8) textures[k] = 0;
926     }
927 };
928 
renderquery(renderstate & cur,occludequery * query,vtxarray * va)929 void renderquery(renderstate &cur, occludequery *query, vtxarray *va)
930 {
931     nocolorshader->set();
932     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
933     if(cur.depthmask) { cur.depthmask = false; glDepthMask(GL_FALSE); }
934 
935     vec camera(camera1->o);
936     if(reflecting) camera.z = reflectz;
937 
938     startquery(query);
939 
940     drawbb(va->bbmin, ivec(va->bbmax).sub(va->bbmin), camera, vaorigin.x >= 0 ? VVEC_FRAC : 0, vaorigin.x >= 0 ? vaorigin : ivec(0, 0, 0));
941 
942     endquery(query);
943 }
944 
945 enum
946 {
947 	RENDERPASS_LIGHTMAP = 0,
948 	RENDERPASS_COLOR,
949 	RENDERPASS_Z,
950     RENDERPASS_GLOW,
951     RENDERPASS_CAUSTICS,
952     RENDERPASS_FOG,
953     RENDERPASS_SHADOWMAP,
954     RENDERPASS_DYNLIGHT,
955     RENDERPASS_LIGHTMAP_BLEND
956 };
957 
958 struct geombatch
959 {
960     const elementset &es;
961     Slot &slot;
962     ushort *edata;
963     vtxarray *va;
964     int next, batch;
965 
geombatchgeombatch966     geombatch(const elementset &es, ushort *edata, vtxarray *va)
967       : es(es), slot(lookuptexture(es.texture)), edata(edata), va(va),
968         next(-1), batch(-1)
969     {}
970 
comparegeombatch971     int compare(const geombatch &b) const
972     {
973         if(va->vbuf < b.va->vbuf) return -1;
974         if(va->vbuf > b.va->vbuf) return 1;
975         if(renderpath!=R_FIXEDFUNCTION)
976         {
977             if(va->dynlightmask < b.va->dynlightmask) return -1;
978             if(va->dynlightmask > b.va->dynlightmask) return 1;
979             if(slot.shader < b.slot.shader) return -1;
980             if(slot.shader > b.slot.shader) return 1;
981             if(slot.params.length() < b.slot.params.length()) return -1;
982             if(slot.params.length() > b.slot.params.length()) return 1;
983         }
984         if(es.texture < b.es.texture) return -1;
985         if(es.texture > b.es.texture) return 1;
986         if(es.lmid < b.es.lmid) return -1;
987         if(es.lmid > b.es.lmid) return 1;
988         if(es.envmap < b.es.envmap) return -1;
989         if(es.envmap > b.es.envmap) return 1;
990         return 0;
991     }
992 };
993 
994 static vector<geombatch> geombatches;
995 static int firstbatch = -1, numbatches = 0;
996 
mergetexs(vtxarray * va,elementset * texs=NULL,int numtexs=0,ushort * edata=NULL)997 static void mergetexs(vtxarray *va, elementset *texs = NULL, int numtexs = 0, ushort *edata = NULL)
998 {
999     if(!texs)
1000     {
1001         texs = va->eslist;
1002         numtexs = va->texs;
1003         edata = va->edata;
1004     }
1005 
1006     if(firstbatch < 0)
1007     {
1008         firstbatch = geombatches.length();
1009         numbatches = numtexs;
1010         loopi(numtexs-1)
1011         {
1012             geombatches.add(geombatch(texs[i], edata, va)).next = i+1;
1013             edata += texs[i].length[5];
1014         }
1015         geombatches.add(geombatch(texs[numtexs-1], edata, va));
1016         return;
1017     }
1018 
1019     int prevbatch = -1, curbatch = firstbatch, curtex = 0;
1020     do
1021     {
1022         geombatch &b = geombatches.add(geombatch(texs[curtex], edata, va));
1023         edata += texs[curtex].length[5];
1024         int dir = -1;
1025         while(curbatch >= 0)
1026         {
1027             dir = b.compare(geombatches[curbatch]);
1028             if(dir <= 0) break;
1029             prevbatch = curbatch;
1030             curbatch = geombatches[curbatch].next;
1031         }
1032         if(!dir)
1033         {
1034             int last = curbatch, next;
1035             for(;;)
1036             {
1037                 next = geombatches[last].batch;
1038                 if(next < 0) break;
1039                 last = next;
1040             }
1041             if(last==curbatch)
1042             {
1043                 b.batch = curbatch;
1044                 b.next = geombatches[curbatch].next;
1045                 if(prevbatch < 0) firstbatch = geombatches.length()-1;
1046                 else geombatches[prevbatch].next = geombatches.length()-1;
1047                 curbatch = geombatches.length()-1;
1048             }
1049             else
1050             {
1051                 b.batch = next;
1052                 geombatches[last].batch = geombatches.length()-1;
1053             }
1054         }
1055         else
1056         {
1057             numbatches++;
1058             b.next = curbatch;
1059             if(prevbatch < 0) firstbatch = geombatches.length()-1;
1060             else geombatches[prevbatch].next = geombatches.length()-1;
1061             prevbatch = geombatches.length()-1;
1062         }
1063     }
1064     while(++curtex < numtexs);
1065 }
1066 
mergeglowtexs(vtxarray * va)1067 static void mergeglowtexs(vtxarray *va)
1068 {
1069     int start = -1;
1070     ushort *edata = va->edata, *startdata = NULL;
1071     loopi(va->texs)
1072     {
1073         elementset &es = va->eslist[i];
1074         Slot &slot = lookuptexture(es.texture, false);
1075         if(slot.texmask&(1<<TEX_GLOW) && !slot.mtglowed)
1076         {
1077             if(start<0) { start = i; startdata = edata; }
1078         }
1079         else if(start>=0)
1080         {
1081             mergetexs(va, &va->eslist[start], i-start, startdata);
1082             start = -1;
1083         }
1084         edata += es.length[5];
1085     }
1086     if(start>=0) mergetexs(va, &va->eslist[start], va->texs-start, startdata);
1087 }
1088 
changefogplane(renderstate & cur,int pass,vtxarray * va)1089 static void changefogplane(renderstate &cur, int pass, vtxarray *va)
1090 {
1091     if(renderpath!=R_FIXEDFUNCTION)
1092     {
1093         if((fading && !cur.blending) || fogging)
1094         {
1095             float fogplane = reflectz - vaorigin.z;
1096             if(cur.fogplane!=fogplane)
1097             {
1098                 cur.fogplane = fogplane;
1099                 if(fogging) setfogplane(1.0f/(1<<VVEC_FRAC), fogplane, false, -0.25f/(1<<VVEC_FRAC), 0.5f + 0.25f*fogplane);
1100                 else setfogplane(0, 0, false, 0.25f/(1<<VVEC_FRAC), 0.5f - 0.25f*fogplane);
1101             }
1102         }
1103     }
1104     else if(pass==RENDERPASS_FOG || (cur.fogtmu>=0 && (pass==RENDERPASS_LIGHTMAP || pass==RENDERPASS_GLOW || pass==RENDERPASS_SHADOWMAP)))
1105     {
1106         if(pass==RENDERPASS_LIGHTMAP) glActiveTexture_(GL_TEXTURE0_ARB+cur.fogtmu);
1107         else if(pass==RENDERPASS_GLOW || pass==RENDERPASS_SHADOWMAP) glActiveTexture_(GL_TEXTURE1_ARB);
1108         GLfloat s[4] = { 0, 0, -1.0f/(waterfog<<VVEC_FRAC), (reflectz - vaorigin.z)/waterfog };
1109         glTexGenfv(GL_S, GL_OBJECT_PLANE, s);
1110         if(pass==RENDERPASS_LIGHTMAP) glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1111         else if(pass==RENDERPASS_GLOW || pass==RENDERPASS_SHADOWMAP) glActiveTexture_(GL_TEXTURE0_ARB);
1112     }
1113 }
1114 
changedynlightpos(renderstate & cur,vtxarray * va)1115 static void changedynlightpos(renderstate &cur, vtxarray *va)
1116 {
1117     GLfloat tx[4] = { 0.5f/(cur.dynlightradius * (1<<VVEC_FRAC)), 0, 0, 0.5f + 0.5f*(vaorigin.x - cur.dynlightpos.x)/cur.dynlightradius },
1118             ty[4] = { 0, 0.5f/(cur.dynlightradius * (1<<VVEC_FRAC)), 0, 0.5f + 0.5f*(vaorigin.y - cur.dynlightpos.y)/cur.dynlightradius },
1119             tz[4] = { 0, 0, 0.5f/(cur.dynlightradius * (1<<VVEC_FRAC)), 0.5f + 0.5f*(vaorigin.z - cur.dynlightpos.z)/cur.dynlightradius };
1120     glActiveTexture_(GL_TEXTURE0_ARB);
1121     glTexGenfv(GL_S, GL_OBJECT_PLANE, tx);
1122     glTexGenfv(GL_T, GL_OBJECT_PLANE, ty);
1123     glActiveTexture_(GL_TEXTURE1_ARB);
1124     glTexGenfv(GL_S, GL_OBJECT_PLANE, tz);
1125     glActiveTexture_(GL_TEXTURE2_ARB);
1126 }
1127 
changevbuf(renderstate & cur,int pass,vtxarray * va)1128 static void changevbuf(renderstate &cur, int pass, vtxarray *va)
1129 {
1130     if(setorigin(va, renderpath!=R_FIXEDFUNCTION ? pass==RENDERPASS_LIGHTMAP && !envmapping && !glaring : pass==RENDERPASS_SHADOWMAP))
1131     {
1132         cur.visibledynlights = 0;
1133         cur.dynlightmask = 0;
1134     }
1135     if(hasVBO)
1136     {
1137         glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
1138         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->ebuf);
1139     }
1140     cur.vbuf = va->vbuf;
1141 
1142     glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
1143 
1144     if(pass==RENDERPASS_LIGHTMAP)
1145     {
1146         glClientActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1147         glTexCoordPointer(2, GL_SHORT, VTXSIZE, floatvtx ? &((fvertex *)va->vdata)[0].u : &va->vdata[0].u);
1148         glClientActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1149 
1150         if(renderpath!=R_FIXEDFUNCTION)
1151         {
1152             glColorPointer(3, GL_UNSIGNED_BYTE, VTXSIZE, floatvtx ? &((fvertex *)va->vdata)[0].n : &va->vdata[0].n);
1153             setenvparamf("camera", SHPARAM_VERTEX, 4,
1154                 (camera1->o.x - vaorigin.x)*(1<<VVEC_FRAC),
1155                 (camera1->o.y - vaorigin.y)*(1<<VVEC_FRAC),
1156                 (camera1->o.z - vaorigin.z)*(1<<VVEC_FRAC),
1157                 1);
1158         }
1159     }
1160 }
1161 
changebatchtmus(renderstate & cur,int pass,geombatch & b)1162 static void changebatchtmus(renderstate &cur, int pass, geombatch &b)
1163 {
1164     bool changed = false;
1165     extern bool brightengeom;
1166     int lmid = brightengeom && (b.es.lmid < LMID_RESERVED || fullbright) ? LMID_BRIGHT : b.es.lmid;
1167     if(cur.textures[cur.lightmaptmu]!=lightmaptexs[lmid].id)
1168     {
1169         glActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1170         glBindTexture(GL_TEXTURE_2D, cur.textures[cur.lightmaptmu] = lightmaptexs[lmid].id);
1171         changed = true;
1172     }
1173     if(renderpath!=R_FIXEDFUNCTION)
1174     {
1175         int tmu = cur.lightmaptmu+1;
1176         if(b.slot.shader->type&SHADER_NORMALSLMS)
1177         {
1178             if(cur.textures[tmu]!=lightmaptexs[lmid+1].id)
1179             {
1180                 glActiveTexture_(GL_TEXTURE0_ARB+tmu);
1181                 glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = lightmaptexs[lmid+1].id);
1182                 changed = true;
1183             }
1184             tmu++;
1185         }
1186         if(b.slot.shader->type&SHADER_ENVMAP && b.es.envmap!=EMID_CUSTOM)
1187         {
1188             GLuint emtex = lookupenvmap(b.es.envmap);
1189             if(cur.textures[tmu]!=emtex)
1190             {
1191                 glActiveTexture_(GL_TEXTURE0_ARB+tmu);
1192                 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, cur.textures[tmu] = emtex);
1193                 changed = true;
1194             }
1195         }
1196     }
1197     if(changed) glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1198 }
1199 
changeglow(renderstate & cur,int pass,Slot & slot)1200 static void changeglow(renderstate &cur, int pass, Slot &slot)
1201 {
1202     vec color = slot.glowcolor;
1203     if(slot.pulseglowspeed)
1204     {
1205         float k = lastmillis*slot.pulseglowspeed;
1206         k -= floor(k);
1207         k = fabs(k*2 - 1);
1208         color.lerp(color, slot.pulseglowcolor, k);
1209     }
1210     if(pass==RENDERPASS_GLOW)
1211     {
1212         if(cur.glowcolor!=color) glColor3fv(color.v);
1213     }
1214     else
1215     {
1216         if(cur.glowcolor!=color)
1217         {
1218             if(color==vec(1, 1, 1))
1219             {
1220                 glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1221                 setuptmu(cur.glowtmu, "P + T", "= Pa");
1222             }
1223             else if(hasTE3 || hasTE4)
1224             {
1225                 glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1226                 if(cur.glowcolor==vec(1, 1, 1))
1227                 {
1228                     if(hasTE3) setuptmu(cur.glowtmu, "TPK3", "= Pa");
1229                     else if(hasTE4) setuptmu(cur.glowtmu, "TKP14", "= Pa");
1230                 }
1231                 colortmu(cur.glowtmu, color.x, color.y, color.z);
1232             }
1233             else
1234             {
1235                 slot.mtglowed = false;
1236                 cur.skippedglow = true;
1237                 return;
1238             }
1239         }
1240         else glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1241         if(!cur.mtglow) { glEnable(GL_TEXTURE_2D); cur.mtglow = true; }
1242         slot.mtglowed = true;
1243     }
1244     loopvj(slot.sts)
1245     {
1246         Slot::Tex &t = slot.sts[j];
1247         if(t.type==TEX_GLOW && t.combined<0)
1248         {
1249             if(cur.textures[cur.glowtmu]!=t.t->id)
1250                 glBindTexture(GL_TEXTURE_2D, cur.textures[cur.glowtmu] = t.t->id);
1251             break;
1252         }
1253     }
1254     cur.glowcolor = color;
1255 }
1256 
1257 VAR(blankgeom, 0, 0, 1);
1258 
changeslottmus(renderstate & cur,int pass,Slot & slot)1259 static void changeslottmus(renderstate &cur, int pass, Slot &slot)
1260 {
1261     if(pass==RENDERPASS_LIGHTMAP || pass==RENDERPASS_COLOR || pass==RENDERPASS_DYNLIGHT)
1262     {
1263         GLuint diffusetex = blankgeom ? blanktexture->id : (slot.sts.empty() ? notexture->id : slot.sts[0].t->id);
1264         if(cur.textures[cur.diffusetmu]!=diffusetex)
1265             glBindTexture(GL_TEXTURE_2D, cur.textures[cur.diffusetmu] = diffusetex);
1266     }
1267 
1268     if(renderpath==R_FIXEDFUNCTION)
1269     {
1270         if(slot.texmask&(1<<TEX_GLOW))
1271         {
1272             if(pass==RENDERPASS_LIGHTMAP || pass==RENDERPASS_COLOR)
1273             {
1274                 if(cur.glowtmu<0) { slot.mtglowed = false; cur.skippedglow = true; }
1275                 else changeglow(cur, pass, slot);
1276             }
1277             else if(pass==RENDERPASS_GLOW && !slot.mtglowed) changeglow(cur, pass, slot);
1278         }
1279         if(cur.mtglow)
1280         {
1281             if(!(slot.texmask&(1<<TEX_GLOW)) || !slot.mtglowed)
1282             {
1283                 glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1284                 glDisable(GL_TEXTURE_2D);
1285                 cur.mtglow = false;
1286             }
1287             glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1288         }
1289     }
1290     else
1291     {
1292         int tmu = cur.lightmaptmu+1, envmaptmu = -1;
1293         if(slot.shader->type&SHADER_NORMALSLMS) tmu++;
1294         if(slot.shader->type&SHADER_ENVMAP) envmaptmu = tmu++;
1295         loopvj(slot.sts)
1296         {
1297             Slot::Tex &t = slot.sts[j];
1298             Texture *u = slot.sts[j].t;
1299             if(t.type==TEX_DIFFUSE || t.combined>=0) continue;
1300             if(t.type==TEX_DIFFUSE && blankgeom) u = blanktexture;
1301             if(t.type==TEX_ENVMAP)
1302             {
1303                 if(envmaptmu>=0 && cur.textures[envmaptmu]!=u->id)
1304                 {
1305                     glActiveTexture_(GL_TEXTURE0_ARB+envmaptmu);
1306                     glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, cur.textures[envmaptmu] = u->id);
1307                 }
1308                 continue;
1309             }
1310             else if(cur.textures[tmu]!=u->id)
1311             {
1312                 glActiveTexture_(GL_TEXTURE0_ARB+tmu);
1313                 glBindTexture(GL_TEXTURE_2D, cur.textures[tmu] = u->id);
1314             }
1315             tmu++;
1316         }
1317         glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1318     }
1319 
1320     Texture *curtex = blankgeom ? blanktexture :
1321 		(!cur.slot || cur.slot->sts.empty() ? notexture : cur.slot->sts[0].t),
1322             *tex = blankgeom ? blanktexture : (slot.sts.empty() ? notexture : slot.sts[0].t);
1323     if(!cur.slot || slot.sts.empty() ||
1324         (curtex->xs != tex->xs || curtex->ys != tex->ys ||
1325          cur.slot->rotation != slot.rotation || cur.slot->scale != slot.scale ||
1326          cur.slot->xoffset != slot.xoffset || cur.slot->yoffset != slot.yoffset ||
1327          cur.slot->scrollS != slot.scrollS || cur.slot->scrollT != slot.scrollT))
1328     {
1329         float k = 8.0f/slot.scale/(1<<VVEC_FRAC),
1330               xs = slot.rotation>=2 && slot.rotation<=4 ? -tex->xs : tex->xs,
1331               ys = (slot.rotation>=1 && slot.rotation<=2) || slot.rotation==5 ? -tex->ys : tex->ys;
1332         if((slot.rotation&5)==1)
1333         {
1334             cur.texgenSk = k/xs; cur.texgenSoff = (slot.scrollT*lastmillis*tex->xs - slot.yoffset)/xs;
1335             cur.texgenTk = k/ys; cur.texgenToff = (slot.scrollS*lastmillis*tex->ys - slot.xoffset)/ys;
1336         }
1337         else
1338         {
1339             cur.texgenSk = k/xs; cur.texgenSoff = (slot.scrollS*lastmillis*tex->xs - slot.xoffset)/xs;
1340             cur.texgenTk = k/ys; cur.texgenToff = (slot.scrollT*lastmillis*tex->ys - slot.yoffset)/ys;
1341         }
1342         cur.texgendim = -1;
1343     }
1344 
1345     cur.slot = &slot;
1346 }
1347 
changeshader(renderstate & cur,Shader * s,Slot & slot,bool shadowed)1348 static void changeshader(renderstate &cur, Shader *s, Slot &slot, bool shadowed)
1349 {
1350     if(glaring)
1351     {
1352         static Shader *noglareshader = NULL, *noglareblendshader = NULL;
1353         if(!noglareshader) noglareshader = lookupshaderbyname("noglareworld");
1354         if(!noglareblendshader) noglareblendshader = lookupshaderbyname("noglareblendworld");
1355         if(s->hasoption(4)) s->setvariant(cur.visibledynlights, 4, &slot, cur.blending ? noglareblendshader : noglareshader);
1356         else s->setvariant(cur.blending ? 1 : 0, 4, &slot, cur.blending ? noglareblendshader : noglareshader);
1357     }
1358     else if(fading && !cur.blending)
1359     {
1360         if(shadowed) s->setvariant(cur.visibledynlights, 3, &slot);
1361         else s->setvariant(cur.visibledynlights, 2, &slot);
1362     }
1363     else if(shadowed) s->setvariant(cur.visibledynlights, 1, &slot);
1364     else if(!cur.visibledynlights) s->set(&slot);
1365     else s->setvariant(cur.visibledynlights-1, 0, &slot);
1366     if(s->type&SHADER_GLSLANG) cur.texgendim = -1;
1367 }
1368 
changetexgen(renderstate & cur,Slot & slot,int dim)1369 static void changetexgen(renderstate &cur, Slot &slot, int dim)
1370 {
1371     static const int si[] = { 1, 0, 0 };
1372     static const int ti[] = { 2, 2, 1 };
1373 
1374     GLfloat sgen[4] = { 0.0f, 0.0f, 0.0f, cur.texgenSoff },
1375             tgen[4] = { 0.0f, 0.0f, 0.0f, cur.texgenToff };
1376     int sdim = si[dim], tdim = ti[dim];
1377     if((slot.rotation&5)==1)
1378     {
1379         sgen[tdim] = (dim <= 1 ? -cur.texgenSk : cur.texgenSk);
1380         sgen[3] += (vaorigin[tdim]<<VVEC_FRAC)*sgen[tdim];
1381         tgen[sdim] = cur.texgenTk;
1382         tgen[3] += (vaorigin[sdim]<<VVEC_FRAC)*tgen[sdim];
1383     }
1384     else
1385     {
1386         sgen[sdim] = cur.texgenSk;
1387         sgen[3] += (vaorigin[sdim]<<VVEC_FRAC)*sgen[sdim];
1388         tgen[tdim] = (dim <= 1 ? -cur.texgenTk : cur.texgenTk);
1389         tgen[3] += (vaorigin[tdim]<<VVEC_FRAC)*tgen[tdim];
1390     }
1391 
1392     if(renderpath==R_FIXEDFUNCTION)
1393     {
1394         if(cur.texgendim!=dim)
1395         {
1396             glTexGenfv(GL_S, GL_OBJECT_PLANE, sgen);
1397             glTexGenfv(GL_T, GL_OBJECT_PLANE, tgen);
1398             // KLUGE: workaround for buggy nvidia drivers
1399             // object planes are somehow invalid unless texgen is toggled
1400             extern int nvidia_texgen_bug;
1401             if(nvidia_texgen_bug)
1402             {
1403                 glDisable(GL_TEXTURE_GEN_S);
1404                 glDisable(GL_TEXTURE_GEN_T);
1405                 glEnable(GL_TEXTURE_GEN_S);
1406                 glEnable(GL_TEXTURE_GEN_T);
1407             }
1408         }
1409 
1410         if(cur.mtglow)
1411         {
1412             glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1413             glTexGenfv(GL_S, GL_OBJECT_PLANE, sgen);
1414             glTexGenfv(GL_T, GL_OBJECT_PLANE, tgen);
1415             glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1416         }
1417         cur.mttexgen = cur.mtglow;
1418     }
1419     else
1420     {
1421         // have to pass in env, otherwise same problem as fixed function
1422         setlocalparamfv("texgenS", SHPARAM_VERTEX, 0, sgen);
1423         setlocalparamfv("texgenT", SHPARAM_VERTEX, 1, tgen);
1424         setlocalparamfv("orienttangent", SHPARAM_VERTEX, 2, orientation_tangent[slot.rotation][dim]);
1425         setlocalparamfv("orientbinormal", SHPARAM_VERTEX, 3, orientation_binormal[slot.rotation][dim]);
1426     }
1427 
1428     cur.texgendim = dim;
1429 }
1430 
1431 struct batchdrawinfo
1432 {
1433     ushort *edata;
1434     ushort len, minvert, maxvert;
1435 
batchdrawinfobatchdrawinfo1436     batchdrawinfo(geombatch &b, int dim, ushort offset, ushort len)
1437       : edata(b.edata + offset), len(len),
1438         minvert(b.va->shadowed ? b.es.minvert[dim] : min(b.es.minvert[dim], b.es.minvert[dim+1])),
1439         maxvert(b.va->shadowed ? b.es.maxvert[dim] : max(b.es.maxvert[dim], b.es.maxvert[dim+1]))
1440     {}
1441 };
1442 
renderbatch(renderstate & cur,int pass,geombatch & b)1443 static void renderbatch(renderstate &cur, int pass, geombatch &b)
1444 {
1445     static vector<batchdrawinfo> draws[6];
1446     for(geombatch *curbatch = &b;; curbatch = &geombatches[curbatch->batch])
1447     {
1448         int dim = 0;
1449         ushort offset = 0, len = 0;
1450         loopi(3)
1451         {
1452             offset += len;
1453             len = curbatch->es.length[dim + (curbatch->va->shadowed ? 0 : 1)] - offset;
1454             if(len) draws[dim].add(batchdrawinfo(*curbatch, dim, offset, len));
1455             dim++;
1456 
1457             if(curbatch->va->shadowed)
1458             {
1459                 offset += len;
1460                 len = curbatch->es.length[dim] - offset;
1461                 if(len) draws[dim].add(batchdrawinfo(*curbatch, dim, offset, len));
1462             }
1463             dim++;
1464         }
1465         if(curbatch->batch < 0) break;
1466     }
1467     loop(shadowed, 2)
1468     {
1469         bool rendered = false;
1470         loop(dim, 3)
1471         {
1472             vector<batchdrawinfo> &draw = draws[2*dim + shadowed];
1473             if(draw.empty()) continue;
1474 
1475             if(!rendered)
1476             {
1477                 if(renderpath!=R_FIXEDFUNCTION) changeshader(cur, b.slot.shader, b.slot, shadowed!=0);
1478                 rendered = true;
1479             }
1480             if(cur.texgendim!=dim || cur.mtglow>cur.mttexgen)
1481                 changetexgen(cur, b.slot, dim);
1482 
1483             gbatches++;
1484             loopv(draw)
1485             {
1486                 batchdrawinfo &info = draw[i];
1487                 drawtris(info.len, info.edata, info.minvert, info.maxvert);
1488                 vtris += info.len/3;
1489             }
1490             draw.setsizenodelete(0);
1491         }
1492     }
1493 }
1494 
resetbatches()1495 static void resetbatches()
1496 {
1497     geombatches.setsizenodelete(0);
1498     firstbatch = -1;
1499     numbatches = 0;
1500 }
1501 
renderbatches(renderstate & cur,int pass)1502 static void renderbatches(renderstate &cur, int pass)
1503 {
1504     cur.slot = NULL;
1505     int curbatch = firstbatch;
1506     if(curbatch >= 0)
1507     {
1508         if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1509         if(!cur.colormask) { cur.colormask = true; glColorMask(COLORMASK, GL_TRUE); }
1510     }
1511     while(curbatch >= 0)
1512     {
1513         geombatch &b = geombatches[curbatch];
1514         curbatch = b.next;
1515 
1516         if(cur.vbuf != b.va->vbuf)
1517         {
1518             changevbuf(cur, pass, b.va);
1519             if(pass == RENDERPASS_DYNLIGHT) changedynlightpos(cur, b.va);
1520             else changefogplane(cur, pass, b.va);
1521         }
1522         if(pass == RENDERPASS_LIGHTMAP)
1523         {
1524             changebatchtmus(cur, pass, b);
1525             if(cur.dynlightmask != b.va->dynlightmask)
1526             {
1527                 cur.visibledynlights = setdynlights(b.va, vaorigin);
1528                 cur.dynlightmask = b.va->dynlightmask;
1529             }
1530         }
1531         if(cur.slot != &b.slot) changeslottmus(cur, pass, b.slot);
1532 
1533         renderbatch(cur, pass, b);
1534     }
1535 
1536     if(pass == RENDERPASS_LIGHTMAP && cur.mtglow)
1537     {
1538         glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1539         glDisable(GL_TEXTURE_2D);
1540         glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1541         cur.mtglow = false;
1542     }
1543 
1544     resetbatches();
1545 }
1546 
renderzpass(renderstate & cur,vtxarray * va)1547 void renderzpass(renderstate &cur, vtxarray *va)
1548 {
1549     if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_Z, va);
1550     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
1551     if(cur.colormask) { cur.colormask = false; glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); }
1552 
1553     extern int apple_glsldepth_bug;
1554     if(renderpath!=R_GLSLANG || !apple_glsldepth_bug)
1555     {
1556         nocolorshader->set();
1557         drawvatris(va, 3*va->tris, va->edata);
1558     }
1559     else
1560     {
1561         static Shader *nocolorglslshader = NULL;
1562         if(!nocolorglslshader) nocolorglslshader = lookupshaderbyname("nocolorglsl");
1563         Slot *lastslot = NULL;
1564         int lastdraw = 0, offset = 0;
1565         loopi(va->texs)
1566         {
1567             Slot &slot = lookuptexture(va->eslist[i].texture);
1568             if(lastslot && (slot.shader->type&SHADER_GLSLANG) != (lastslot->shader->type&SHADER_GLSLANG) && offset > lastdraw)
1569             {
1570                 (lastslot->shader->type&SHADER_GLSLANG ? nocolorglslshader : nocolorshader)->set();
1571                 drawvatris(va, offset-lastdraw, va->edata+lastdraw);
1572                 lastdraw = offset;
1573             }
1574             lastslot = &slot;
1575             offset += va->eslist[i].length[5];
1576         }
1577         if(offset > lastdraw)
1578         {
1579             (lastslot->shader->type&SHADER_GLSLANG ? nocolorglslshader : nocolorshader)->set();
1580             drawvatris(va, offset-lastdraw, va->edata+lastdraw);
1581         }
1582     }
1583     xtravertsva += va->verts;
1584 }
1585 
1586 vector<vtxarray *> foggedvas;
1587 
1588 #define startvaquery(va, flush) \
1589     do { \
1590         if(!refracting) \
1591         { \
1592             occludequery *query = reflecting ? va->rquery : va->query; \
1593             if(query) \
1594             { \
1595                 flush; \
1596                 startquery(query); \
1597             } \
1598         } \
1599     } while(0)
1600 
1601 
1602 #define endvaquery(va, flush) \
1603     do { \
1604         if(!refracting) \
1605         { \
1606             occludequery *query = reflecting ? va->rquery : va->query; \
1607             if(query) \
1608             { \
1609                 flush; \
1610                 endquery(query); \
1611             } \
1612         } \
1613     } while(0)
1614 
renderfoggedvas(renderstate & cur,bool doquery=false)1615 void renderfoggedvas(renderstate &cur, bool doquery = false)
1616 {
1617     static Shader *fogshader = NULL;
1618     if(!fogshader) fogshader = lookupshaderbyname("fogworld");
1619     fogshader->set();
1620 
1621     glDisable(GL_TEXTURE_2D);
1622 
1623     glColor3ubv(watercol.v);
1624 
1625     loopv(foggedvas)
1626     {
1627         vtxarray *va = foggedvas[i];
1628         if(cur.vbuf!=va->vbuf) changevbuf(cur, RENDERPASS_FOG, va);
1629 
1630         if(doquery) startvaquery(va, );
1631         drawvatris(va, 3*va->tris, va->edata);
1632         vtris += va->tris;
1633         if(doquery) endvaquery(va, );
1634     }
1635 
1636     glEnable(GL_TEXTURE_2D);
1637 
1638     foggedvas.setsizenodelete(0);
1639 }
1640 
rendershadowmappass(renderstate & cur,vtxarray * va)1641 void rendershadowmappass(renderstate &cur, vtxarray *va)
1642 {
1643     if(cur.vbuf!=va->vbuf)
1644     {
1645         changevbuf(cur, RENDERPASS_SHADOWMAP, va);
1646         changefogplane(cur, RENDERPASS_SHADOWMAP, va);
1647     }
1648 
1649     elementset *texs = va->eslist;
1650     ushort *edata = va->edata;
1651     loopi(va->texs)
1652     {
1653         elementset &es = texs[i];
1654         int len = es.length[1] - es.length[0];
1655         if(len > 0)
1656         {
1657             drawtris(len, &edata[es.length[0]], es.minvert[1], es.maxvert[1]);
1658             vtris += len/3;
1659         }
1660         len = es.length[3] - es.length[2];
1661         if(len > 0)
1662         {
1663             drawtris(len, &edata[es.length[2]], es.minvert[3], es.maxvert[3]);
1664             vtris += len/3;
1665         }
1666         len = es.length[5] - es.length[4];
1667         if(len > 0)
1668         {
1669             drawtris(len, &edata[es.length[4]], es.minvert[5], es.maxvert[5]);
1670             vtris += len/3;
1671         }
1672         edata += es.length[5];
1673     }
1674 }
1675 
1676 VAR(batchgeom, 0, 1, 1);
1677 
renderva(renderstate & cur,vtxarray * va,int pass=RENDERPASS_LIGHTMAP,bool fogpass=false,bool doquery=false)1678 void renderva(renderstate &cur, vtxarray *va, int pass = RENDERPASS_LIGHTMAP, bool fogpass = false, bool doquery = false)
1679 {
1680     switch(pass)
1681     {
1682         case RENDERPASS_GLOW:
1683             if(!(va->texmask&(1<<TEX_GLOW))) return;
1684             mergeglowtexs(va);
1685             if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
1686             break;
1687 
1688         case RENDERPASS_COLOR:
1689         case RENDERPASS_LIGHTMAP:
1690             vverts += va->verts;
1691             va->shadowed = false;
1692             va->dynlightmask = 0;
1693             if(fogpass ? va->geommax.z<=reflectz-waterfog : va->curvfc==VFC_FOGGED)
1694             {
1695                 foggedvas.add(va);
1696                 break;
1697             }
1698             if(renderpath!=R_FIXEDFUNCTION && !envmapping && !glaring)
1699             {
1700                 va->shadowed = isshadowmapreceiver(va);
1701                 calcdynlightmask(va);
1702             }
1703             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1704             mergetexs(va);
1705             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, pass); });
1706             else if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
1707             break;
1708 
1709         case RENDERPASS_LIGHTMAP_BLEND:
1710         {
1711             if(doquery) startvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1712             ushort *edata = va->edata;
1713             loopi(va->texs) edata += va->eslist[i].length[5];
1714             mergetexs(va, &va->eslist[va->texs], va->blends, edata);
1715             if(doquery) endvaquery(va, { if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP); });
1716             else if(!batchgeom && geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
1717             break;
1718         }
1719 
1720         case RENDERPASS_DYNLIGHT:
1721             if(cur.dynlightpos.dist_to_bb(va->geommin, va->geommax) >= cur.dynlightradius) break;
1722             vverts += va->verts;
1723             mergetexs(va);
1724             if(!batchgeom && geombatches.length()) renderbatches(cur, pass);
1725             break;
1726 
1727         case RENDERPASS_FOG:
1728             if(cur.vbuf!=va->vbuf)
1729             {
1730                 changevbuf(cur, pass, va);
1731                 changefogplane(cur, pass, va);
1732             }
1733             drawvatris(va, 3*va->tris, va->edata);
1734             xtravertsva += va->verts;
1735             break;
1736 
1737         case RENDERPASS_SHADOWMAP:
1738             if(isshadowmapreceiver(va)) rendershadowmappass(cur, va);
1739             break;
1740 
1741         case RENDERPASS_CAUSTICS:
1742             if(cur.vbuf!=va->vbuf) changevbuf(cur, pass, va);
1743             drawvatris(va, 3*va->tris, va->edata);
1744             xtravertsva += va->verts;
1745             break;
1746 
1747         case RENDERPASS_Z:
1748             if(doquery) startvaquery(va, );
1749             renderzpass(cur, va);
1750             if(doquery) endvaquery(va, );
1751             break;
1752     }
1753 }
1754 
1755 VAR(oqdist, 0, 256, 1024);
1756 VAR(zpass, 0, 1, 1);
1757 VAR(glowpass, 0, 1, 1);
1758 
1759 GLuint fogtex = 0;
1760 
createfogtex()1761 void createfogtex()
1762 {
1763     extern int bilinear;
1764     uchar buf[2*256] = { 255, 0, 255, 255 };
1765     if(!bilinear) loopi(256) { buf[2*i] = 255; buf[2*i+1] = i; }
1766     glGenTextures(1, &fogtex);
1767     createtexture(fogtex, bilinear ? 2 : 256, 1, buf, 3, 1, GL_LUMINANCE_ALPHA, GL_TEXTURE_1D);
1768 }
1769 
1770 GLuint attenxytex = 0, attenztex = 0;
1771 
createattenxytex(int size)1772 static GLuint createattenxytex(int size)
1773 {
1774     uchar *data = new uchar[size*size], *dst = data;
1775     loop(y, size) loop(x, size)
1776     {
1777         float dx = 2*float(x)/(size-1) - 1, dy = 2*float(y)/(size-1) - 1;
1778         float atten = max(0.0f, 1.0f - dx*dx - dy*dy);
1779         *dst++ = uchar(atten*255);
1780     }
1781     GLuint tex = 0;
1782     glGenTextures(1, &tex);
1783     createtexture(tex, size, size, data, 3, 1, GL_ALPHA);
1784     delete[] data;
1785     return tex;
1786 }
1787 
createattenztex(int size)1788 static GLuint createattenztex(int size)
1789 {
1790     uchar *data = new uchar[size], *dst = data;
1791     loop(z, size)
1792     {
1793         float dz = 2*float(z)/(size-1) - 1;
1794         float atten = dz*dz;
1795         *dst++ = uchar(atten*255);
1796     }
1797     GLuint tex = 0;
1798     glGenTextures(1, &tex);
1799     createtexture(tex, size, 1, data, 3, 1, GL_ALPHA, GL_TEXTURE_1D);
1800     delete[] data;
1801     return tex;
1802 }
1803 
1804 Texture *caustic = NULL;
loadcaustic(const char * name)1805 void loadcaustic(const char *name)
1806 {
1807 	if(*name)
1808 	{
1809 		defformatstring(s)("%s%s", renderpath==R_FIXEDFUNCTION ? "<mad:0.6,0.4>" : "<mad:-0.6,0.6>", name);
1810 		caustic = textureload(s);
1811 	}
1812 	else caustic = notexture;
1813 }
1814 SVARFW(caustictex, "<anim:75>textures/caustics", loadcaustic(caustictex));
1815 
loadcaustics(bool force)1816 void loadcaustics(bool force)
1817 {
1818     static bool needcaustics = false;
1819     if(force) needcaustics = true;
1820     if(!caustics || !needcaustics) return;
1821     useshaderbyname("caustic");
1822     loadcaustic(caustictex);
1823 }
1824 
1825 VARW(causticscale, 0, 100, 10000);
1826 VARFP(caustics, 0, 1, 1, loadcaustics());
1827 
cleanupva()1828 void cleanupva()
1829 {
1830     clearvas(worldroot);
1831     clearqueries();
1832     if(fogtex) { glDeleteTextures(1, &fogtex); fogtex = 0; }
1833     if(attenxytex) { glDeleteTextures(1, &attenxytex); attenxytex = 0; }
1834     if(attenztex) { glDeleteTextures(1, &attenztex); attenztex = 0; }
1835     caustic = NULL;
1836 }
1837 
setupcaustics(int tmu,float blend,GLfloat * color=NULL)1838 void setupcaustics(int tmu, float blend, GLfloat *color = NULL)
1839 {
1840     if(!caustictex[0]) loadcaustics(true);
1841 
1842     GLfloat s[4] = { 0.011f, 0, 0.0066f, 0 };
1843     GLfloat t[4] = { 0, 0.011f, 0.0066f, 0 };
1844     loopk(3)
1845     {
1846         s[k] *= 100.0f/(causticscale<<VVEC_FRAC);
1847         t[k] *= 100.0f/(causticscale<<VVEC_FRAC);
1848     }
1849     int tex = (lastmillis/caustic->delay)%caustic->frames.length();
1850     float frac = float(lastmillis%caustic->delay)/caustic->delay;
1851     if(color) color[3] = frac;
1852     else glColor4f(1, 1, 1, frac);
1853     loopi(2)
1854     {
1855         glActiveTexture_(GL_TEXTURE0_ARB+tmu+i);
1856         glEnable(GL_TEXTURE_2D);
1857         glBindTexture(GL_TEXTURE_2D, caustic->frames[(tex+i)%caustic->frames.length()]);
1858         if(renderpath==R_FIXEDFUNCTION)
1859         {
1860             setuptexgen();
1861             setuptmu(tmu+i, !i ? "= T" : "T , P @ Ca");
1862             glTexGenfv(GL_S, GL_OBJECT_PLANE, s);
1863             glTexGenfv(GL_T, GL_OBJECT_PLANE, t);
1864         }
1865     }
1866     if(renderpath!=R_FIXEDFUNCTION)
1867     {
1868         static Shader *causticshader = NULL;
1869         if(!causticshader) causticshader = lookupshaderbyname("caustic");
1870         causticshader->set();
1871         setlocalparamfv("texgenS", SHPARAM_VERTEX, 0, s);
1872         setlocalparamfv("texgenT", SHPARAM_VERTEX, 1, t);
1873         setlocalparamf("frameoffset", SHPARAM_PIXEL, 0, blend*(1-frac), blend*frac, blend);
1874     }
1875 }
1876 
setupTMUs(renderstate & cur,float causticspass,bool fogpass)1877 void setupTMUs(renderstate &cur, float causticspass, bool fogpass)
1878 {
1879     if(!reflecting && !refracting && !envmapping && shadowmap && hasFBO)
1880     {
1881         glDisableClientState(GL_VERTEX_ARRAY);
1882 
1883         if(hasVBO)
1884         {
1885             glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
1886             glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
1887         }
1888 
1889         rendershadowmap();
1890 
1891         glEnableClientState(GL_VERTEX_ARRAY);
1892     }
1893 
1894     if(renderpath==R_FIXEDFUNCTION)
1895     {
1896         if(nolights) cur.lightmaptmu = -1;
1897         else if(maxtmus>=3)
1898         {
1899             if(maxtmus>=4 && causticspass>=1)
1900             {
1901                 cur.causticstmu = 0;
1902                 cur.diffusetmu = 2;
1903                 cur.lightmaptmu = 3;
1904                 if(maxtmus>=5)
1905                 {
1906                     if(fogpass)
1907                     {
1908                         if(glowpass && maxtmus>=6)
1909                         {
1910                             cur.fogtmu = 5;
1911                             cur.glowtmu = 4;
1912                         }
1913                         else cur.fogtmu = 4;
1914                     }
1915                     else if(glowpass) cur.glowtmu = 4;
1916                 }
1917             }
1918             else if(fogpass && causticspass<1) cur.fogtmu = 2;
1919             else if(glowpass) cur.glowtmu = 2;
1920         }
1921         if(cur.glowtmu>=0)
1922         {
1923             glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1924 			setuptexgen();
1925             setuptmu(cur.glowtmu, "P + T", "= Pa");
1926         }
1927         if(cur.fogtmu>=0)
1928 		{
1929             glActiveTexture_(GL_TEXTURE0_ARB+cur.fogtmu);
1930             glEnable(GL_TEXTURE_1D);
1931             setuptexgen(1);
1932             setuptmu(cur.fogtmu, "C , P @ Ta", "= Pa");
1933             if(!fogtex) createfogtex();
1934             glBindTexture(GL_TEXTURE_1D, fogtex);
1935             loopk(3) cur.color[k] = watercol[k]/255.0f;
1936         }
1937         if(cur.causticstmu>=0) setupcaustics(cur.causticstmu, causticspass, cur.color);
1938 	}
1939     else
1940 	{
1941         // need to invalidate vertex params in case they were used somewhere else for streaming params
1942         invalidateenvparams(SHPARAM_VERTEX, 10, RESERVEDSHADERPARAMS + MAXSHADERPARAMS - 10);
1943 		glEnableClientState(GL_COLOR_ARRAY);
1944 		loopi(8-2) { glActiveTexture_(GL_TEXTURE2_ARB+i); glEnable(GL_TEXTURE_2D); }
1945 		glActiveTexture_(GL_TEXTURE0_ARB);
1946 		setenvparamf("ambient", SHPARAM_PIXEL, 5, ambientcolor[0]/255.0f, ambientcolor[1]/255.0f, ambientcolor[2]/255.0f);
1947 		setenvparamf("millis", SHPARAM_VERTEX, 6, lastmillis/1000.0f, lastmillis/1000.0f, lastmillis/1000.0f);
1948 	}
1949 
1950     glColor4fv(cur.color);
1951 
1952     if(cur.lightmaptmu>=0)
1953     {
1954         glActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1955         glClientActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1956 
1957         setuptmu(cur.lightmaptmu, "P * T x 2", "= Ta");
1958 		glEnable(GL_TEXTURE_2D);
1959         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1960         glMatrixMode(GL_TEXTURE);
1961         glLoadIdentity();
1962         glScalef(1.0f/SHRT_MAX, 1.0f/SHRT_MAX, 1.0f);
1963         glMatrixMode(GL_MODELVIEW);
1964 
1965         glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1966         glClientActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
1967         glEnable(GL_TEXTURE_2D);
1968         setuptmu(cur.diffusetmu, cur.diffusetmu>0 ? "P * T" : "= T");
1969 	}
1970 
1971      if(renderpath==R_FIXEDFUNCTION) setuptexgen();
1972 }
1973 
cleanupTMUs(renderstate & cur)1974 void cleanupTMUs(renderstate &cur)
1975 {
1976     if(cur.lightmaptmu>=0)
1977     {
1978         glActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1979         glClientActiveTexture_(GL_TEXTURE0_ARB+cur.lightmaptmu);
1980 
1981         resettmu(cur.lightmaptmu);
1982 		glDisable(GL_TEXTURE_2D);
1983 		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1984 		glMatrixMode(GL_TEXTURE);
1985 		glLoadIdentity();
1986 		glMatrixMode(GL_MODELVIEW);
1987     }
1988     if(cur.glowtmu>=0)
1989     {
1990         glActiveTexture_(GL_TEXTURE0_ARB+cur.glowtmu);
1991         resettmu(cur.glowtmu);
1992         disabletexgen();
1993         glDisable(GL_TEXTURE_2D);
1994     }
1995     if(cur.fogtmu>=0)
1996     {
1997         glActiveTexture_(GL_TEXTURE0_ARB+cur.fogtmu);
1998         resettmu(cur.fogtmu);
1999         disabletexgen(1);
2000         glDisable(GL_TEXTURE_1D);
2001     }
2002     if(cur.causticstmu>=0) loopi(2)
2003     {
2004         glActiveTexture_(GL_TEXTURE0_ARB+cur.causticstmu+i);
2005         resettmu(cur.causticstmu+i);
2006         disabletexgen();
2007         glDisable(GL_TEXTURE_2D);
2008     }
2009 
2010     if(cur.lightmaptmu>=0)
2011 	{
2012         glActiveTexture_(GL_TEXTURE0_ARB+cur.diffusetmu);
2013         resettmu(cur.diffusetmu);
2014         glDisable(GL_TEXTURE_2D);
2015     }
2016 
2017     if(renderpath==R_FIXEDFUNCTION) disabletexgen();
2018     else
2019     {
2020         glDisableClientState(GL_COLOR_ARRAY);
2021         loopi(8-2) { glActiveTexture_(GL_TEXTURE2_ARB+i); glDisable(GL_TEXTURE_2D); }
2022 	}
2023 
2024     if(cur.lightmaptmu>=0)
2025     {
2026 		glActiveTexture_(GL_TEXTURE0_ARB);
2027 		glClientActiveTexture_(GL_TEXTURE0_ARB);
2028         glEnable(GL_TEXTURE_2D);
2029     }
2030 }
2031 
2032 #define FIRSTVA (reflecting ? reflectedva : visibleva)
2033 #define NEXTVA (reflecting ? va->rnext : va->next)
2034 
rendergeommultipass(renderstate & cur,int pass,bool fogpass)2035 static void rendergeommultipass(renderstate &cur, int pass, bool fogpass)
2036 {
2037     cur.vbuf = 0;
2038     for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
2039     {
2040         if(!va->texs || va->occluded >= OCCLUDE_GEOM) continue;
2041         if(refracting)
2042         {
2043             if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
2044             if(ishiddencube(va->o, va->size)) continue;
2045             if((!hasOQ || !oqfrags) && va->distance > reflectdist) break;
2046         }
2047         else if(reflecting)
2048         {
2049             if(va->geommax.z <= reflectz || (va->rquery && checkquery(va->rquery))) continue;
2050         }
2051         if(fogpass ? va->geommax.z <= reflectz-waterfog : va->curvfc==VFC_FOGGED) continue;
2052         renderva(cur, va, pass, fogpass);
2053     }
2054     if(geombatches.length()) renderbatches(cur, pass);
2055 }
2056 
2057 VAR(oqgeom, 0, 1, 1);
2058 VAR(oqbatch, 0, 1, 1);
2059 
2060 VAR(dbgffsm, 0, 0, 1);
2061 VAR(dbgffdl, 0, 0, 1);
2062 VAR(ffdlscissor, 0, 1, 1);
2063 
rendergeom(float causticspass,bool fogpass)2064 void rendergeom(float causticspass, bool fogpass)
2065 {
2066     renderstate cur;
2067 
2068     if(causticspass && ((renderpath==R_FIXEDFUNCTION && maxtmus<2) || !causticscale)) causticspass = 0;
2069 
2070 	glEnableClientState(GL_VERTEX_ARRAY);
2071 
2072 	if(!reflecting && !refracting)
2073 	{
2074 		flipqueries();
2075 		vtris = vverts = 0;
2076 	}
2077 
2078     bool doOQ = reflecting ? hasOQ && oqfrags && oqreflect : !refracting && zpass!=0;
2079     if(!doOQ)
2080     {
2081         setupTMUs(cur, causticspass, fogpass);
2082         if(shadowmap && !envmapping && !glaring && renderpath!=R_FIXEDFUNCTION) pushshadowmap();
2083     }
2084 
2085     int hasdynlights = finddynlights();
2086 
2087     glPushMatrix();
2088 
2089     resetorigin();
2090 
2091     resetbatches();
2092 
2093     int blends = 0;
2094 	for(vtxarray *va = FIRSTVA; va; va = NEXTVA)
2095 	{
2096 		if(!va->texs) continue;
2097         if(refracting)
2098         {
2099             if((refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) || va->occluded >= OCCLUDE_GEOM) continue;
2100             if(ishiddencube(va->o, va->size)) continue;
2101             if((!hasOQ || !oqfrags) && va->distance > reflectdist) break;
2102         }
2103         else if(reflecting)
2104         {
2105             if(va->geommax.z <= reflectz) continue;
2106 			if(doOQ)
2107 			{
2108 				va->rquery = newquery(&va->rquery);
2109 				if(!va->rquery) continue;
2110                 if(va->occluded >= OCCLUDE_BB || va->curvfc >= VFC_NOT_VISIBLE)
2111 				{
2112 					renderquery(cur, va->rquery, va);
2113 					continue;
2114 				}
2115 			}
2116 		}
2117         else if(hasOQ && oqfrags && (zpass || va->distance > oqdist) && !insideva(va, camera1->o) && oqgeom)
2118 		{
2119             if(!zpass && va->query && va->query->owner == va)
2120             {
2121                 if(checkquery(va->query)) va->occluded = min(va->occluded+1, int(OCCLUDE_BB));
2122                 else va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
2123             }
2124             if(zpass && oqbatch)
2125             {
2126                 if(va->parent && va->parent->occluded >= OCCLUDE_BB)
2127                 {
2128                     va->query = NULL;
2129                     va->occluded = OCCLUDE_PARENT;
2130                     continue;
2131                 }
2132                 bool succeeded = false;
2133                 if(va->query && va->query->owner == va && checkquery(va->query))
2134                 {
2135                     va->occluded = min(va->occluded+1, int(OCCLUDE_BB));
2136                     succeeded = true;
2137                 }
2138                 va->query = newquery(va);
2139                 if(!va->query || !succeeded)
2140                     va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
2141                 if(va->occluded >= OCCLUDE_GEOM)
2142                 {
2143                     if(va->query) renderquery(cur, va->query, va);
2144                     continue;
2145                 }
2146             }
2147             else if(zpass && va->parent &&
2148                (va->parent->occluded == OCCLUDE_PARENT ||
2149 				(va->parent->occluded >= OCCLUDE_BB &&
2150 				 va->parent->query && va->parent->query->owner == va->parent && va->parent->query->fragments < 0)))
2151 			{
2152 				va->query = NULL;
2153                 if(va->occluded >= OCCLUDE_GEOM || pvsoccluded(va->geommin, va->geommax))
2154 				{
2155 					va->occluded = OCCLUDE_PARENT;
2156 					continue;
2157 				}
2158 			}
2159 			else if(va->occluded >= OCCLUDE_GEOM)
2160 			{
2161 				va->query = newquery(va);
2162 				if(va->query) renderquery(cur, va->query, va);
2163 				continue;
2164 			}
2165 			else va->query = newquery(va);
2166 		}
2167 		else
2168 		{
2169 			va->query = NULL;
2170             va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
2171             if(va->occluded >= OCCLUDE_GEOM) continue;
2172 		}
2173 
2174         if(!doOQ) blends += va->blends;
2175         renderva(cur, va, doOQ ? RENDERPASS_Z : (nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP), fogpass, true);
2176     }
2177 
2178     if(geombatches.length()) renderbatches(cur, nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP);
2179 
2180     if(!cur.colormask) { cur.colormask = true; glColorMask(COLORMASK, GL_TRUE); }
2181     if(!cur.depthmask) { cur.depthmask = true; glDepthMask(GL_TRUE); }
2182 
2183 	if(doOQ)
2184 	{
2185         setupTMUs(cur, causticspass, fogpass);
2186         if(shadowmap && !envmapping && !glaring && renderpath!=R_FIXEDFUNCTION)
2187         {
2188             glPopMatrix();
2189             glPushMatrix();
2190             pushshadowmap();
2191             resetorigin();
2192         }
2193 		glDepthFunc(GL_LEQUAL);
2194         cur.vbuf = 0;
2195 
2196 		for(vtxarray **prevva = &FIRSTVA, *va = FIRSTVA; va; prevva = &NEXTVA, va = NEXTVA)
2197 		{
2198 			if(!va->texs) continue;
2199 			if(reflecting)
2200 			{
2201 				if(va->geommax.z <= reflectz) continue;
2202 				if(va->rquery && checkquery(va->rquery))
2203 				{
2204                     if(va->occluded >= OCCLUDE_BB || va->curvfc >= VFC_NOT_VISIBLE) *prevva = va->rnext;
2205 					continue;
2206 				}
2207 			}
2208             else if(oqbatch)
2209             {
2210                 if(va->occluded >= OCCLUDE_GEOM) continue;
2211             }
2212 			else if(va->parent && va->parent->occluded >= OCCLUDE_BB && (!va->parent->query || va->parent->query->fragments >= 0))
2213 			{
2214 				va->query = NULL;
2215 				va->occluded = OCCLUDE_BB;
2216 				continue;
2217 			}
2218             else
2219             {
2220                 if(va->query && checkquery(va->query)) va->occluded = min(va->occluded+1, int(OCCLUDE_BB));
2221                 else va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
2222                 if(va->occluded >= OCCLUDE_GEOM) continue;
2223             }
2224 
2225             blends += va->blends;
2226             renderva(cur, va, nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP, fogpass);
2227         }
2228         if(geombatches.length()) renderbatches(cur, nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP);
2229         if(oqbatch && !reflecting) for(vtxarray **prevva = &FIRSTVA, *va = FIRSTVA; va; prevva = &NEXTVA, va = NEXTVA)
2230         {
2231             if(!va->texs || va->occluded < OCCLUDE_GEOM) continue;
2232             else if(va->query && checkquery(va->query)) continue;
2233             else if(va->parent && (va->parent->occluded >= OCCLUDE_BB ||
2234                     (va->parent->occluded >= OCCLUDE_GEOM && va->parent->query && checkquery(va->parent->query))))
2235             {
2236                 va->occluded = OCCLUDE_BB;
2237                 continue;
2238             }
2239             else
2240             {
2241                 va->occluded = pvsoccluded(va->geommin, va->geommax) ? OCCLUDE_GEOM : OCCLUDE_NOTHING;
2242                 if(va->occluded >= OCCLUDE_GEOM) continue;
2243             }
2244 
2245             blends += va->blends;
2246             renderva(cur, va, nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP, fogpass);
2247         }
2248         if(geombatches.length()) renderbatches(cur, nolights ? RENDERPASS_COLOR : RENDERPASS_LIGHTMAP);
2249 
2250         if(foggedvas.empty()) glDepthFunc(GL_LESS);
2251     }
2252 
2253     if(blends && (renderpath!=R_FIXEDFUNCTION || !nolights))
2254     {
2255         if(shadowmap && !envmapping && !glaring && renderpath!=R_FIXEDFUNCTION)
2256         {
2257             glPopMatrix();
2258             glPushMatrix();
2259             resetorigin();
2260         }
2261         if(foggedvas.empty()) glDepthFunc(GL_LEQUAL);
2262         glDepthMask(GL_FALSE);
2263         glEnable(GL_BLEND);
2264         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2265         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
2266 
2267         cur.vbuf = 0;
2268         cur.blending = true;
2269         for(vtxarray **prevva = &FIRSTVA, *va = FIRSTVA; va; prevva = &NEXTVA, va = NEXTVA)
2270         {
2271             if(!va->blends || va->occluded >= OCCLUDE_GEOM) continue;
2272             if(refracting)
2273             {
2274                 if(refracting < 0 ? va->geommin.z > reflectz : va->geommax.z <= reflectz) continue;
2275                 if(ishiddencube(va->o, va->size)) continue;
2276                 if((!hasOQ || !oqfrags) && va->distance > reflectdist) break;
2277             }
2278             else if(reflecting)
2279             {
2280                 if(va->geommax.z <= reflectz || (va->rquery && checkquery(va->rquery))) continue;
2281             }
2282             if(fogpass ? va->geommax.z <= reflectz-waterfog : va->curvfc==VFC_FOGGED) continue;
2283             renderva(cur, va, RENDERPASS_LIGHTMAP_BLEND, fogpass);
2284         }
2285         if(geombatches.length()) renderbatches(cur, RENDERPASS_LIGHTMAP);
2286         cur.blending = false;
2287 
2288         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2289         glDisable(GL_BLEND);
2290         glDepthMask(GL_TRUE);
2291         if(foggedvas.empty()) glDepthFunc(GL_LESS);
2292     }
2293 
2294     if(shadowmap && !envmapping && !glaring && renderpath!=R_FIXEDFUNCTION) popshadowmap();
2295 
2296     cleanupTMUs(cur);
2297 
2298     if(foggedvas.length())
2299     {
2300         renderfoggedvas(cur, !doOQ);
2301         if(doOQ) glDepthFunc(GL_LESS);
2302     }
2303 
2304     if(renderpath==R_FIXEDFUNCTION ? (glowpass && cur.skippedglow) || (causticspass>=1 && cur.causticstmu<0) || (fogpass && cur.fogtmu<0) || (shadowmap && shadowmapcasters) || hasdynlights : causticspass)
2305     {
2306         glDepthFunc(GL_LEQUAL);
2307         glDepthMask(GL_FALSE);
2308         glEnable(GL_BLEND);
2309         static GLfloat zerofog[4] = { 0, 0, 0, 1 }, onefog[4] = { 1, 1, 1, 1 };
2310         GLfloat oldfogc[4];
2311         glGetFloatv(GL_FOG_COLOR, oldfogc);
2312 
2313         if(renderpath==R_FIXEDFUNCTION && glowpass && cur.skippedglow)
2314         {
2315             glBlendFunc(GL_ONE, GL_ONE);
2316             glFogfv(GL_FOG_COLOR, zerofog);
2317             setuptexgen();
2318             if(cur.fogtmu>=0)
2319             {
2320                 setuptmu(0, "C * T");
2321                 glActiveTexture_(GL_TEXTURE1_ARB);
2322                 glEnable(GL_TEXTURE_1D);
2323                 setuptexgen(1);
2324                 setuptmu(1, "P * T~a");
2325                 if(!fogtex) createfogtex();
2326                 glBindTexture(GL_TEXTURE_1D, fogtex);
2327                 glActiveTexture_(GL_TEXTURE0_ARB);
2328             }
2329             cur.glowcolor = vec(-1, -1, -1);
2330             cur.glowtmu = 0;
2331             rendergeommultipass(cur, RENDERPASS_GLOW, fogpass);
2332             disabletexgen();
2333             if(cur.fogtmu>=0)
2334             {
2335                 resettmu(0);
2336                 glActiveTexture_(GL_TEXTURE1_ARB);
2337                 resettmu(1);
2338                 disabletexgen(1);
2339                 glDisable(GL_TEXTURE_1D);
2340                 glActiveTexture_(GL_TEXTURE0_ARB);
2341             }
2342         }
2343 
2344         if(renderpath==R_FIXEDFUNCTION ? causticspass>=1 && cur.causticstmu<0 : causticspass)
2345         {
2346             setupcaustics(0, causticspass);
2347             glBlendFunc(GL_ZERO, renderpath==R_FIXEDFUNCTION ? GL_SRC_COLOR : GL_ONE_MINUS_SRC_COLOR);
2348             glFogfv(GL_FOG_COLOR, renderpath==R_FIXEDFUNCTION ? onefog : zerofog);
2349             if(fading) glColorMask(COLORMASK, GL_FALSE);
2350             rendergeommultipass(cur, RENDERPASS_CAUSTICS, fogpass);
2351             if(fading) glColorMask(COLORMASK, GL_TRUE);
2352             loopi(2)
2353             {
2354                 glActiveTexture_(GL_TEXTURE0_ARB+i);
2355                 resettmu(i);
2356                 if(renderpath==R_FIXEDFUNCTION || !i)
2357                 {
2358                     resettmu(i);
2359                     disabletexgen();
2360                 }
2361                 if(i) glDisable(GL_TEXTURE_2D);
2362             }
2363             glActiveTexture_(GL_TEXTURE0_ARB);
2364         }
2365 
2366         if(renderpath==R_FIXEDFUNCTION && shadowmap && shadowmapcasters)
2367         {
2368             glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2369             glFogfv(GL_FOG_COLOR, zerofog);
2370             glPopMatrix();
2371             glPushMatrix();
2372             pushshadowmap();
2373             resetorigin();
2374             if(cur.fogtmu>=0)
2375             {
2376                 setuptmu(0, "C * T");
2377                 glActiveTexture_(GL_TEXTURE1_ARB);
2378                 glEnable(GL_TEXTURE_1D);
2379                 setuptexgen(1);
2380                 setuptmu(1, "P * T~a");
2381                 if(!fogtex) createfogtex();
2382                 glBindTexture(GL_TEXTURE_1D, fogtex);
2383                 glActiveTexture_(GL_TEXTURE0_ARB);
2384             }
2385             if(dbgffsm) { glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor3f(1, 0, 1); }
2386             rendergeommultipass(cur, RENDERPASS_SHADOWMAP, fogpass);
2387             if(dbgffsm) { glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); }
2388             popshadowmap();
2389             if(cur.fogtmu>=0)
2390             {
2391                 resettmu(0);
2392                 glActiveTexture_(GL_TEXTURE1_ARB);
2393                 resettmu(1);
2394                 disabletexgen();
2395                 glDisable(GL_TEXTURE_1D);
2396                 glActiveTexture_(GL_TEXTURE0_ARB);
2397             }
2398         }
2399 
2400         if(renderpath==R_FIXEDFUNCTION && hasdynlights)
2401         {
2402             glBlendFunc(GL_SRC_ALPHA, dbgffdl ? GL_ZERO : GL_ONE);
2403             glFogfv(GL_FOG_COLOR, zerofog);
2404 
2405             if(!attenxytex) attenxytex = createattenxytex(64);
2406             glBindTexture(GL_TEXTURE_2D, attenxytex);
2407 
2408             setuptmu(0, "= C", "= Ta");
2409             setuptexgen();
2410 
2411             glActiveTexture_(GL_TEXTURE1_ARB);
2412             setuptmu(1, "= P", "Pa - Ta");
2413             setuptexgen(1);
2414             if(!attenztex) attenztex = createattenztex(64);
2415             glBindTexture(GL_TEXTURE_1D, attenztex);
2416             glEnable(GL_TEXTURE_1D);
2417 
2418             glActiveTexture_(GL_TEXTURE2_ARB);
2419             cur.diffusetmu = 2;
2420             setuptmu(2, "P * T x 4", "= Pa");
2421             setuptexgen();
2422             glEnable(GL_TEXTURE_2D);
2423 
2424             vec lightcolor;
2425             for(int n = 0; getdynlight(n, cur.dynlightpos, cur.dynlightradius, lightcolor); n++)
2426             {
2427                 lightcolor.mul(0.5f);
2428                 if(fogpass && cur.fogtmu>=0)
2429                 {
2430                     float fog = (reflectz - cur.dynlightpos.z)/waterfog;
2431                     if(fog >= 1.0f) continue;
2432                     lightcolor.mul(1.0f - max(fog, 0.0f));
2433                 }
2434                 glColor3f(lightcolor.x, lightcolor.y, lightcolor.z);
2435                 if(ffdlscissor)
2436                 {
2437                     float sx1, sy1, sx2, sy2;
2438                     calcspherescissor(cur.dynlightpos, cur.dynlightradius, sx1, sy1, sx2, sy2);
2439                     pushscissor(sx1, sy1, sx2, sy2);
2440                 }
2441                 resetorigin();
2442                 rendergeommultipass(cur, RENDERPASS_DYNLIGHT, fogpass);
2443                 if(ffdlscissor) popscissor();
2444             }
2445 
2446             glDisable(GL_TEXTURE_2D);
2447             disabletexgen();
2448             resettmu(2);
2449 
2450             glActiveTexture_(GL_TEXTURE1_ARB);
2451             glDisable(GL_TEXTURE_1D);
2452             resettmu(1);
2453             disabletexgen(1);
2454 
2455             glActiveTexture_(GL_TEXTURE0_ARB);
2456             resettmu(0);
2457             disabletexgen();
2458         }
2459 
2460         if(renderpath==R_FIXEDFUNCTION && fogpass && cur.fogtmu<0)
2461 		{
2462             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2463             glDisable(GL_TEXTURE_2D);
2464             glEnable(GL_TEXTURE_1D);
2465             setuptexgen(1);
2466             if(!fogtex) createfogtex();
2467             glBindTexture(GL_TEXTURE_1D, fogtex);
2468             setuptexgen(1);
2469             glColor3ubv(watercol.v);
2470             rendergeommultipass(cur, RENDERPASS_FOG, fogpass);
2471             disabletexgen(1);
2472             glDisable(GL_TEXTURE_1D);
2473             glEnable(GL_TEXTURE_2D);
2474         }
2475 
2476         glFogfv(GL_FOG_COLOR, oldfogc);
2477         glDisable(GL_BLEND);
2478         glDepthFunc(GL_LESS);
2479         glDepthMask(GL_TRUE);
2480 	}
2481 
2482 	glPopMatrix();
2483 
2484     if(hasVBO)
2485     {
2486         glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
2487         glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
2488     }
2489     glDisableClientState(GL_VERTEX_ARRAY);
2490 }
2491 
findreflectedvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)2492 void findreflectedvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
2493 {
2494     bool doOQ = hasOQ && oqfrags && oqreflect;
2495     loopv(vas)
2496     {
2497         vtxarray *va = vas[i];
2498         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
2499         if(va->curvfc == VFC_FOGGED || va->curvfc == PVS_FOGGED || va->o.z+va->size <= reflectz || isfoggedcube(va->o, va->size)) continue;
2500         bool render = true;
2501         if(va->curvfc == VFC_FULL_VISIBLE)
2502         {
2503             if(va->occluded >= OCCLUDE_BB) continue;
2504             if(va->occluded >= OCCLUDE_GEOM) render = false;
2505         }
2506         else if(va->curvfc == PVS_FULL_VISIBLE) continue;
2507         if(render)
2508         {
2509             if(va->curvfc >= VFC_NOT_VISIBLE) va->distance = (int)vadist(va, camera1->o);
2510             if(!doOQ && va->distance > reflectdist) continue;
2511             va->rquery = NULL;
2512             vtxarray **vprev = &reflectedva, *vcur = reflectedva;
2513             while(vcur && va->distance > vcur->distance)
2514             {
2515                 vprev = &vcur->rnext;
2516                 vcur = vcur->rnext;
2517             }
2518             va->rnext = *vprev;
2519             *vprev = va;
2520         }
2521         if(va->children.length()) findreflectedvas(va->children, va->curvfc);
2522     }
2523 }
2524 
renderreflectedgeom(bool causticspass,bool fogpass)2525 void renderreflectedgeom(bool causticspass, bool fogpass)
2526 {
2527     if(reflecting)
2528     {
2529         reflectedva = NULL;
2530         findreflectedvas(varoot);
2531         rendergeom(causticspass ? 1 : 0, fogpass);
2532     }
2533     else rendergeom(causticspass ? 1 : 0, fogpass);
2534 }
2535 
2536 static vtxarray *prevskyva = NULL;
2537 
renderskyva(vtxarray * va,bool explicitonly=false)2538 void renderskyva(vtxarray *va, bool explicitonly = false)
2539 {
2540     if(!prevskyva || va->vbuf != prevskyva->vbuf)
2541     {
2542         if(!prevskyva)
2543         {
2544             glEnableClientState(GL_VERTEX_ARRAY);
2545             glPushMatrix();
2546             resetorigin();
2547         }
2548 
2549         setorigin(va);
2550         if(hasVBO)
2551         {
2552             glBindBuffer_(GL_ARRAY_BUFFER_ARB, va->vbuf);
2553             glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, va->skybuf);
2554         }
2555         glVertexPointer(3, floatvtx ? GL_FLOAT : GL_SHORT, VTXSIZE, &va->vdata[0].x);
2556     }
2557 
2558     drawvatris(va, explicitonly ? va->explicitsky : va->sky+va->explicitsky, explicitonly ? va->skydata+va->sky : va->skydata);
2559 
2560     if(!explicitonly) xtraverts += va->sky/3;
2561     xtraverts += va->explicitsky/3;
2562 
2563     prevskyva = va;
2564 }
2565 
2566 int renderedsky = 0, renderedexplicitsky = 0, renderedskyfaces = 0, renderedskyclip = INT_MAX;
2567 
updateskystats(vtxarray * va)2568 static inline void updateskystats(vtxarray *va)
2569 {
2570     renderedsky += va->sky;
2571     renderedexplicitsky += va->explicitsky;
2572     renderedskyfaces |= va->skyfaces&0x3F;
2573     if(!(va->skyfaces&0x1F) || camera1->o.z < va->skyclip) renderedskyclip = min(renderedskyclip, va->skyclip);
2574     else renderedskyclip = 0;
2575 }
2576 
renderreflectedskyvas(vector<vtxarray * > & vas,int prevvfc=VFC_PART_VISIBLE)2577 void renderreflectedskyvas(vector<vtxarray *> &vas, int prevvfc = VFC_PART_VISIBLE)
2578 {
2579     loopv(vas)
2580     {
2581         vtxarray *va = vas[i];
2582         if(prevvfc >= VFC_NOT_VISIBLE) va->curvfc = prevvfc;
2583         if((va->curvfc == VFC_FULL_VISIBLE && va->occluded >= OCCLUDE_BB) || va->curvfc==PVS_FULL_VISIBLE) continue;
2584         if(va->o.z+va->size <= reflectz || ishiddencube(va->o, va->size)) continue;
2585         if(va->sky+va->explicitsky)
2586         {
2587             updateskystats(va);
2588             renderskyva(va);
2589         }
2590         if(va->children.length()) renderreflectedskyvas(va->children, va->curvfc);
2591     }
2592 }
2593 
rendersky(bool explicitonly)2594 bool rendersky(bool explicitonly)
2595 {
2596     prevskyva = NULL;
2597     renderedsky = renderedexplicitsky = renderedskyfaces = 0;
2598     renderedskyclip = INT_MAX;
2599 
2600     if(reflecting)
2601     {
2602         renderreflectedskyvas(varoot);
2603     }
2604     else for(vtxarray *va = visibleva; va; va = va->next)
2605     {
2606         if((va->occluded >= OCCLUDE_BB && va->skyfaces&0x80) || !(va->sky+va->explicitsky)) continue;
2607 
2608         // count possibly visible sky even if not actually rendered
2609         updateskystats(va);
2610         if(explicitonly && !va->explicitsky) continue;
2611         renderskyva(va, explicitonly);
2612     }
2613 
2614     if(prevskyva)
2615     {
2616         glPopMatrix();
2617         glDisableClientState(GL_VERTEX_ARRAY);
2618         if(hasVBO)
2619         {
2620             glBindBuffer_(GL_ARRAY_BUFFER_ARB, 0);
2621             glBindBuffer_(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
2622         }
2623     }
2624 
2625     return renderedsky+renderedexplicitsky > 0;
2626 }
2627 
2628