misc. cleanup, bubble trails fixed, improved lightmap compatibility.
[xonotic/darkplaces.git] / gl_poly.c
1 #include "quakedef.h"
2
3 transvert_t *transvert;
4 transpoly_t *transpoly;
5 unsigned short *transpolyindex;
6 wallvert_t *wallvert;
7 wallpoly_t *wallpoly;
8 skyvert_t *skyvert;
9 skypoly_t *skypoly;
10
11 unsigned short currenttranspoly;
12 unsigned short currenttransvert;
13 unsigned short currentwallpoly;
14 unsigned short currentwallvert;
15 unsigned short currentskypoly;
16 unsigned short currentskyvert;
17
18 cvar_t gl_multitexture = {"gl_multitexture", "1"};
19 cvar_t gl_vertexarrays = {"gl_vertexarrays", "1"};
20
21 void glpoly_init()
22 {
23         Cvar_RegisterVariable (&gl_multitexture);
24         Cvar_RegisterVariable (&gl_vertexarrays);
25         transvert = malloc(MAX_TRANSVERTS * sizeof(transvert_t));
26         transpoly = malloc(MAX_TRANSPOLYS * sizeof(transpoly_t));
27         transpolyindex = malloc(MAX_TRANSPOLYS * sizeof(unsigned short));
28         wallvert = malloc(MAX_WALLVERTS * sizeof(wallvert_t));
29         wallpoly = malloc(MAX_WALLPOLYS * sizeof(wallpoly_t));
30         skyvert = malloc(MAX_SKYVERTS * sizeof(skyvert_t));
31         skypoly = malloc(MAX_SKYPOLYS * sizeof(skypoly_t));
32 }
33
34 void transpolyclear()
35 {
36         currenttranspoly = currenttransvert = 0;
37 }
38
39 void transpolybegin(int texnum, int glowtexnum, int fogtexnum, int transpolytype)
40 {
41         if (currenttranspoly >= MAX_TRANSPOLYS || currenttransvert >= MAX_TRANSVERTS)
42                 return;
43         transpoly[currenttranspoly].texnum = (unsigned short) texnum;
44         transpoly[currenttranspoly].glowtexnum = (unsigned short) glowtexnum;
45         transpoly[currenttranspoly].fogtexnum = (unsigned short) fogtexnum;
46         transpoly[currenttranspoly].transpolytype = (unsigned short) transpolytype;
47         transpoly[currenttranspoly].firstvert = currenttransvert;
48         transpoly[currenttranspoly].verts = 0;
49 //      transpoly[currenttranspoly].ndist = 0; // clear the normal
50 }
51
52 // turned into a #define
53 /*
54 void transpolyvert(float x, float y, float z, float s, float t, int r, int g, int b, int a)
55 {
56         int i;
57         if (currenttranspoly >= MAX_TRANSPOLYS || currenttransvert >= MAX_TRANSVERTS)
58                 return;
59         transvert[currenttransvert].s = s;
60         transvert[currenttransvert].t = t;
61         transvert[currenttransvert].r = bound(0, r, 255);
62         transvert[currenttransvert].g = bound(0, g, 255);
63         transvert[currenttransvert].b = bound(0, b, 255);
64         transvert[currenttransvert].a = bound(0, a, 255);
65         transvert[currenttransvert].v[0] = x;
66         transvert[currenttransvert].v[1] = y;
67         transvert[currenttransvert].v[2] = z;
68         currenttransvert++;
69         transpoly[currenttranspoly].verts++;
70 }
71 */
72
73 void transpolyend()
74 {
75         if (currenttranspoly >= MAX_TRANSPOLYS)
76                 return;
77         if (transpoly[currenttranspoly].verts < 3) // skip invalid polygons
78         {
79                 currenttransvert = transpoly[currenttranspoly].firstvert; // reset vert pointer
80                 return;
81         }
82         if (currenttransvert >= MAX_TRANSVERTS)
83                 return;
84         currenttranspoly++;
85 }
86
87 int transpolyindices;
88 void transpolyrenderminmax()
89 {
90         int i, j, k, lastvert;
91         vec_t d, min, max, viewdist, s, average;
92         //vec_t ndist;
93         //vec3_t v1, v2, n;
94         transpolyindices = 0;
95         viewdist = DotProduct(r_refdef.vieworg, vpn);
96         for (i = 0;i < currenttranspoly;i++)
97         {
98                 if (transpoly[i].verts < 3) // only process valid polygons
99                         continue;
100                 min = 1000000;max = -1000000;
101                 s = 1.0f / transpoly[i].verts;
102                 lastvert = transpoly[i].firstvert + transpoly[i].verts;
103                 average = 0;
104                 for (j = transpoly[i].firstvert;j < lastvert;j++)
105                 {
106                         d = DotProduct(transvert[j].v, vpn)-viewdist;
107                         if (d < min) min = d;
108                         if (d > max) max = d;
109                         average += d * s;
110                 }
111                 if (max < 4) // free to check here, so skip polys behind the view
112                         continue;
113                 transpoly[i].distance = average;
114                 /*
115                 transpoly[i].mindistance = min;
116                 transpoly[i].maxdistance = max;
117                 // calculate normal (eek)
118                 VectorSubtract(transvert[transpoly[i].firstvert  ].v, transvert[transpoly[i].firstvert+1].v, v1);
119                 VectorSubtract(transvert[transpoly[i].firstvert+2].v, transvert[transpoly[i].firstvert+1].v, v2);
120                 VectorNormalize(v1);
121                 VectorNormalize(v2);
122                 if (transpoly[i].verts > 3 && fabs(DotProduct(v1, v2)) >= (1.0f - (1.0f / 256.0f))) // colinear edges, find a better triple
123                 {
124                         VectorSubtract(transvert[transpoly[i].firstvert + transpoly[i].verts - 1].v, transvert[transpoly[i].firstvert].v, v1);
125                         VectorSubtract(transvert[transpoly[i].firstvert + 1].v, transvert[transpoly[i].firstvert].v, v2);
126                         VectorNormalize(v1);
127                         VectorNormalize(v2);
128                         if (fabs(DotProduct(v1, v2)) < (1.0f - (1.0f / 256.0f))) // found a good triple
129                                 goto foundtriple;
130                         for (k = transpoly[i].firstvert + 2;k < (transpoly[i].firstvert + transpoly[i].verts - 1);k++)
131                         {
132                                 VectorSubtract(transvert[k-1].v, transvert[k].v, v1);
133                                 VectorSubtract(transvert[k+1].v, transvert[k].v, v2);
134                                 VectorNormalize(v1);
135                                 VectorNormalize(v2);
136                                 if (fabs(DotProduct(v1, v2)) < (1.0f - (1.0f / 256.0f))) // found a good triple
137                                         goto foundtriple;
138                         }
139                         VectorSubtract(transvert[k-1].v, transvert[k].v, v1);
140                         VectorSubtract(transvert[transpoly[i].firstvert].v, transvert[k].v, v2);
141                         VectorNormalize(v1);
142                         VectorNormalize(v2);
143                         if (fabs(DotProduct(v1, v2)) >= (1.0f - (1.0f / 256.0f))) // no good triples; the polygon is a line, skip it
144                                 continue;
145                 }
146 foundtriple:
147                 CrossProduct(v1, v2, n);
148                 VectorNormalize(n);
149                 ndist = DotProduct(transvert[transpoly[i].firstvert+1].v, n);
150                 // sorted insert
151                 for (j = 0;j < transpolyindices;j++)
152                 {
153                         // easy cases
154                         if (transpoly[transpolyindex[j]].mindistance > max)
155                                 continue;
156                         if (transpoly[transpolyindex[j]].maxdistance < min)
157                                 break;
158                         // hard case, check side
159                         for (k = transpoly[transpolyindex[j]].firstvert;k < (transpoly[transpolyindex[j]].firstvert + transpoly[transpolyindex[j]].verts);k++)
160                                 if (DotProduct(transvert[k].v, n) < ndist)
161                                         goto skip;
162                         break;
163 skip:
164                         ;
165                 }
166                 */
167                 // sorted insert
168                 for (j = 0;j < transpolyindices;j++)
169                         if (transpoly[transpolyindex[j]].distance < average)
170                                 break;
171                 for (k = transpolyindices;k > j;k--)
172                         transpolyindex[k] = transpolyindex[k-1];
173                 transpolyindices++;
174                 transpolyindex[j] = i;
175         }
176 }
177
178 // LordHavoc: qsort compare function
179 /*
180 int transpolyqsort(const void *ia, const void *ib)
181 {
182         transpoly_t *a, *b;
183         int i, j;
184         a = &transpoly[*((unsigned short *)ia)];
185         b = &transpoly[*((unsigned short *)ib)];
186         // easy cases
187         if (a->mindistance > b->mindistance && a->maxdistance > b->maxdistance)
188                 return -1; // behind
189         if (a->mindistance < b->mindistance && a->maxdistance < b->maxdistance)
190                 return 1; // infront
191         // hard case
192         if (!a->ndist)
193         {
194                 // calculate normal (eek)
195                 vec3_t v1, v2;
196                 VectorSubtract(transvert[a->firstvert  ].v, transvert[a->firstvert+1].v, v1);
197                 VectorSubtract(transvert[a->firstvert+2].v, transvert[a->firstvert+1].v, v2);
198                 CrossProduct(v1, v2, a->n);
199                 VectorNormalize(a->n);
200                 a->ndist = DotProduct(transvert[a->firstvert  ].v, a->n);
201         }
202         // check side
203         for (i = b->firstvert, j = 0;i < (b->firstvert + b->verts);i++)
204                 j += DotProduct(transvert[i].v, a->n) < a->ndist; // (1) b is infront of a
205         if (j == 0)
206                 return -1; // (-1) a is behind b
207         return j == b->verts; // (1) a is infront of b    (0) a and b intersect
208 //      return (transpoly[*((unsigned short *)ib)].mindistance + transpoly[*((unsigned short *)ib)].maxdistance) - (transpoly[*((unsigned short *)ia)].mindistance + transpoly[*((unsigned short *)ia)].maxdistance);
209 }
210 */
211
212 extern qboolean isG200;
213
214 /*
215 void transpolysort()
216 {
217         int i, j, a;
218 //      qsort(&transpolyindex[0], transpolyindices, sizeof(unsigned short), transpolyqsort);
219         a = true;
220         while(a)
221         {
222                 a = false;
223                 for (i = 1;i < transpolyindices;i++)
224                 {
225                         // easy cases
226                         if (transpoly[transpolyindex[i - 1]].mindistance > transpoly[transpolyindex[i]].mindistance && transpoly[transpolyindex[i - 1]].maxdistance > transpoly[transpolyindex[i]].maxdistance)
227                                 continue; // previous is behind (no swap)
228                         if (transpoly[transpolyindex[i - 1]].mindistance < transpoly[transpolyindex[i]].mindistance && transpoly[transpolyindex[i - 1]].maxdistance < transpoly[transpolyindex[i]].maxdistance)
229                                 goto swap; // previous is infront (swap)
230                         // hard case
231 */
232                         /*
233                         if (!transpoly[transpolyindex[i - 1]].ndist)
234                         {
235                                 // calculate normal (eek)
236                                 vec3_t v1, v2;
237                                 VectorSubtract(transvert[transpoly[transpolyindex[i - 1]].firstvert  ].v, transvert[transpoly[transpolyindex[i - 1]].firstvert+1].v, v1);
238                                 VectorSubtract(transvert[transpoly[transpolyindex[i - 1]].firstvert+2].v, transvert[transpoly[transpolyindex[i - 1]].firstvert+1].v, v2);
239                                 CrossProduct(v1, v2, transpoly[transpolyindex[i - 1]].n);
240                                 VectorNormalize(transpoly[transpolyindex[i - 1]].n);
241                                 transpoly[transpolyindex[i - 1]].ndist = DotProduct(transvert[transpoly[transpolyindex[i - 1]].firstvert  ].v, transpoly[transpolyindex[i - 1]].n);
242                         }
243                         if (DotProduct(transpoly[transpolyindex[i - 1]].n, vpn) >= 0.0f) // backface
244                                 continue;
245                         */
246 /*
247                         // check side
248                         for (i = transpoly[transpolyindex[i]].firstvert;i < (transpoly[transpolyindex[i]].firstvert + transpoly[transpolyindex[i]].verts);i++)
249                                 if (DotProduct(transvert[i].v, transpoly[transpolyindex[i - 1]].n) >= transpoly[transpolyindex[i - 1]].ndist)
250                                         goto noswap; // previous is behind or they intersect
251 swap:
252                         // previous is infront (swap)
253                         j = transpolyindex[i];
254                         transpolyindex[i] = transpolyindex[i - 1];
255                         transpolyindex[i - 1] = j;
256                         a = true;
257 noswap:
258                         ;
259                 }
260         }
261 }
262 */
263
264 void transpolyrender()
265 {
266         int i, j, tpolytype, texnum;
267         transpoly_t *p;
268         if (currenttranspoly < 1)
269                 return;
270         transpolyrenderminmax();
271         if (transpolyindices < 1)
272                 return;
273         // testing
274 //      Con_DPrintf("transpolyrender: %i polys %i infront %i vertices\n", currenttranspoly, transpolyindices, currenttransvert);
275 //      if (transpolyindices >= 2)
276 //              transpolysort();
277         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
278         glEnable(GL_BLEND);
279         glShadeModel(GL_SMOOTH);
280         glDepthMask(0); // disable zbuffer updates
281         if (isG200) // Matrox G200 cards can't handle per pixel alpha
282                 glEnable(GL_ALPHA_TEST);
283         else
284                 glDisable(GL_ALPHA_TEST);
285         // later note: wasn't working on my TNT drivers...  strangely...  used a cheaper hack in transpolyvert
286         //// offset by 16 depth units so decal sprites appear infront of walls
287         //glPolygonOffset(1, -16);
288         //glEnable(GL_POLYGON_OFFSET_FILL);
289         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
290         tpolytype = TPOLYTYPE_ALPHA;
291         texnum = -1;
292         /*
293         if (gl_vertexarrays.value)
294         {
295                 // set up the vertex array
296                 qglInterleavedArrays(GL_T2F_C4UB_V3F, 0, transvert);
297                 for (i = 0;i < transpolyindices;i++)
298                 {
299                         p = &transpoly[transpolyindex[i]];
300                         if (p->texnum != texnum || p->transpolytype != tpolytype)
301                         {
302                                 if (p->texnum != texnum)
303                                 {
304                                         texnum = p->texnum;
305                                         glBindTexture(GL_TEXTURE_2D, texnum);
306                                 }
307                                 if (p->transpolytype != tpolytype)
308                                 {
309                                         tpolytype = p->transpolytype;
310                                         if (tpolytype == TPOLYTYPE_ADD) // additive
311                                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
312                                         else // alpha
313                                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
314                                 }
315                         }
316                         qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
317                         if (p->glowtexnum)
318                         {
319                                 texnum = p->glowtexnum; // highly unlikely to match next poly, but...
320                                 glBindTexture(GL_TEXTURE_2D, texnum);
321                                 tpolytype = TPOLYTYPE_ADD; // might match next poly
322                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
323                                 qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
324                         }
325                 }
326                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
327                 glDisableClientState(GL_COLOR_ARRAY);
328                 glDisableClientState(GL_VERTEX_ARRAY);
329         }
330         else
331         */
332         {
333                 int points = -1;
334                 transvert_t *vert;
335                 for (i = 0;i < transpolyindices;i++)
336                 {
337                         p = &transpoly[transpolyindex[i]];
338                         if (p->texnum != texnum || p->verts != points || p->transpolytype != tpolytype)
339                         {
340                                 glEnd();
341                                 if (isG200)
342                                 {
343                                         if (p->fogtexnum) // alpha
344                                                 glEnable(GL_ALPHA_TEST);
345                                         else
346                                                 glDisable(GL_ALPHA_TEST);
347                                 }
348                                 if (p->texnum != texnum)
349                                 {
350                                         texnum = p->texnum;
351                                         glBindTexture(GL_TEXTURE_2D, texnum);
352                                 }
353                                 if (p->transpolytype != tpolytype)
354                                 {
355                                         tpolytype = p->transpolytype;
356                                         if (tpolytype == TPOLYTYPE_ADD) // additive
357                                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
358                                         else // alpha
359                                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
360                                 }
361                                 points = p->verts;
362                                 switch (points)
363                                 {
364                                 case 3:
365                                         glBegin(GL_TRIANGLES);
366                                         break;
367                                 case 4:
368                                         glBegin(GL_QUADS);
369                                         break;
370                                 default:
371                                         glBegin(GL_TRIANGLE_FAN);
372                                         points = -1; // to force a reinit on the next poly
373                                         break;
374                                 }
375                         }
376                         for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
377                         {
378                                 // would be 2fv, but windoze Matrox G200 and probably G400 drivers don't support that (dumb...)
379                                 glTexCoord2f(vert->s, vert->t);
380                                 // again, vector version isn't supported I think
381                                 glColor4ub(vert->r, vert->g, vert->b, vert->a);
382                                 glVertex3fv(vert->v);
383                         }
384                         if (p->glowtexnum)
385                         {
386                                 glEnd();
387                                 texnum = p->glowtexnum; // highly unlikely to match next poly, but...
388                                 glBindTexture(GL_TEXTURE_2D, texnum);
389                                 if (tpolytype != TPOLYTYPE_ADD)
390                                 {
391                                         tpolytype = TPOLYTYPE_ADD; // might match next poly
392                                         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
393                                 }
394                                 points = -1;
395                                 glBegin(GL_TRIANGLE_FAN);
396                                 for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
397                                 {
398                                         glColor4ub(255,255,255,vert->a);
399                                         // would be 2fv, but windoze Matrox G200 and probably G400 drivers don't support that (dumb...)
400                                         glTexCoord2f(vert->s, vert->t);
401                                         glVertex3fv(vert->v);
402                                 }
403                                 glEnd();
404                         }
405                         if (fogenabled && p->transpolytype == TPOLYTYPE_ALPHA)
406                         {
407                                 vec3_t diff;
408                                 glEnd();
409                                 points = -1; // to force a reinit on the next poly
410                                 if (tpolytype != TPOLYTYPE_ALPHA)
411                                 {
412                                         tpolytype = TPOLYTYPE_ALPHA; // probably matchs next poly
413                                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
414                                 }
415                                 if (p->fogtexnum)
416                                 {
417                                         if (texnum != p->fogtexnum) // highly unlikely to match next poly, but...
418                                         {
419                                                 texnum = p->fogtexnum;
420                                                 glBindTexture(GL_TEXTURE_2D, texnum);
421                                         }
422                                         glBegin(GL_TRIANGLE_FAN);
423                                         for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
424                                         {
425                                                 VectorSubtract(vert->v, r_refdef.vieworg,diff);
426                                                 glTexCoord2f(vert->s, vert->t);
427                                                 glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], vert->a*(1.0f/255.0f)*exp(fogdensity/DotProduct(diff,diff)));
428                                                 glVertex3fv(vert->v);
429                                         }
430                                         glEnd ();
431                                 }
432                                 else
433                                 {
434                                         glDisable(GL_TEXTURE_2D);
435                                         glBegin(GL_TRIANGLE_FAN);
436                                         for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
437                                         {
438                                                 VectorSubtract(vert->v, r_refdef.vieworg,diff);
439                                                 glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], vert->a*(1.0f/255.0f)*exp(fogdensity/DotProduct(diff,diff)));
440                                                 glVertex3fv(vert->v);
441                                         }
442                                         glEnd ();
443                                         glEnable(GL_TEXTURE_2D);
444                                 }
445                         }
446                 }
447                 glEnd();
448         }
449
450         //glDisable(GL_POLYGON_OFFSET_FILL);
451         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
452         glDepthMask(1); // enable zbuffer updates
453         glDisable(GL_ALPHA_TEST);
454 }
455
456 void wallpolyclear()
457 {
458         currentwallpoly = currentwallvert = 0;
459 }
460
461 extern qboolean lighthalf;
462 void wallpolyrender()
463 {
464         int i, j, texnum, lighttexnum;
465         wallpoly_t *p;
466         wallvert_t *vert;
467         if (currentwallpoly < 1)
468                 return;
469         // testing
470         //Con_DPrintf("wallpolyrender: %i polys %i vertices\n", currentwallpoly, currentwallvert);
471         if (!gl_mtexable)
472                 gl_multitexture.value = 0;
473         glDisable(GL_BLEND);
474         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
475         glShadeModel(GL_FLAT);
476         // make sure zbuffer is enabled
477         glEnable(GL_DEPTH_TEST);
478         glDisable(GL_ALPHA_TEST);
479         glDepthMask(1);
480         glColor3f(1,1,1);
481         if (r_fullbright.value) // LordHavoc: easy to do fullbright...
482         {
483                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
484                 texnum = -1;
485                 for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
486                 {
487                         if (p->texnum != texnum)
488                         {
489                                 texnum = p->texnum;
490                                 glBindTexture(GL_TEXTURE_2D, texnum);
491                         }
492                         vert = &wallvert[p->firstvert];
493                         glBegin(GL_POLYGON);
494                         for (j=0 ; j<p->verts ; j++, vert++)
495                         {
496                                 glTexCoord2f (vert->s, vert->t);
497                                 glVertex3fv (vert->vert);
498                         }
499                         glEnd ();
500                 }
501         }
502         else if (gl_multitexture.value)
503         {
504                 qglSelectTexture(gl_mtex_enum+0);
505                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
506                 glEnable(GL_TEXTURE_2D);
507                 qglSelectTexture(gl_mtex_enum+1);
508                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
509                 glEnable(GL_TEXTURE_2D);
510                 texnum = -1;
511                 lighttexnum = -1;
512                 for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
513                 {
514 //                      if (p->texnum != texnum || p->lighttexnum != lighttexnum)
515 //                      {
516                                 texnum = p->texnum;
517                                 lighttexnum = p->lighttexnum;
518                                 qglSelectTexture(gl_mtex_enum+0);
519                                 glBindTexture(GL_TEXTURE_2D, texnum);
520                                 qglSelectTexture(gl_mtex_enum+1);
521                                 glBindTexture(GL_TEXTURE_2D, lighttexnum);
522 //                      }
523                         vert = &wallvert[p->firstvert];
524                         glBegin(GL_POLYGON);
525                         for (j=0 ; j<p->verts ; j++, vert++)
526                         {
527                                 qglMTexCoord2f(gl_mtex_enum, vert->s, vert->t); // texture
528                                 qglMTexCoord2f((gl_mtex_enum+1), vert->u, vert->v); // lightmap
529                                 glVertex3fv (vert->vert);
530                         }
531                         glEnd ();
532                 }
533
534                 qglSelectTexture(gl_mtex_enum+1);
535                 glDisable(GL_TEXTURE_2D);
536                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
537                 qglSelectTexture(gl_mtex_enum+0);
538                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
539         }
540         else
541         {
542                 // first do the textures
543                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
544                 texnum = -1;
545                 for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
546                 {
547                         if (p->texnum != texnum)
548                         {
549                                 texnum = p->texnum;
550                                 glBindTexture(GL_TEXTURE_2D, texnum);
551                         }
552                         vert = &wallvert[p->firstvert];
553                         glBegin(GL_POLYGON);
554                         for (j=0 ; j<p->verts ; j++, vert++)
555                         {
556                                 glTexCoord2f (vert->s, vert->t);
557                                 glVertex3fv (vert->vert);
558                         }
559                         glEnd ();
560                 }
561                 // then modulate using the lightmaps
562                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
563                 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
564                 glEnable(GL_BLEND);
565                 texnum = -1;
566                 for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
567                 {
568                         if (p->lighttexnum != texnum)
569                         {
570                                 texnum = p->lighttexnum;
571                                 glBindTexture(GL_TEXTURE_2D, texnum);
572                         }
573                         vert = &wallvert[p->firstvert];
574                         glBegin(GL_POLYGON);
575                         for (j=0 ; j<p->verts ; j++, vert++)
576                         {
577                                 glTexCoord2f (vert->u, vert->v);
578                                 glVertex3fv (vert->vert);
579                         }
580                         glEnd ();
581                 }
582         }
583         // render glow textures
584         glDepthMask(0);
585         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
586         glBlendFunc(GL_ONE, GL_ONE);
587         glEnable(GL_BLEND);
588         if (lighthalf)
589                 glColor3f(0.5,0.5,0.5);
590         else
591                 glColor3f(1,1,1);
592         texnum = -1;
593         for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
594         {
595                 if (!p->glowtexnum)
596                         continue;
597                 if (p->glowtexnum != texnum)
598                 {
599                         texnum = p->glowtexnum;
600                         glBindTexture(GL_TEXTURE_2D, texnum);
601                 }
602                 vert = &wallvert[p->firstvert];
603                 glBegin(GL_POLYGON);
604                 for (j=0 ; j<p->verts ; j++, vert++)
605                 {
606                         glTexCoord2f (vert->s, vert->t);
607                         glVertex3fv (vert->vert);
608                 }
609                 glEnd ();
610         }
611         glColor3f(1,1,1);
612         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
613         glShadeModel(GL_SMOOTH);
614         if (fogenabled)
615         {
616                 vec3_t diff;
617                 glDisable(GL_TEXTURE_2D);
618                 for (i = 0,p = &wallpoly[0];i < currentwallpoly;i++, p++)
619                 {
620                         vert = &wallvert[p->firstvert];
621                         glBegin(GL_POLYGON);
622                         for (j=0 ; j<p->verts ; j++, vert++)
623                         {
624                                 VectorSubtract(vert->vert, r_refdef.vieworg,diff);
625                                 glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], exp(fogdensity/DotProduct(diff,diff)));
626                                 glVertex3fv (vert->vert);
627                         }
628                         glEnd ();
629                 }
630                 glEnable(GL_TEXTURE_2D);
631         }
632         glDisable(GL_BLEND);
633         glDepthMask(1);
634 }
635
636 void skypolyclear()
637 {
638         currentskypoly = currentskyvert = 0;
639 }
640
641 extern qboolean isATI;
642
643 extern char skyname[];
644 extern int solidskytexture, alphaskytexture;
645 void skypolyrender()
646 {
647         int i, j;
648         skypoly_t *p;
649         skyvert_t *vert;
650         float length, speedscale;
651         vec3_t dir;
652         if (currentskypoly < 1)
653                 return;
654         // testing
655 //      Con_DPrintf("skypolyrender: %i polys %i vertices\n", currentskypoly, currentskyvert);
656         glDisable(GL_ALPHA_TEST);
657         glDisable(GL_BLEND);
658         // make sure zbuffer is enabled
659         glEnable(GL_DEPTH_TEST);
660         glDepthMask(1);
661         if (!fogenabled && !skyname[0]) // normal quake sky
662         {
663                 glColor3f(0.5f, 0.5f, 0.5f);
664                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
665                 glEnable(GL_TEXTURE_2D);
666                 glDisable(GL_BLEND);
667                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
668                 glBindTexture(GL_TEXTURE_2D, solidskytexture); // upper clouds
669                 speedscale = realtime*8;
670                 speedscale -= (int)speedscale & ~127 ;
671                 for (i = 0,p = &skypoly[0];i < currentskypoly;i++, p++)
672                 {
673                         vert = &skyvert[p->firstvert];
674                         glBegin(GL_POLYGON);
675                         for (j=0 ; j<p->verts ; j++, vert++)
676                         {
677                                 VectorSubtract (vert->v, r_origin, dir);
678                                 dir[2] *= 3;    // flatten the sphere
679
680                                 length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
681                                 length = sqrt (length);
682                                 length = 6*63/length;
683
684                                 glTexCoord2f ((speedscale + dir[0] * length) * (1.0/128), (speedscale + dir[1] * length) * (1.0/128));
685                                 glVertex3fv (vert->v);
686                         }
687                         glEnd ();
688                 }
689                 glEnable(GL_BLEND);
690                 glDepthMask(0);
691                 glBindTexture(GL_TEXTURE_2D, alphaskytexture); // lower clouds
692                 speedscale = realtime*16;
693                 speedscale -= (int)speedscale & ~127 ;
694                 for (i = 0,p = &skypoly[0];i < currentskypoly;i++, p++)
695                 {
696                         vert = &skyvert[p->firstvert];
697                         glBegin(GL_POLYGON);
698                         for (j=0 ; j<p->verts ; j++, vert++)
699                         {
700                                 VectorSubtract (vert->v, r_origin, dir);
701                                 dir[2] *= 3;    // flatten the sphere
702
703                                 length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
704                                 length = sqrt (length);
705                                 length = 6*63/length;
706
707                                 glTexCoord2f ((speedscale + dir[0] * length) * (1.0/128), (speedscale + dir[1] * length) * (1.0/128));
708                                 glVertex3fv (vert->v);
709                         }
710                         glEnd ();
711                 }
712                 glDisable(GL_BLEND);
713                 glColor3f(1,1,1);
714                 glDepthMask(1);
715         }
716         else
717         {
718                 glDisable(GL_TEXTURE_2D);
719                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
720                 glColor3fv(fogcolor); // note: gets rendered over by skybox if fog is not enabled
721                 for (i = 0,p = &skypoly[0];i < currentskypoly;i++, p++)
722                 {
723                         vert = &skyvert[p->firstvert];
724                         glBegin(GL_POLYGON);
725                         for (j=0 ; j<p->verts ; j++, vert++)
726                                 glVertex3fv (vert->v);
727                         glEnd ();
728                 }
729                 glColor3f(1,1,1);
730                 glEnable(GL_TEXTURE_2D);
731         }
732 }