]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_rsurf.c
temporarily disabled compiled rtlights (they'll make a comeback after dynamic rtlight...
[divverent/darkplaces.git] / gl_rsurf.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // r_surf.c: surface-related refresh code
21
22 #include "quakedef.h"
23 #include "r_shadow.h"
24
25 #define MAX_LIGHTMAP_SIZE 256
26
27 static unsigned int intblocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*3]; // LordHavoc: *3 for colored lighting
28 static float floatblocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*3]; // LordHavoc: *3 for colored lighting
29
30 static qbyte templight[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*4];
31
32 cvar_t r_ambient = {0, "r_ambient", "0"};
33 cvar_t r_drawportals = {0, "r_drawportals", "0"};
34 cvar_t r_testvis = {0, "r_testvis", "0"};
35 cvar_t r_floatbuildlightmap = {0, "r_floatbuildlightmap", "0"};
36 cvar_t r_detailtextures = {CVAR_SAVE, "r_detailtextures", "1"};
37 cvar_t r_surfaceworldnode = {0, "r_surfaceworldnode", "1"};
38 cvar_t r_drawcollisionbrushes_polygonfactor = {0, "r_drawcollisionbrushes_polygonfactor", "-1"};
39 cvar_t r_drawcollisionbrushes_polygonoffset = {0, "r_drawcollisionbrushes_polygonoffset", "0"};
40 cvar_t gl_lightmaps = {0, "gl_lightmaps", "0"};
41
42 static int dlightdivtable[32768];
43
44 static int R_IntAddDynamicLights (const matrix4x4_t *matrix, msurface_t *surf)
45 {
46         int sdtable[256], lnum, td, maxdist, maxdist2, maxdist3, i, s, t, smax, tmax, smax3, red, green, blue, lit, dist2, impacts, impactt, subtract, k;
47         unsigned int *bl;
48         float dist, impact[3], local[3];
49         dlight_t *light;
50
51         lit = false;
52
53         smax = (surf->extents[0] >> 4) + 1;
54         tmax = (surf->extents[1] >> 4) + 1;
55         smax3 = smax * 3;
56
57         for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
58         {
59                 if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31))))
60                         continue;                                       // not lit by this light
61
62                 Matrix4x4_Transform(matrix, light->origin, local);
63                 dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
64
65                 // for comparisons to minimum acceptable light
66                 // compensate for LIGHTOFFSET
67                 maxdist = (int) light->rtlight.lightmap_cullradius2 + LIGHTOFFSET;
68
69                 dist2 = dist * dist;
70                 dist2 += LIGHTOFFSET;
71                 if (dist2 >= maxdist)
72                         continue;
73
74                 if (surf->plane->type < 3)
75                 {
76                         VectorCopy(local, impact);
77                         impact[surf->plane->type] -= dist;
78                 }
79                 else
80                 {
81                         impact[0] = local[0] - surf->plane->normal[0] * dist;
82                         impact[1] = local[1] - surf->plane->normal[1] * dist;
83                         impact[2] = local[2] - surf->plane->normal[2] * dist;
84                 }
85
86                 impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
87                 impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
88
89                 s = bound(0, impacts, smax * 16) - impacts;
90                 t = bound(0, impactt, tmax * 16) - impactt;
91                 i = s * s + t * t + dist2;
92                 if (i > maxdist)
93                         continue;
94
95                 // reduce calculations
96                 for (s = 0, i = impacts; s < smax; s++, i -= 16)
97                         sdtable[s] = i * i + dist2;
98
99                 maxdist3 = maxdist - dist2;
100
101                 // convert to 8.8 blocklights format
102                 red = light->rtlight.lightmap_light[0] * (1.0f / 128.0f);
103                 green = light->rtlight.lightmap_light[1] * (1.0f / 128.0f);
104                 blue = light->rtlight.lightmap_light[2] * (1.0f / 128.0f);
105                 subtract = (int) (light->rtlight.lightmap_subtract * 4194304.0f);
106                 bl = intblocklights;
107
108                 i = impactt;
109                 for (t = 0;t < tmax;t++, i -= 16)
110                 {
111                         td = i * i;
112                         // make sure some part of it is visible on this line
113                         if (td < maxdist3)
114                         {
115                                 maxdist2 = maxdist - td;
116                                 for (s = 0;s < smax;s++)
117                                 {
118                                         if (sdtable[s] < maxdist2)
119                                         {
120                                                 k = dlightdivtable[(sdtable[s] + td) >> 7] - subtract;
121                                                 if (k > 0)
122                                                 {
123                                                         bl[0] += (red   * k);
124                                                         bl[1] += (green * k);
125                                                         bl[2] += (blue  * k);
126                                                         lit = true;
127                                                 }
128                                         }
129                                         bl += 3;
130                                 }
131                         }
132                         else // skip line
133                                 bl += smax3;
134                 }
135         }
136         return lit;
137 }
138
139 static int R_FloatAddDynamicLights (const matrix4x4_t *matrix, msurface_t *surf)
140 {
141         int lnum, s, t, smax, tmax, smax3, lit, impacts, impactt;
142         float sdtable[256], *bl, k, dist, dist2, maxdist, maxdist2, maxdist3, td1, td, red, green, blue, impact[3], local[3], subtract;
143         dlight_t *light;
144
145         lit = false;
146
147         smax = (surf->extents[0] >> 4) + 1;
148         tmax = (surf->extents[1] >> 4) + 1;
149         smax3 = smax * 3;
150
151         for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
152         {
153                 if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31))))
154                         continue;                                       // not lit by this light
155
156                 Matrix4x4_Transform(matrix, light->origin, local);
157                 dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
158
159                 // for comparisons to minimum acceptable light
160                 // compensate for LIGHTOFFSET
161                 maxdist = (int) light->rtlight.lightmap_cullradius2 + LIGHTOFFSET;
162
163                 dist2 = dist * dist;
164                 dist2 += LIGHTOFFSET;
165                 if (dist2 >= maxdist)
166                         continue;
167
168                 if (surf->plane->type < 3)
169                 {
170                         VectorCopy(local, impact);
171                         impact[surf->plane->type] -= dist;
172                 }
173                 else
174                 {
175                         impact[0] = local[0] - surf->plane->normal[0] * dist;
176                         impact[1] = local[1] - surf->plane->normal[1] * dist;
177                         impact[2] = local[2] - surf->plane->normal[2] * dist;
178                 }
179
180                 impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
181                 impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
182
183                 td = bound(0, impacts, smax * 16) - impacts;
184                 td1 = bound(0, impactt, tmax * 16) - impactt;
185                 td = td * td + td1 * td1 + dist2;
186                 if (td > maxdist)
187                         continue;
188
189                 // reduce calculations
190                 for (s = 0, td1 = impacts; s < smax; s++, td1 -= 16.0f)
191                         sdtable[s] = td1 * td1 + dist2;
192
193                 maxdist3 = maxdist - dist2;
194
195                 // convert to 8.8 blocklights format
196                 red = light->rtlight.lightmap_light[0];
197                 green = light->rtlight.lightmap_light[1];
198                 blue = light->rtlight.lightmap_light[2];
199                 subtract = light->rtlight.lightmap_subtract * 32768.0f;
200                 bl = floatblocklights;
201
202                 td1 = impactt;
203                 for (t = 0;t < tmax;t++, td1 -= 16.0f)
204                 {
205                         td = td1 * td1;
206                         // make sure some part of it is visible on this line
207                         if (td < maxdist3)
208                         {
209                                 maxdist2 = maxdist - td;
210                                 for (s = 0;s < smax;s++)
211                                 {
212                                         if (sdtable[s] < maxdist2)
213                                         {
214                                                 k = (32768.0f / (sdtable[s] + td)) - subtract;
215                                                 bl[0] += red   * k;
216                                                 bl[1] += green * k;
217                                                 bl[2] += blue  * k;
218                                                 lit = true;
219                                         }
220                                         bl += 3;
221                                 }
222                         }
223                         else // skip line
224                                 bl += smax3;
225                 }
226         }
227         return lit;
228 }
229
230 /*
231 ===============
232 R_BuildLightMap
233
234 Combine and scale multiple lightmaps into the 8.8 format in blocklights
235 ===============
236 */
237 static void R_BuildLightMap (const entity_render_t *ent, msurface_t *surf)
238 {
239         if (!r_floatbuildlightmap.integer)
240         {
241                 int smax, tmax, i, j, size, size3, maps, stride, l;
242                 unsigned int *bl, scale;
243                 qbyte *lightmap, *out, *stain;
244
245                 // update cached lighting info
246                 surf->cached_dlight = 0;
247
248                 smax = (surf->extents[0]>>4)+1;
249                 tmax = (surf->extents[1]>>4)+1;
250                 size = smax*tmax;
251                 size3 = size*3;
252                 lightmap = surf->samples;
253
254         // set to full bright if no light data
255                 bl = intblocklights;
256                 if (!ent->model->brushq1.lightdata)
257                 {
258                         for (i = 0;i < size3;i++)
259                                 bl[i] = 255*256;
260                 }
261                 else
262                 {
263         // clear to no light
264                         j = r_ambient.value * 512.0f; // would be 128.0f logically, but using 512.0f to match winquake style
265                         if (j)
266                         {
267                                 for (i = 0;i < size3;i++)
268                                         *bl++ = j;
269                         }
270                         else
271                                 memset(bl, 0, size*3*sizeof(unsigned int));
272
273                         if (surf->dlightframe == r_framecount)
274                         {
275                                 surf->cached_dlight = R_IntAddDynamicLights(&ent->inversematrix, surf);
276                                 if (surf->cached_dlight)
277                                         c_light_polys++;
278                         }
279
280         // add all the lightmaps
281                         if (lightmap)
282                         {
283                                 bl = intblocklights;
284                                 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++, lightmap += size3)
285                                         for (scale = d_lightstylevalue[surf->styles[maps]], i = 0;i < size3;i++)
286                                                 bl[i] += lightmap[i] * scale;
287                         }
288                 }
289
290                 stain = surf->stainsamples;
291                 bl = intblocklights;
292                 out = templight;
293                 // the >> 16 shift adjusts down 8 bits to account for the stainmap
294                 // scaling, and remaps the 0-65536 (2x overbright) to 0-256, it will
295                 // be doubled during rendering to achieve 2x overbright
296                 // (0 = 0.0, 128 = 1.0, 256 = 2.0)
297                 if (ent->model->brushq1.lightmaprgba)
298                 {
299                         stride = (surf->lightmaptexturestride - smax) * 4;
300                         for (i = 0;i < tmax;i++, out += stride)
301                         {
302                                 for (j = 0;j < smax;j++)
303                                 {
304                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
305                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
306                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
307                                         *out++ = 255;
308                                 }
309                         }
310                 }
311                 else
312                 {
313                         stride = (surf->lightmaptexturestride - smax) * 3;
314                         for (i = 0;i < tmax;i++, out += stride)
315                         {
316                                 for (j = 0;j < smax;j++)
317                                 {
318                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
319                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
320                                         l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
321                                 }
322                         }
323                 }
324
325                 R_UpdateTexture(surf->lightmaptexture, templight);
326         }
327         else
328         {
329                 int smax, tmax, i, j, size, size3, maps, stride, l;
330                 float *bl, scale;
331                 qbyte *lightmap, *out, *stain;
332
333                 // update cached lighting info
334                 surf->cached_dlight = 0;
335
336                 smax = (surf->extents[0]>>4)+1;
337                 tmax = (surf->extents[1]>>4)+1;
338                 size = smax*tmax;
339                 size3 = size*3;
340                 lightmap = surf->samples;
341
342         // set to full bright if no light data
343                 bl = floatblocklights;
344                 if (!ent->model->brushq1.lightdata)
345                         j = 255*256;
346                 else
347                         j = r_ambient.value * 512.0f; // would be 128.0f logically, but using 512.0f to match winquake style
348
349                 // clear to no light
350                 if (j)
351                 {
352                         for (i = 0;i < size3;i++)
353                                 *bl++ = j;
354                 }
355                 else
356                         memset(bl, 0, size*3*sizeof(float));
357
358                 if (surf->dlightframe == r_framecount)
359                 {
360                         surf->cached_dlight = R_FloatAddDynamicLights(&ent->inversematrix, surf);
361                         if (surf->cached_dlight)
362                                 c_light_polys++;
363                 }
364
365                 // add all the lightmaps
366                 if (lightmap)
367                 {
368                         bl = floatblocklights;
369                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++, lightmap += size3)
370                                 for (scale = d_lightstylevalue[surf->styles[maps]], i = 0;i < size3;i++)
371                                         bl[i] += lightmap[i] * scale;
372                 }
373
374                 stain = surf->stainsamples;
375                 bl = floatblocklights;
376                 out = templight;
377                 // this scaling adjusts down 8 bits to account for the stainmap
378                 // scaling, and remaps the 0.0-2.0 (2x overbright) to 0-256, it will
379                 // be doubled during rendering to achieve 2x overbright
380                 // (0 = 0.0, 128 = 1.0, 256 = 2.0)
381                 scale = 1.0f / (1 << 16);
382                 if (ent->model->brushq1.lightmaprgba)
383                 {
384                         stride = (surf->lightmaptexturestride - smax) * 4;
385                         for (i = 0;i < tmax;i++, out += stride)
386                         {
387                                 for (j = 0;j < smax;j++)
388                                 {
389                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
390                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
391                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
392                                         *out++ = 255;
393                                 }
394                         }
395                 }
396                 else
397                 {
398                         stride = (surf->lightmaptexturestride - smax) * 3;
399                         for (i = 0;i < tmax;i++, out += stride)
400                         {
401                                 for (j = 0;j < smax;j++)
402                                 {
403                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
404                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
405                                         l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
406                                 }
407                         }
408                 }
409
410                 R_UpdateTexture(surf->lightmaptexture, templight);
411         }
412 }
413
414 void R_StainNode (mnode_t *node, model_t *model, const vec3_t origin, float radius, const float fcolor[8])
415 {
416         float ndist, a, ratio, maxdist, maxdist2, maxdist3, invradius, sdtable[256], td, dist2;
417         msurface_t *surf, *endsurf;
418         int i, s, t, smax, tmax, smax3, impacts, impactt, stained;
419         qbyte *bl;
420         vec3_t impact;
421
422         maxdist = radius * radius;
423         invradius = 1.0f / radius;
424
425 loc0:
426         if (node->contents < 0)
427                 return;
428         ndist = PlaneDiff(origin, node->plane);
429         if (ndist > radius)
430         {
431                 node = node->children[0];
432                 goto loc0;
433         }
434         if (ndist < -radius)
435         {
436                 node = node->children[1];
437                 goto loc0;
438         }
439
440         dist2 = ndist * ndist;
441         maxdist3 = maxdist - dist2;
442
443         if (node->plane->type < 3)
444         {
445                 VectorCopy(origin, impact);
446                 impact[node->plane->type] -= ndist;
447         }
448         else
449         {
450                 impact[0] = origin[0] - node->plane->normal[0] * ndist;
451                 impact[1] = origin[1] - node->plane->normal[1] * ndist;
452                 impact[2] = origin[2] - node->plane->normal[2] * ndist;
453         }
454
455         for (surf = model->brushq1.surfaces + node->firstsurface, endsurf = surf + node->numsurfaces;surf < endsurf;surf++)
456         {
457                 if (surf->stainsamples)
458                 {
459                         smax = (surf->extents[0] >> 4) + 1;
460                         tmax = (surf->extents[1] >> 4) + 1;
461
462                         impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
463                         impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
464
465                         s = bound(0, impacts, smax * 16) - impacts;
466                         t = bound(0, impactt, tmax * 16) - impactt;
467                         i = s * s + t * t + dist2;
468                         if (i > maxdist)
469                                 continue;
470
471                         // reduce calculations
472                         for (s = 0, i = impacts; s < smax; s++, i -= 16)
473                                 sdtable[s] = i * i + dist2;
474
475                         bl = surf->stainsamples;
476                         smax3 = smax * 3;
477                         stained = false;
478
479                         i = impactt;
480                         for (t = 0;t < tmax;t++, i -= 16)
481                         {
482                                 td = i * i;
483                                 // make sure some part of it is visible on this line
484                                 if (td < maxdist3)
485                                 {
486                                         maxdist2 = maxdist - td;
487                                         for (s = 0;s < smax;s++)
488                                         {
489                                                 if (sdtable[s] < maxdist2)
490                                                 {
491                                                         ratio = lhrandom(0.0f, 1.0f);
492                                                         a = (fcolor[3] + ratio * fcolor[7]) * (1.0f - sqrt(sdtable[s] + td) * invradius);
493                                                         if (a >= (1.0f / 64.0f))
494                                                         {
495                                                                 if (a > 1)
496                                                                         a = 1;
497                                                                 bl[0] = (qbyte) ((float) bl[0] + a * ((fcolor[0] + ratio * fcolor[4]) - (float) bl[0]));
498                                                                 bl[1] = (qbyte) ((float) bl[1] + a * ((fcolor[1] + ratio * fcolor[5]) - (float) bl[1]));
499                                                                 bl[2] = (qbyte) ((float) bl[2] + a * ((fcolor[2] + ratio * fcolor[6]) - (float) bl[2]));
500                                                                 stained = true;
501                                                         }
502                                                 }
503                                                 bl += 3;
504                                         }
505                                 }
506                                 else // skip line
507                                         bl += smax3;
508                         }
509                         // force lightmap upload
510                         if (stained)
511                                 surf->cached_dlight = true;
512                 }
513         }
514
515         if (node->children[0]->contents >= 0)
516         {
517                 if (node->children[1]->contents >= 0)
518                 {
519                         R_StainNode(node->children[0], model, origin, radius, fcolor);
520                         node = node->children[1];
521                         goto loc0;
522                 }
523                 else
524                 {
525                         node = node->children[0];
526                         goto loc0;
527                 }
528         }
529         else if (node->children[1]->contents >= 0)
530         {
531                 node = node->children[1];
532                 goto loc0;
533         }
534 }
535
536 void R_Stain (const vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
537 {
538         int n;
539         float fcolor[8];
540         entity_render_t *ent;
541         model_t *model;
542         vec3_t org;
543         if (cl.worldmodel == NULL || !cl.worldmodel->brushq1.nodes)
544                 return;
545         fcolor[0] = cr1;
546         fcolor[1] = cg1;
547         fcolor[2] = cb1;
548         fcolor[3] = ca1 * (1.0f / 64.0f);
549         fcolor[4] = cr2 - cr1;
550         fcolor[5] = cg2 - cg1;
551         fcolor[6] = cb2 - cb1;
552         fcolor[7] = (ca2 - ca1) * (1.0f / 64.0f);
553
554         R_StainNode(cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, cl.worldmodel, origin, radius, fcolor);
555
556         // look for embedded bmodels
557         for (n = 0;n < cl_num_brushmodel_entities;n++)
558         {
559                 ent = cl_brushmodel_entities[n];
560                 model = ent->model;
561                 if (model && model->name[0] == '*')
562                 {
563                         Mod_CheckLoaded(model);
564                         if (model->brushq1.nodes)
565                         {
566                                 Matrix4x4_Transform(&ent->inversematrix, origin, org);
567                                 R_StainNode(model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, model, org, radius, fcolor);
568                         }
569                 }
570         }
571 }
572
573
574 /*
575 =============================================================
576
577         BRUSH MODELS
578
579 =============================================================
580 */
581
582 static void RSurf_AddLightmapToVertexColors_Color4f(const int *lightmapoffsets, float *c, int numverts, const qbyte *samples, int size3, const qbyte *styles)
583 {
584         int i;
585         float scale;
586         const qbyte *lm;
587         if (styles[0] != 255)
588         {
589                 for (i = 0;i < numverts;i++, c += 4)
590                 {
591                         lm = samples + lightmapoffsets[i];
592                         scale = d_lightstylevalue[styles[0]] * (1.0f / 32768.0f);
593                         VectorMA(c, scale, lm, c);
594                         if (styles[1] != 255)
595                         {
596                                 lm += size3;
597                                 scale = d_lightstylevalue[styles[1]] * (1.0f / 32768.0f);
598                                 VectorMA(c, scale, lm, c);
599                                 if (styles[2] != 255)
600                                 {
601                                         lm += size3;
602                                         scale = d_lightstylevalue[styles[2]] * (1.0f / 32768.0f);
603                                         VectorMA(c, scale, lm, c);
604                                         if (styles[3] != 255)
605                                         {
606                                                 lm += size3;
607                                                 scale = d_lightstylevalue[styles[3]] * (1.0f / 32768.0f);
608                                                 VectorMA(c, scale, lm, c);
609                                         }
610                                 }
611                         }
612                 }
613         }
614 }
615
616 static void RSurf_FogColors_Vertex3f_Color4f(const float *v, float *c, float colorscale, int numverts, const float *modelorg)
617 {
618         int i;
619         float diff[3], f;
620         if (fogenabled)
621         {
622                 for (i = 0;i < numverts;i++, v += 3, c += 4)
623                 {
624                         VectorSubtract(v, modelorg, diff);
625                         f = colorscale * (1 - exp(fogdensity/DotProduct(diff, diff)));
626                         VectorScale(c, f, c);
627                 }
628         }
629         else if (colorscale != 1)
630                 for (i = 0;i < numverts;i++, c += 4)
631                         VectorScale(c, colorscale, c);
632 }
633
634 static void RSurf_FoggedColors_Vertex3f_Color4f(const float *v, float *c, float r, float g, float b, float a, float colorscale, int numverts, const float *modelorg)
635 {
636         int i;
637         float diff[3], f;
638         r *= colorscale;
639         g *= colorscale;
640         b *= colorscale;
641         if (fogenabled)
642         {
643                 for (i = 0;i < numverts;i++, v += 3, c += 4)
644                 {
645                         VectorSubtract(v, modelorg, diff);
646                         f = 1 - exp(fogdensity/DotProduct(diff, diff));
647                         c[0] = r * f;
648                         c[1] = g * f;
649                         c[2] = b * f;
650                         c[3] = a;
651                 }
652         }
653         else
654         {
655                 for (i = 0;i < numverts;i++, c += 4)
656                 {
657                         c[0] = r;
658                         c[1] = g;
659                         c[2] = b;
660                         c[3] = a;
661                 }
662         }
663 }
664
665 static void RSurf_FogPassColors_Vertex3f_Color4f(const float *v, float *c, float r, float g, float b, float a, float colorscale, int numverts, const float *modelorg)
666 {
667         int i;
668         float diff[3], f;
669         r *= colorscale;
670         g *= colorscale;
671         b *= colorscale;
672         for (i = 0;i < numverts;i++, v += 3, c += 4)
673         {
674                 VectorSubtract(v, modelorg, diff);
675                 f = exp(fogdensity/DotProduct(diff, diff));
676                 c[0] = r;
677                 c[1] = g;
678                 c[2] = b;
679                 c[3] = a * f;
680         }
681 }
682
683 static int RSurf_LightSeparate_Vertex3f_Color4f(const matrix4x4_t *matrix, const int *dlightbits, int numverts, const float *vert, float *color, float scale)
684 {
685         float f;
686         const float *v;
687         float *c;
688         int i, l, lit = false;
689         const dlight_t *light;
690         vec3_t lightorigin;
691         for (l = 0;l < r_numdlights;l++)
692         {
693                 if (dlightbits[l >> 5] & (1 << (l & 31)))
694                 {
695                         light = &r_dlight[l];
696                         Matrix4x4_Transform(matrix, light->origin, lightorigin);
697                         for (i = 0, v = vert, c = color;i < numverts;i++, v += 3, c += 4)
698                         {
699                                 f = VectorDistance2(v, lightorigin) + LIGHTOFFSET;
700                                 if (f < light->rtlight.lightmap_cullradius2)
701                                 {
702                                         f = ((1.0f / f) - light->rtlight.lightmap_subtract) * scale;
703                                         VectorMA(c, f, light->rtlight.lightmap_light, c);
704                                         lit = true;
705                                 }
706                         }
707                 }
708         }
709         return lit;
710 }
711
712 static void RSurfShader_Sky(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
713 {
714         const msurface_t *surf;
715         rmeshstate_t m;
716
717         // LordHavoc: HalfLife maps have freaky skypolys...
718         if (ent->model->brush.ishlbsp)
719                 return;
720
721         if (skyrendernow)
722         {
723                 skyrendernow = false;
724                 if (skyrendermasked)
725                         R_Sky();
726         }
727
728         R_Mesh_Matrix(&ent->matrix);
729
730         GL_Color(fogcolor[0], fogcolor[1], fogcolor[2], 1);
731         if (skyrendermasked)
732         {
733                 // depth-only (masking)
734                 qglColorMask(0,0,0,0);
735                 // just to make sure that braindead drivers don't draw anything
736                 // despite that colormask...
737                 GL_BlendFunc(GL_ZERO, GL_ONE);
738         }
739         else
740         {
741                 // fog sky
742                 GL_BlendFunc(GL_ONE, GL_ZERO);
743         }
744         GL_DepthMask(true);
745         GL_DepthTest(true);
746
747         memset(&m, 0, sizeof(m));
748         R_Mesh_State_Texture(&m);
749
750         while((surf = *surfchain++) != NULL)
751         {
752                 if (surf->visframe == r_framecount)
753                 {
754                         GL_VertexPointer(surf->mesh.data_vertex3f);
755                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
756                 }
757         }
758         qglColorMask(1,1,1,1);
759 }
760
761 static void RSurfShader_Water_Callback(const void *calldata1, int calldata2)
762 {
763         const entity_render_t *ent = calldata1;
764         const msurface_t *surf = ent->model->brushq1.surfaces + calldata2;
765         rmeshstate_t m;
766         float alpha;
767         float modelorg[3];
768         texture_t *texture;
769         matrix4x4_t tempmatrix;
770         float   args[4] = {0.05f,0,0,0.04f};
771
772         if (gl_textureshader && r_watershader.value && !fogenabled)
773         {
774                 Matrix4x4_CreateTranslate(&tempmatrix, sin(cl.time) * 0.025 * r_waterscroll.value, sin(cl.time * 0.8f) * 0.025 * r_waterscroll.value, 0);
775                 R_Mesh_TextureMatrix(1, &tempmatrix);
776                 Matrix4x4_CreateFromQuakeEntity(&tempmatrix, 0, 0, 0, 0, 0, 0, r_watershader.value);
777                 R_Mesh_TextureMatrix(0, &tempmatrix);
778         }
779         else if (r_waterscroll.value)
780         {
781                 // scrolling in texture matrix
782                 Matrix4x4_CreateTranslate(&tempmatrix, sin(cl.time) * 0.025 * r_waterscroll.value, sin(cl.time * 0.8f) * 0.025 * r_waterscroll.value, 0);
783                 R_Mesh_TextureMatrix(0, &tempmatrix);
784         }
785
786         R_Mesh_Matrix(&ent->matrix);
787         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
788
789         memset(&m, 0, sizeof(m));
790         texture = surf->texinfo->texture->currentframe;
791         alpha = texture->currentalpha;
792         if (texture->rendertype == SURFRENDER_ADD)
793         {
794                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
795                 GL_DepthMask(false);
796         }
797         else if (texture->rendertype == SURFRENDER_ALPHA)
798         {
799                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
800                 GL_DepthMask(false);
801         }
802         else
803         {
804                 GL_BlendFunc(GL_ONE, GL_ZERO);
805                 GL_DepthMask(true);
806         }
807         if (gl_textureshader && r_watershader.value && !fogenabled)
808         {
809                 m.tex[0] = R_GetTexture(mod_shared_distorttexture[(int)(cl.time * 16)&63]);
810                 m.tex[1] = R_GetTexture(texture->skin.base);
811         }
812         else
813                 m.tex[0] = R_GetTexture(texture->skin.base);
814         GL_DepthTest(true);
815         if (fogenabled)
816                 GL_ColorPointer(varray_color4f);
817         else
818                 GL_Color(1, 1, 1, alpha);
819         if (gl_textureshader && r_watershader.value && !fogenabled)
820         {
821                 GL_ActiveTexture (0);
822                 qglTexEnvi (GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
823                 GL_ActiveTexture (1);
824                 qglTexEnvi (GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
825                 qglTexEnvi (GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_OFFSET_TEXTURE_2D_NV);
826                 qglTexEnvi (GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);
827                 qglTexEnvfv (GL_TEXTURE_SHADER_NV, GL_OFFSET_TEXTURE_MATRIX_NV, &args[0]);
828                 qglEnable (GL_TEXTURE_SHADER_NV);
829         }
830
831         GL_VertexPointer(surf->mesh.data_vertex3f);
832         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
833         m.pointer_texcoord[1] = surf->mesh.data_texcoordtexture2f;
834         m.texcombinergb[1] = GL_REPLACE;
835         R_Mesh_State_Texture(&m);
836         if (fogenabled)
837         {
838                 R_FillColors(varray_color4f, surf->mesh.num_vertices, 1, 1, 1, alpha);
839                 RSurf_FogColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, 1, surf->mesh.num_vertices, modelorg);
840         }
841         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
842
843         if (gl_textureshader && r_watershader.value && !fogenabled)
844         {
845                 qglDisable (GL_TEXTURE_SHADER_NV);
846                 GL_ActiveTexture (0);
847         }
848
849         if (fogenabled)
850         {
851                 memset(&m, 0, sizeof(m));
852                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
853                 GL_DepthMask(false);
854                 GL_DepthTest(true);
855                 m.tex[0] = R_GetTexture(texture->skin.fog);
856                 GL_VertexPointer(surf->mesh.data_vertex3f);
857                 m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
858                 GL_ColorPointer(varray_color4f);
859                 R_Mesh_State_Texture(&m);
860                 RSurf_FogPassColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, fogcolor[0], fogcolor[1], fogcolor[2], alpha, 1, surf->mesh.num_vertices, modelorg);
861                 R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
862         }
863
864         if ((gl_textureshader && r_watershader.value && !fogenabled) || r_waterscroll.value)
865         {
866                 Matrix4x4_CreateIdentity(&tempmatrix);
867                 R_Mesh_TextureMatrix(0, &tempmatrix);
868                 R_Mesh_TextureMatrix(1, &tempmatrix);
869         }
870 }
871
872 static void RSurfShader_Water(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
873 {
874         const msurface_t *surf;
875         msurface_t **chain;
876         vec3_t center;
877         if (texture->rendertype != SURFRENDER_OPAQUE)
878         {
879                 for (chain = surfchain;(surf = *chain) != NULL;chain++)
880                 {
881                         if (surf->visframe == r_framecount)
882                         {
883                                 Matrix4x4_Transform(&ent->matrix, surf->poly_center, center);
884                                 R_MeshQueue_AddTransparent(center, RSurfShader_Water_Callback, ent, surf - ent->model->brushq1.surfaces);
885                         }
886                 }
887         }
888         else
889                 for (chain = surfchain;(surf = *chain) != NULL;chain++)
890                         if (surf->visframe == r_framecount)
891                                 RSurfShader_Water_Callback(ent, surf - ent->model->brushq1.surfaces);
892 }
893
894 static void RSurfShader_Wall_Pass_BaseVertex(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
895 {
896         float base, colorscale;
897         rmeshstate_t m;
898         float modelorg[3];
899         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
900         memset(&m, 0, sizeof(m));
901         if (rendertype == SURFRENDER_ADD)
902         {
903                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
904                 GL_DepthMask(false);
905         }
906         else if (rendertype == SURFRENDER_ALPHA)
907         {
908                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
909                 GL_DepthMask(false);
910         }
911         else
912         {
913                 GL_BlendFunc(GL_ONE, GL_ZERO);
914                 GL_DepthMask(true);
915         }
916         m.tex[0] = R_GetTexture(texture->skin.base);
917         colorscale = 1;
918         if (gl_combine.integer)
919         {
920                 m.texrgbscale[0] = 4;
921                 colorscale *= 0.25f;
922         }
923         base = ent->effects & EF_FULLBRIGHT ? 2.0f : r_ambient.value * (1.0f / 64.0f);
924         GL_DepthTest(true);
925         GL_ColorPointer(varray_color4f);
926
927         GL_VertexPointer(surf->mesh.data_vertex3f);
928         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
929         R_Mesh_State_Texture(&m);
930         R_FillColors(varray_color4f, surf->mesh.num_vertices, base, base, base, currentalpha);
931         if (!(ent->effects & EF_FULLBRIGHT))
932         {
933                 if (surf->dlightframe == r_framecount)
934                         RSurf_LightSeparate_Vertex3f_Color4f(&ent->inversematrix, surf->dlightbits, surf->mesh.num_vertices, surf->mesh.data_vertex3f, varray_color4f, 1);
935                 if (surf->flags & SURF_LIGHTMAP)
936                         RSurf_AddLightmapToVertexColors_Color4f(surf->mesh.data_lightmapoffsets, varray_color4f,surf->mesh.num_vertices, surf->samples, ((surf->extents[0]>>4)+1)*((surf->extents[1]>>4)+1)*3, surf->styles);
937         }
938         RSurf_FogColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, colorscale, surf->mesh.num_vertices, modelorg);
939         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
940 }
941
942 static void RSurfShader_Wall_Pass_Glow(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
943 {
944         rmeshstate_t m;
945         float modelorg[3];
946         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
947         memset(&m, 0, sizeof(m));
948         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
949         GL_DepthMask(false);
950         GL_DepthTest(true);
951         m.tex[0] = R_GetTexture(texture->skin.glow);
952         GL_ColorPointer(varray_color4f);
953
954         GL_VertexPointer(surf->mesh.data_vertex3f);
955         if (m.tex[0])
956                 m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
957         R_Mesh_State_Texture(&m);
958         RSurf_FoggedColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, 1, 1, 1, currentalpha, 1, surf->mesh.num_vertices, modelorg);
959         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
960 }
961
962 static void RSurfShader_Wall_Pass_Fog(const entity_render_t *ent, const msurface_t *surf, const texture_t *texture, int rendertype, float currentalpha)
963 {
964         rmeshstate_t m;
965         float modelorg[3];
966         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
967         memset(&m, 0, sizeof(m));
968         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
969         GL_DepthMask(false);
970         GL_DepthTest(true);
971         m.tex[0] = R_GetTexture(texture->skin.fog);
972         GL_ColorPointer(varray_color4f);
973
974         GL_VertexPointer(surf->mesh.data_vertex3f);
975         if (m.tex[0])
976                 m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
977         R_Mesh_State_Texture(&m);
978         RSurf_FogPassColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, fogcolor[0], fogcolor[1], fogcolor[2], currentalpha, 1, surf->mesh.num_vertices, modelorg);
979         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
980 }
981
982 static void RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmapDetailGlow(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
983 {
984         const msurface_t *surf;
985         rmeshstate_t m;
986         int lightmaptexturenum;
987         memset(&m, 0, sizeof(m));
988         GL_BlendFunc(GL_ONE, GL_ZERO);
989         GL_DepthMask(true);
990         GL_DepthTest(true);
991         m.tex[0] = R_GetTexture(texture->skin.base);
992         m.tex[1] = R_GetTexture((**surfchain).lightmaptexture);
993         m.texrgbscale[1] = 2;
994         m.tex[2] = R_GetTexture(texture->skin.detail);
995         m.texrgbscale[2] = 2;
996         if (texture->skin.glow)
997         {
998                 m.tex[3] = R_GetTexture(texture->skin.glow);
999                 m.texcombinergb[3] = GL_ADD;
1000         }
1001         if (r_shadow_realtime_world.integer)
1002                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
1003         else
1004                 GL_Color(1, 1, 1, 1);
1005
1006         while((surf = *surfchain++) != NULL)
1007         {
1008                 if (surf->visframe == r_framecount)
1009                 {
1010                         lightmaptexturenum = R_GetTexture(surf->lightmaptexture);
1011                         //if (m.tex[1] != lightmaptexturenum)
1012                         //{
1013                                 m.tex[1] = lightmaptexturenum;
1014                         //      R_Mesh_State_Texture(&m);
1015                         //}
1016                         GL_VertexPointer(surf->mesh.data_vertex3f);
1017                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1018                         m.pointer_texcoord[1] = surf->mesh.data_texcoordlightmap2f;
1019                         m.pointer_texcoord[2] = surf->mesh.data_texcoorddetail2f;
1020                         m.pointer_texcoord[3] = surf->mesh.data_texcoordtexture2f;
1021                         R_Mesh_State_Texture(&m);
1022                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1023                 }
1024         }
1025 }
1026
1027 static void RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmapDetail(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1028 {
1029         const msurface_t *surf;
1030         rmeshstate_t m;
1031         int lightmaptexturenum;
1032         memset(&m, 0, sizeof(m));
1033         GL_BlendFunc(GL_ONE, GL_ZERO);
1034         GL_DepthMask(true);
1035         GL_DepthTest(true);
1036         m.tex[0] = R_GetTexture(texture->skin.base);
1037         m.tex[1] = R_GetTexture((**surfchain).lightmaptexture);
1038         m.texrgbscale[1] = 2;
1039         m.tex[2] = R_GetTexture(texture->skin.detail);
1040         m.texrgbscale[2] = 2;
1041         if (r_shadow_realtime_world.integer)
1042                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
1043         else
1044                 GL_Color(1, 1, 1, 1);
1045
1046         while((surf = *surfchain++) != NULL)
1047         {
1048                 if (surf->visframe == r_framecount)
1049                 {
1050                         lightmaptexturenum = R_GetTexture(surf->lightmaptexture);
1051                         //if (m.tex[1] != lightmaptexturenum)
1052                         //{
1053                                 m.tex[1] = lightmaptexturenum;
1054                         //      R_Mesh_State_Texture(&m);
1055                         //}
1056                         GL_VertexPointer(surf->mesh.data_vertex3f);
1057                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1058                         m.pointer_texcoord[1] = surf->mesh.data_texcoordlightmap2f;
1059                         m.pointer_texcoord[2] = surf->mesh.data_texcoorddetail2f;
1060                         R_Mesh_State_Texture(&m);
1061                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1062                 }
1063         }
1064 }
1065
1066 static void RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmap(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1067 {
1068         const msurface_t *surf;
1069         rmeshstate_t m;
1070         int lightmaptexturenum;
1071         memset(&m, 0, sizeof(m));
1072         GL_BlendFunc(GL_ONE, GL_ZERO);
1073         GL_DepthMask(true);
1074         GL_DepthTest(true);
1075         m.tex[0] = R_GetTexture(texture->skin.base);
1076         m.tex[1] = R_GetTexture((**surfchain).lightmaptexture);
1077         m.texrgbscale[1] = 2;
1078         if (r_shadow_realtime_world.integer)
1079                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
1080         else
1081                 GL_Color(1, 1, 1, 1);
1082         while((surf = *surfchain++) != NULL)
1083         {
1084                 if (surf->visframe == r_framecount)
1085                 {
1086                         lightmaptexturenum = R_GetTexture(surf->lightmaptexture);
1087                         //if (m.tex[1] != lightmaptexturenum)
1088                         //{
1089                                 m.tex[1] = lightmaptexturenum;
1090                         //      R_Mesh_State_Texture(&m);
1091                         //}
1092                         GL_VertexPointer(surf->mesh.data_vertex3f);
1093                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1094                         m.pointer_texcoord[1] = surf->mesh.data_texcoordlightmap2f;
1095                         R_Mesh_State_Texture(&m);
1096                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1097                 }
1098         }
1099 }
1100
1101 static void RSurfShader_OpaqueWall_Pass_BaseTexture(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1102 {
1103         const msurface_t *surf;
1104         rmeshstate_t m;
1105         memset(&m, 0, sizeof(m));
1106         GL_DepthMask(true);
1107         GL_DepthTest(true);
1108         GL_BlendFunc(GL_ONE, GL_ZERO);
1109         m.tex[0] = R_GetTexture(texture->skin.base);
1110         if (r_shadow_realtime_world.integer)
1111                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
1112         else
1113                 GL_Color(1, 1, 1, 1);
1114         while((surf = *surfchain++) != NULL)
1115         {
1116                 if (surf->visframe == r_framecount)
1117                 {
1118                         GL_VertexPointer(surf->mesh.data_vertex3f);
1119                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1120                         R_Mesh_State_Texture(&m);
1121                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1122                 }
1123         }
1124 }
1125
1126 static void RSurfShader_OpaqueWall_Pass_BaseLightmap(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1127 {
1128         const msurface_t *surf;
1129         rmeshstate_t m;
1130         int lightmaptexturenum;
1131         memset(&m, 0, sizeof(m));
1132         GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1133         GL_DepthMask(false);
1134         GL_DepthTest(true);
1135         m.tex[0] = R_GetTexture((**surfchain).lightmaptexture);
1136         GL_Color(1, 1, 1, 1);
1137         while((surf = *surfchain++) != NULL)
1138         {
1139                 if (surf->visframe == r_framecount)
1140                 {
1141                         lightmaptexturenum = R_GetTexture(surf->lightmaptexture);
1142                         //if (m.tex[0] != lightmaptexturenum)
1143                         //{
1144                                 m.tex[0] = lightmaptexturenum;
1145                         //      R_Mesh_State_Texture(&m);
1146                         //}
1147                         GL_VertexPointer(surf->mesh.data_vertex3f);
1148                         m.pointer_texcoord[0] = surf->mesh.data_texcoordlightmap2f;
1149                         R_Mesh_State_Texture(&m);
1150                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1151                 }
1152         }
1153 }
1154
1155 static void RSurfShader_OpaqueWall_Pass_Fog(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1156 {
1157         const msurface_t *surf;
1158         rmeshstate_t m;
1159         float modelorg[3];
1160         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1161         memset(&m, 0, sizeof(m));
1162         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1163         GL_DepthMask(false);
1164         GL_DepthTest(true);
1165         GL_ColorPointer(varray_color4f);
1166         while((surf = *surfchain++) != NULL)
1167         {
1168                 if (surf->visframe == r_framecount)
1169                 {
1170                         GL_VertexPointer(surf->mesh.data_vertex3f);
1171                         if (m.tex[0])
1172                                 m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1173                         R_Mesh_State_Texture(&m);
1174                         RSurf_FogPassColors_Vertex3f_Color4f(surf->mesh.data_vertex3f, varray_color4f, fogcolor[0], fogcolor[1], fogcolor[2], 1, 1, surf->mesh.num_vertices, modelorg);
1175                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1176                 }
1177         }
1178 }
1179
1180 static void RSurfShader_OpaqueWall_Pass_BaseDetail(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1181 {
1182         const msurface_t *surf;
1183         rmeshstate_t m;
1184         memset(&m, 0, sizeof(m));
1185         GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1186         GL_DepthMask(false);
1187         GL_DepthTest(true);
1188         m.tex[0] = R_GetTexture(texture->skin.detail);
1189         GL_Color(1, 1, 1, 1);
1190         while((surf = *surfchain++) != NULL)
1191         {
1192                 if (surf->visframe == r_framecount)
1193                 {
1194                         GL_VertexPointer(surf->mesh.data_vertex3f);
1195                         m.pointer_texcoord[0] = surf->mesh.data_texcoorddetail2f;
1196                         R_Mesh_State_Texture(&m);
1197                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1198                 }
1199         }
1200 }
1201
1202 static void RSurfShader_OpaqueWall_Pass_Glow(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1203 {
1204         const msurface_t *surf;
1205         rmeshstate_t m;
1206         memset(&m, 0, sizeof(m));
1207         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1208         GL_DepthMask(false);
1209         GL_DepthTest(true);
1210         m.tex[0] = R_GetTexture(texture->skin.glow);
1211         GL_Color(1, 1, 1, 1);
1212         while((surf = *surfchain++) != NULL)
1213         {
1214                 if (surf->visframe == r_framecount)
1215                 {
1216                         GL_VertexPointer(surf->mesh.data_vertex3f);
1217                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1218                         R_Mesh_State_Texture(&m);
1219                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1220                 }
1221         }
1222 }
1223
1224 /*
1225 static void RSurfShader_OpaqueWall_Pass_OpaqueGlow(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1226 {
1227         const msurface_t *surf;
1228         rmeshstate_t m;
1229         memset(&m, 0, sizeof(m));
1230         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1231         GL_DepthMask(true);
1232         m.tex[0] = R_GetTexture(texture->skin.glow);
1233         if (m.tex[0])
1234                 GL_Color(1, 1, 1, 1);
1235         else
1236                 GL_Color(0, 0, 0, 1);
1237         while((surf = *surfchain++) != NULL)
1238         {
1239                 if (surf->visframe == r_framecount)
1240                 {
1241                         GL_VertexPointer(surf->mesh.data_vertex3f);
1242                         m.pointer_texcoord[0] = surf->mesh.data_texcoordtexture2f;
1243                         R_Mesh_State_Texture(&m);
1244                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1245                 }
1246         }
1247 }
1248 */
1249
1250 static void RSurfShader_OpaqueWall_Pass_BaseLightmapOnly(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1251 {
1252         const msurface_t *surf;
1253         rmeshstate_t m;
1254         int lightmaptexturenum;
1255         memset(&m, 0, sizeof(m));
1256         GL_BlendFunc(GL_ONE, GL_ZERO);
1257         GL_DepthMask(true);
1258         GL_DepthTest(true);
1259         m.tex[0] = R_GetTexture((**surfchain).lightmaptexture);
1260         if (r_shadow_realtime_world.integer)
1261                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
1262         else
1263                 GL_Color(1, 1, 1, 1);
1264         while((surf = *surfchain++) != NULL)
1265         {
1266                 if (surf->visframe == r_framecount)
1267                 {
1268                         lightmaptexturenum = R_GetTexture(surf->lightmaptexture);
1269                         //if (m.tex[0] != lightmaptexturenum)
1270                         //{
1271                                 m.tex[0] = lightmaptexturenum;
1272                         //      R_Mesh_State_Texture(&m);
1273                         //}
1274                         GL_VertexPointer(surf->mesh.data_vertex3f);
1275                         m.pointer_texcoord[0] = surf->mesh.data_texcoordlightmap2f;
1276                         R_Mesh_State_Texture(&m);
1277                         R_Mesh_Draw(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i);
1278                 }
1279         }
1280 }
1281
1282 static void RSurfShader_Wall_Vertex_Callback(const void *calldata1, int calldata2)
1283 {
1284         const entity_render_t *ent = calldata1;
1285         const msurface_t *surf = ent->model->brushq1.surfaces + calldata2;
1286         int rendertype;
1287         float currentalpha;
1288         texture_t *texture;
1289         R_Mesh_Matrix(&ent->matrix);
1290
1291         texture = surf->texinfo->texture;
1292         if (texture->animated)
1293                 texture = texture->anim_frames[ent->frame != 0][(texture->anim_total[ent->frame != 0] >= 2) ? ((int) (cl.time * 5.0f) % texture->anim_total[ent->frame != 0]) : 0];
1294
1295         currentalpha = ent->alpha;
1296         if (ent->effects & EF_ADDITIVE)
1297                 rendertype = SURFRENDER_ADD;
1298         else if (currentalpha < 1 || texture->skin.fog != NULL)
1299                 rendertype = SURFRENDER_ALPHA;
1300         else
1301                 rendertype = SURFRENDER_OPAQUE;
1302
1303         RSurfShader_Wall_Pass_BaseVertex(ent, surf, texture, rendertype, currentalpha);
1304         if (texture->skin.glow)
1305                 RSurfShader_Wall_Pass_Glow(ent, surf, texture, rendertype, currentalpha);
1306         if (fogenabled)
1307                 RSurfShader_Wall_Pass_Fog(ent, surf, texture, rendertype, currentalpha);
1308 }
1309
1310 static void RSurfShader_Wall_Lightmap(const entity_render_t *ent, const texture_t *texture, msurface_t **surfchain)
1311 {
1312         const msurface_t *surf;
1313         msurface_t **chain;
1314         vec3_t center;
1315         if (texture->rendertype != SURFRENDER_OPAQUE)
1316         {
1317                 // transparent vertex shaded from lightmap
1318                 for (chain = surfchain;(surf = *chain) != NULL;chain++)
1319                 {
1320                         if (surf->visframe == r_framecount)
1321                         {
1322                                 Matrix4x4_Transform(&ent->matrix, surf->poly_center, center);
1323                                 R_MeshQueue_AddTransparent(center, RSurfShader_Wall_Vertex_Callback, ent, surf - ent->model->brushq1.surfaces);
1324                         }
1325                 }
1326         }
1327         else if (ent->effects & EF_FULLBRIGHT)
1328         {
1329                 RSurfShader_OpaqueWall_Pass_BaseTexture(ent, texture, surfchain);
1330                 if (r_detailtextures.integer)
1331                         RSurfShader_OpaqueWall_Pass_BaseDetail(ent, texture, surfchain);
1332                 if (texture->skin.glow)
1333                         RSurfShader_OpaqueWall_Pass_Glow(ent, texture, surfchain);
1334                 if (fogenabled)
1335                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1336         }
1337         /*
1338         // opaque base lighting
1339         else if (r_shadow_realtime_world.integer && r_shadow_realtime_world_lightmaps.value <= 0)
1340         {
1341                 if (r_ambient.value > 0)
1342                 {
1343                 }
1344                 else
1345                         RSurfShader_OpaqueWall_Pass_OpaqueGlow(ent, texture, surfchain);
1346                 if (fogenabled)
1347                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1348         }
1349         */
1350         // opaque lightmapped
1351         else if (r_textureunits.integer >= 4 && gl_combine.integer && r_detailtextures.integer && !gl_lightmaps.integer)
1352         {
1353                 RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmapDetailGlow(ent, texture, surfchain);
1354                 if (fogenabled)
1355                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1356         }
1357         else if (r_textureunits.integer >= 3 && gl_combine.integer && r_detailtextures.integer && !gl_lightmaps.integer)
1358         {
1359                 RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmapDetail(ent, texture, surfchain);
1360                 if (texture->skin.glow)
1361                         RSurfShader_OpaqueWall_Pass_Glow(ent, texture, surfchain);
1362                 if (fogenabled)
1363                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1364         }
1365         else if (r_textureunits.integer >= 2 && gl_combine.integer && !gl_lightmaps.integer)
1366         {
1367                 RSurfShader_OpaqueWall_Pass_BaseCombine_TextureLightmap(ent, texture, surfchain);
1368                 if (r_detailtextures.integer)
1369                         RSurfShader_OpaqueWall_Pass_BaseDetail(ent, texture, surfchain);
1370                 if (texture->skin.glow)
1371                         RSurfShader_OpaqueWall_Pass_Glow(ent, texture, surfchain);
1372                 if (fogenabled)
1373                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1374         }
1375         else if (!gl_lightmaps.integer)
1376         {
1377                 RSurfShader_OpaqueWall_Pass_BaseTexture(ent, texture, surfchain);
1378                 RSurfShader_OpaqueWall_Pass_BaseLightmap(ent, texture, surfchain);
1379                 if (r_detailtextures.integer)
1380                         RSurfShader_OpaqueWall_Pass_BaseDetail(ent, texture, surfchain);
1381                 if (texture->skin.glow)
1382                         RSurfShader_OpaqueWall_Pass_Glow(ent, texture, surfchain);
1383                 if (fogenabled)
1384                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1385         }
1386         else
1387         {
1388                 RSurfShader_OpaqueWall_Pass_BaseLightmapOnly(ent, texture, surfchain);
1389                 if (fogenabled)
1390                         RSurfShader_OpaqueWall_Pass_Fog(ent, texture, surfchain);
1391         }
1392 }
1393
1394 Cshader_t Cshader_wall_lightmap = {{NULL, RSurfShader_Wall_Lightmap}, SHADERFLAGS_NEEDLIGHTMAP};
1395 Cshader_t Cshader_water = {{NULL, RSurfShader_Water}, 0};
1396 Cshader_t Cshader_sky = {{RSurfShader_Sky, NULL}, 0};
1397
1398 int Cshader_count = 3;
1399 Cshader_t *Cshaders[3] =
1400 {
1401         &Cshader_wall_lightmap,
1402         &Cshader_water,
1403         &Cshader_sky
1404 };
1405
1406 void R_UpdateTextureInfo(entity_render_t *ent)
1407 {
1408         int i, texframe, alttextures;
1409         texture_t *t;
1410
1411         if (!ent->model)
1412                 return;
1413
1414         alttextures = ent->frame != 0;
1415         texframe = (int)(cl.time * 5.0f);
1416         for (i = 0;i < ent->model->brushq1.numtextures;i++)
1417         {
1418                 t = ent->model->brushq1.textures + i;
1419                 t->currentalpha = ent->alpha;
1420                 if (t->flags & SURF_WATERALPHA)
1421                         t->currentalpha *= r_wateralpha.value;
1422                 if (ent->effects & EF_ADDITIVE)
1423                         t->rendertype = SURFRENDER_ADD;
1424                 else if (t->currentalpha < 1 || t->skin.fog != NULL)
1425                         t->rendertype = SURFRENDER_ALPHA;
1426                 else
1427                         t->rendertype = SURFRENDER_OPAQUE;
1428                 // we don't need to set currentframe if t->animated is false because
1429                 // it was already set up by the texture loader for non-animating
1430                 if (t->animated)
1431                         t->currentframe = t->anim_frames[alttextures][(t->anim_total[alttextures] >= 2) ? (texframe % t->anim_total[alttextures]) : 0];
1432         }
1433 }
1434
1435 void R_PrepareSurfaces(entity_render_t *ent)
1436 {
1437         int i, numsurfaces, *surfacevisframes;
1438         model_t *model;
1439         msurface_t *surf, *surfaces, **surfchain;
1440         vec3_t modelorg;
1441
1442         if (!ent->model)
1443                 return;
1444
1445         model = ent->model;
1446         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1447         numsurfaces = model->brushq1.nummodelsurfaces;
1448         surfaces = model->brushq1.surfaces + model->brushq1.firstmodelsurface;
1449         surfacevisframes = model->brushq1.surfacevisframes + model->brushq1.firstmodelsurface;
1450
1451         R_UpdateTextureInfo(ent);
1452
1453         if (r_dynamic.integer && !r_shadow_realtime_dlight.integer)
1454                 R_MarkLights(ent);
1455
1456         if (model->brushq1.light_ambient != r_ambient.value)
1457         {
1458                 model->brushq1.light_ambient = r_ambient.value;
1459                 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
1460                         model->brushq1.surfaces[i + model->brushq1.firstmodelsurface].cached_dlight = true;
1461         }
1462         else
1463         {
1464                 for (i = 0;i < model->brushq1.light_styles;i++)
1465                 {
1466                         if (model->brushq1.light_stylevalue[i] != d_lightstylevalue[model->brushq1.light_style[i]])
1467                         {
1468                                 model->brushq1.light_stylevalue[i] = d_lightstylevalue[model->brushq1.light_style[i]];
1469                                 for (surfchain = model->brushq1.light_styleupdatechains[i];*surfchain;surfchain++)
1470                                         (**surfchain).cached_dlight = true;
1471                         }
1472                 }
1473         }
1474
1475         for (i = 0, surf = surfaces;i < numsurfaces;i++, surf++)
1476         {
1477                 if (surfacevisframes[i] == r_framecount)
1478                 {
1479 #if !WORLDNODECULLBACKFACES
1480                         // mark any backface surfaces as not visible
1481                         if (PlaneDist(modelorg, surf->plane) < surf->plane->dist)
1482                         {
1483                                 if (!(surf->flags & SURF_PLANEBACK))
1484                                         surfacevisframes[i] = -1;
1485                         }
1486                         else
1487                         {
1488                                 if ((surf->flags & SURF_PLANEBACK))
1489                                         surfacevisframes[i] = -1;
1490                         }
1491                         if (surfacevisframes[i] == r_framecount)
1492 #endif
1493                         {
1494                                 c_faces++;
1495                                 surf->visframe = r_framecount;
1496                                 if (surf->cached_dlight && surf->lightmaptexture != NULL)
1497                                         R_BuildLightMap(ent, surf);
1498                         }
1499                 }
1500         }
1501 }
1502
1503 void R_DrawSurfaces(entity_render_t *ent, int type, msurface_t ***chains)
1504 {
1505         int i;
1506         texture_t *t;
1507         if (ent->model == NULL)
1508                 return;
1509         R_Mesh_Matrix(&ent->matrix);
1510         for (i = 0, t = ent->model->brushq1.textures;i < ent->model->brushq1.numtextures;i++, t++)
1511                 if (t->shader->shaderfunc[type] && t->currentframe && chains[i] != NULL)
1512                         t->shader->shaderfunc[type](ent, t->currentframe, chains[i]);
1513 }
1514
1515 static void R_DrawPortal_Callback(const void *calldata1, int calldata2)
1516 {
1517         int i;
1518         float *v;
1519         rmeshstate_t m;
1520         const entity_render_t *ent = calldata1;
1521         const mportal_t *portal = ent->model->brushq1.portals + calldata2;
1522         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1523         GL_DepthMask(false);
1524         GL_DepthTest(true);
1525         R_Mesh_Matrix(&ent->matrix);
1526         GL_VertexPointer(varray_vertex3f);
1527
1528         memset(&m, 0, sizeof(m));
1529         R_Mesh_State_Texture(&m);
1530
1531         i = portal - ent->model->brushq1.portals;
1532         GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f),
1533                          ((i & 0x0038) >> 3) * (1.0f / 7.0f),
1534                          ((i & 0x01C0) >> 6) * (1.0f / 7.0f),
1535                          0.125f);
1536         if (PlaneDiff(r_vieworigin, (&portal->plane)) < 0)
1537         {
1538                 for (i = portal->numpoints - 1, v = varray_vertex3f;i >= 0;i--, v += 3)
1539                         VectorCopy(portal->points[i].position, v);
1540         }
1541         else
1542                 for (i = 0, v = varray_vertex3f;i < portal->numpoints;i++, v += 3)
1543                         VectorCopy(portal->points[i].position, v);
1544         R_Mesh_Draw(portal->numpoints, portal->numpoints - 2, polygonelements);
1545 }
1546
1547 // LordHavoc: this is just a nice debugging tool, very slow
1548 static void R_DrawPortals(entity_render_t *ent)
1549 {
1550         int i;
1551         mportal_t *portal, *endportal;
1552         float temp[3], center[3], f;
1553         if (ent->model == NULL)
1554                 return;
1555         for (portal = ent->model->brushq1.portals, endportal = portal + ent->model->brushq1.numportals;portal < endportal;portal++)
1556         {
1557                 if (portal->numpoints <= POLYGONELEMENTS_MAXPOINTS)
1558                 {
1559                         VectorClear(temp);
1560                         for (i = 0;i < portal->numpoints;i++)
1561                                 VectorAdd(temp, portal->points[i].position, temp);
1562                         f = ixtable[portal->numpoints];
1563                         VectorScale(temp, f, temp);
1564                         Matrix4x4_Transform(&ent->matrix, temp, center);
1565                         R_MeshQueue_AddTransparent(center, R_DrawPortal_Callback, ent, portal - ent->model->brushq1.portals);
1566                 }
1567         }
1568 }
1569
1570 void R_PrepareBrushModel(entity_render_t *ent)
1571 {
1572         int i, numsurfaces, *surfacevisframes, *surfacepvsframes;
1573         msurface_t *surf;
1574         model_t *model;
1575 #if WORLDNODECULLBACKFACES
1576         vec3_t modelorg;
1577 #endif
1578
1579         // because bmodels can be reused, we have to decide which things to render
1580         // from scratch every time
1581         model = ent->model;
1582         if (model == NULL)
1583                 return;
1584 #if WORLDNODECULLBACKFACES
1585         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1586 #endif
1587         numsurfaces = model->brushq1.nummodelsurfaces;
1588         surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface;
1589         surfacevisframes = model->brushq1.surfacevisframes + model->brushq1.firstmodelsurface;
1590         surfacepvsframes = model->brushq1.surfacepvsframes + model->brushq1.firstmodelsurface;
1591         for (i = 0;i < numsurfaces;i++, surf++)
1592         {
1593 #if WORLDNODECULLBACKFACES
1594                 // mark any backface surfaces as not visible
1595                 if (PlaneDist(modelorg, surf->plane) < surf->plane->dist)
1596                 {
1597                         if ((surf->flags & SURF_PLANEBACK))
1598                                 surfacevisframes[i] = r_framecount;
1599                 }
1600                 else if (!(surf->flags & SURF_PLANEBACK))
1601                         surfacevisframes[i] = r_framecount;
1602 #else
1603                 surfacevisframes[i] = r_framecount;
1604 #endif
1605                 surf->dlightframe = -1;
1606         }
1607         R_PrepareSurfaces(ent);
1608 }
1609
1610 void R_SurfaceWorldNode (entity_render_t *ent)
1611 {
1612         int i, *surfacevisframes, *surfacepvsframes, surfnum;
1613         msurface_t *surf;
1614         mleaf_t *leaf;
1615         model_t *model;
1616         vec3_t modelorg;
1617
1618         // equivilant to quake's RecursiveWorldNode but faster and more effective
1619         model = ent->model;
1620         if (model == NULL)
1621                 return;
1622         surfacevisframes = model->brushq1.surfacevisframes + model->brushq1.firstmodelsurface;
1623         surfacepvsframes = model->brushq1.surfacepvsframes + model->brushq1.firstmodelsurface;
1624         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1625
1626         for (leaf = model->brushq1.pvsleafchain;leaf;leaf = leaf->pvschain)
1627         {
1628                 if (!R_CullBox (leaf->mins, leaf->maxs))
1629                 {
1630                         c_leafs++;
1631                         leaf->visframe = r_framecount;
1632                 }
1633         }
1634
1635         for (i = 0;i < model->brushq1.pvssurflistlength;i++)
1636         {
1637                 surfnum = model->brushq1.pvssurflist[i];
1638                 surf = model->brushq1.surfaces + surfnum;
1639 #if WORLDNODECULLBACKFACES
1640                 if (PlaneDist(modelorg, surf->plane) < surf->plane->dist)
1641                 {
1642                         if ((surf->flags & SURF_PLANEBACK) && !R_CullBox (surf->poly_mins, surf->poly_maxs))
1643                                 surfacevisframes[surfnum] = r_framecount;
1644                 }
1645                 else
1646                 {
1647                         if (!(surf->flags & SURF_PLANEBACK) && !R_CullBox (surf->poly_mins, surf->poly_maxs))
1648                                 surfacevisframes[surfnum] = r_framecount;
1649                 }
1650 #else
1651                 if (!R_CullBox (surf->poly_mins, surf->poly_maxs))
1652                         surfacevisframes[surfnum] = r_framecount;
1653 #endif
1654         }
1655 }
1656
1657 static void R_PortalWorldNode(entity_render_t *ent, mleaf_t *viewleaf)
1658 {
1659         int c, leafstackpos, *mark, *surfacevisframes;
1660 #if WORLDNODECULLBACKFACES
1661         int n;
1662         msurface_t *surf;
1663 #endif
1664         mleaf_t *leaf, *leafstack[8192];
1665         mportal_t *p;
1666         vec3_t modelorg;
1667         msurface_t *surfaces;
1668         if (ent->model == NULL)
1669                 return;
1670         // LordHavoc: portal-passage worldnode with PVS;
1671         // follows portals leading outward from viewleaf, does not venture
1672         // offscreen or into leafs that are not visible, faster than Quake's
1673         // RecursiveWorldNode
1674         surfaces = ent->model->brushq1.surfaces;
1675         surfacevisframes = ent->model->brushq1.surfacevisframes;
1676         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1677         viewleaf->worldnodeframe = r_framecount;
1678         leafstack[0] = viewleaf;
1679         leafstackpos = 1;
1680         while (leafstackpos)
1681         {
1682                 c_leafs++;
1683                 leaf = leafstack[--leafstackpos];
1684                 leaf->visframe = r_framecount;
1685                 // draw any surfaces bounding this leaf
1686                 if (leaf->nummarksurfaces)
1687                 {
1688                         for (c = leaf->nummarksurfaces, mark = leaf->firstmarksurface;c;c--)
1689                         {
1690 #if WORLDNODECULLBACKFACES
1691                                 n = *mark++;
1692                                 if (surfacevisframes[n] != r_framecount)
1693                                 {
1694                                         surf = surfaces + n;
1695                                         if (PlaneDist(modelorg, surf->plane) < surf->plane->dist)
1696                                         {
1697                                                 if ((surf->flags & SURF_PLANEBACK))
1698                                                         surfacevisframes[n] = r_framecount;
1699                                         }
1700                                         else
1701                                         {
1702                                                 if (!(surf->flags & SURF_PLANEBACK))
1703                                                         surfacevisframes[n] = r_framecount;
1704                                         }
1705                                 }
1706 #else
1707                                 surfacevisframes[*mark++] = r_framecount;
1708 #endif
1709                         }
1710                 }
1711                 // follow portals into other leafs
1712                 for (p = leaf->portals;p;p = p->next)
1713                 {
1714                         // LordHavoc: this DotProduct hurts less than a cache miss
1715                         // (which is more likely to happen if backflowing through leafs)
1716                         if (DotProduct(modelorg, p->plane.normal) < (p->plane.dist + 1))
1717                         {
1718                                 leaf = p->past;
1719                                 if (leaf->worldnodeframe != r_framecount)
1720                                 {
1721                                         leaf->worldnodeframe = r_framecount;
1722                                         // FIXME: R_CullBox is absolute, should be done relative
1723                                         if (CHECKPVSBIT(r_pvsbits, leaf->clusterindex) && !R_CullBox(leaf->mins, leaf->maxs))
1724                                                 leafstack[leafstackpos++] = leaf;
1725                                 }
1726                         }
1727                 }
1728         }
1729 }
1730
1731 void R_PVSUpdate (entity_render_t *ent, mleaf_t *viewleaf)
1732 {
1733         int j, c, *surfacepvsframes, *mark;
1734         mleaf_t *leaf;
1735         model_t *model;
1736
1737         model = ent->model;
1738         if (model && (model->brushq1.pvsviewleaf != viewleaf || model->brushq1.pvsviewleafnovis != r_novis.integer))
1739         {
1740                 model->brushq1.pvsframecount++;
1741                 model->brushq1.pvsviewleaf = viewleaf;
1742                 model->brushq1.pvsviewleafnovis = r_novis.integer;
1743                 model->brushq1.pvsleafchain = NULL;
1744                 model->brushq1.pvssurflistlength = 0;
1745                 if (viewleaf)
1746                 {
1747                         surfacepvsframes = model->brushq1.surfacepvsframes;
1748                         for (j = 0, leaf = model->brushq1.data_leafs;j < model->brushq1.num_leafs;j++, leaf++)
1749                         {
1750                                 if (CHECKPVSBIT(r_pvsbits, leaf->clusterindex))
1751                                 {
1752                                         leaf->pvsframe = model->brushq1.pvsframecount;
1753                                         leaf->pvschain = model->brushq1.pvsleafchain;
1754                                         model->brushq1.pvsleafchain = leaf;
1755                                         // mark surfaces bounding this leaf as visible
1756                                         for (c = leaf->nummarksurfaces, mark = leaf->firstmarksurface;c;c--, mark++)
1757                                                 surfacepvsframes[*mark] = model->brushq1.pvsframecount;
1758                                 }
1759                         }
1760                         model->brushq1.BuildPVSTextureChains(model);
1761                 }
1762         }
1763 }
1764
1765 void R_WorldVisibility(entity_render_t *ent)
1766 {
1767         vec3_t modelorg;
1768         mleaf_t *viewleaf;
1769
1770         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
1771         viewleaf = (ent->model && ent->model->brushq1.PointInLeaf) ? ent->model->brushq1.PointInLeaf(ent->model, modelorg) : NULL;
1772         R_PVSUpdate(ent, viewleaf);
1773
1774         if (!viewleaf)
1775                 return;
1776
1777         if (r_surfaceworldnode.integer || viewleaf->contents == CONTENTS_SOLID)
1778                 R_SurfaceWorldNode (ent);
1779         else
1780                 R_PortalWorldNode (ent, viewleaf);
1781 }
1782
1783 void R_DrawWorld(entity_render_t *ent)
1784 {
1785         if (ent->model == NULL)
1786                 return;
1787         if (!ent->model->brushq1.num_leafs)
1788         {
1789                 if (ent->model->DrawSky)
1790                         ent->model->DrawSky(ent);
1791                 if (ent->model->Draw)
1792                         ent->model->Draw(ent);
1793         }
1794         else
1795         {
1796                 R_PrepareSurfaces(ent);
1797                 R_DrawSurfaces(ent, SHADERSTAGE_SKY, ent->model->brushq1.pvstexturechains);
1798                 R_DrawSurfaces(ent, SHADERSTAGE_NORMAL, ent->model->brushq1.pvstexturechains);
1799                 if (r_drawportals.integer)
1800                         R_DrawPortals(ent);
1801         }
1802 }
1803
1804 void R_Model_Brush_DrawSky(entity_render_t *ent)
1805 {
1806         if (ent->model == NULL)
1807                 return;
1808         if (ent != &cl_entities[0].render)
1809                 R_PrepareBrushModel(ent);
1810         R_DrawSurfaces(ent, SHADERSTAGE_SKY, ent->model->brushq1.pvstexturechains);
1811 }
1812
1813 void R_Model_Brush_Draw(entity_render_t *ent)
1814 {
1815         if (ent->model == NULL)
1816                 return;
1817         c_bmodels++;
1818         if (ent != &cl_entities[0].render)
1819                 R_PrepareBrushModel(ent);
1820         R_DrawSurfaces(ent, SHADERSTAGE_NORMAL, ent->model->brushq1.pvstexturechains);
1821 }
1822
1823 void R_Model_Brush_DrawShadowVolume (entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
1824 {
1825 #if 0
1826         int i;
1827         msurface_t *surf;
1828         float projectdistance, f, temp[3], lightradius2;
1829         if (ent->model == NULL)
1830                 return;
1831         R_Mesh_Matrix(&ent->matrix);
1832         lightradius2 = lightradius * lightradius;
1833         R_UpdateTextureInfo(ent);
1834         projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
1835         //projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
1836         for (i = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;i < ent->model->brushq1.nummodelsurfaces;i++, surf++)
1837         {
1838                 if (surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && surf->flags & SURF_SHADOWCAST)
1839                 {
1840                         f = PlaneDiff(relativelightorigin, surf->plane);
1841                         if (surf->flags & SURF_PLANEBACK)
1842                                 f = -f;
1843                         // draw shadows only for frontfaces and only if they are close
1844                         if (f >= 0.1 && f < lightradius)
1845                         {
1846                                 temp[0] = bound(surf->poly_mins[0], relativelightorigin[0], surf->poly_maxs[0]) - relativelightorigin[0];
1847                                 temp[1] = bound(surf->poly_mins[1], relativelightorigin[1], surf->poly_maxs[1]) - relativelightorigin[1];
1848                                 temp[2] = bound(surf->poly_mins[2], relativelightorigin[2], surf->poly_maxs[2]) - relativelightorigin[2];
1849                                 if (DotProduct(temp, temp) < lightradius2)
1850                                         R_Shadow_VolumeFromSphere(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_vertex3f, surf->mesh.data_element3i, surf->mesh.data_neighbor3i, relativelightorigin, projectdistance, lightradius);
1851                         }
1852                 }
1853         }
1854 #else
1855         int i, j, t;
1856         const int *e;
1857         msurface_t *surf;
1858         float projectdistance;
1859         const float *v[3];
1860         vec3_t lightmins, lightmaxs;
1861         if (ent->model == NULL)
1862                 return;
1863         R_Mesh_Matrix(&ent->matrix);
1864         R_UpdateTextureInfo(ent);
1865         projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
1866         //projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
1867         lightmins[0] = relativelightorigin[0] - lightradius;
1868         lightmins[1] = relativelightorigin[1] - lightradius;
1869         lightmins[2] = relativelightorigin[2] - lightradius;
1870         lightmaxs[0] = relativelightorigin[0] + lightradius;
1871         lightmaxs[1] = relativelightorigin[1] + lightradius;
1872         lightmaxs[2] = relativelightorigin[2] + lightradius;
1873         R_Shadow_PrepareShadowMark(ent->model->brush.shadowmesh->numtriangles);
1874         for (i = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;i < ent->model->brushq1.nummodelsurfaces;i++, surf++)
1875         {
1876                 if (BoxesOverlap(lightmins, lightmaxs, surf->poly_mins, surf->poly_maxs) && surf->texinfo->texture->rendertype == SURFRENDER_OPAQUE && (surf->flags & SURF_SHADOWCAST))
1877                 {
1878                         for (j = 0, t = surf->num_firstshadowmeshtriangle, e = ent->model->brush.shadowmesh->element3i + t * 3;j < surf->mesh.num_triangles;j++, t++, e += 3)
1879                         {
1880                                 v[0] = ent->model->brush.shadowmesh->vertex3f + e[0] * 3;
1881                                 v[1] = ent->model->brush.shadowmesh->vertex3f + e[1] * 3;
1882                                 v[2] = ent->model->brush.shadowmesh->vertex3f + e[2] * 3;
1883                                 if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
1884                                         shadowmarklist[numshadowmark++] = t;
1885                         }
1886                 }
1887         }
1888         R_Shadow_VolumeFromList(ent->model->brush.shadowmesh->numverts, ent->model->brush.shadowmesh->numtriangles, ent->model->brush.shadowmesh->vertex3f, ent->model->brush.shadowmesh->element3i, ent->model->brush.shadowmesh->neighbor3i, relativelightorigin, projectdistance, numshadowmark, shadowmarklist);
1889 #endif
1890 }
1891
1892 void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
1893 {
1894         int surfnum;
1895         msurface_t *surf;
1896         texture_t *t;
1897         float f, lightmins[3], lightmaxs[3];
1898         if (ent->model == NULL)
1899                 return;
1900         R_Mesh_Matrix(&ent->matrix);
1901         lightmins[0] = relativelightorigin[0] - lightradius;
1902         lightmins[1] = relativelightorigin[1] - lightradius;
1903         lightmins[2] = relativelightorigin[2] - lightradius;
1904         lightmaxs[0] = relativelightorigin[0] + lightradius;
1905         lightmaxs[1] = relativelightorigin[1] + lightradius;
1906         lightmaxs[2] = relativelightorigin[2] + lightradius;
1907         R_UpdateTextureInfo(ent);
1908         for (surfnum = 0, surf = ent->model->brushq1.surfaces + ent->model->brushq1.firstmodelsurface;surfnum < ent->model->brushq1.nummodelsurfaces;surfnum++, surf++)
1909         {
1910                 if ((ent != &cl_entities[0].render || surf->visframe == r_framecount) && BoxesOverlap(surf->poly_mins, surf->poly_maxs, lightmins, lightmaxs))
1911                 {
1912                         f = PlaneDiff(relativelightorigin, surf->plane);
1913                         if (surf->flags & SURF_PLANEBACK)
1914                                 f = -f;
1915                         if (f >= -0.1 && f < lightradius)
1916                         {
1917                                 t = surf->texinfo->texture->currentframe;
1918                                 if (t->rendertype == SURFRENDER_OPAQUE && t->flags & SURF_SHADOWLIGHT)
1919                                 {
1920                                         R_Shadow_DiffuseLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, t->skin.nmap, lightcubemap);
1921                                         R_Shadow_SpecularLighting(surf->mesh.num_vertices, surf->mesh.num_triangles, surf->mesh.data_element3i, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.gloss, t->skin.nmap, lightcubemap);
1922                                 }
1923                         }
1924                 }
1925         }
1926 }
1927
1928 void R_DrawCollisionBrush(colbrushf_t *brush)
1929 {
1930         int i;
1931         i = ((int)brush) / sizeof(colbrushf_t);
1932         GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
1933         GL_VertexPointer(brush->points->v);
1934         R_Mesh_Draw(brush->numpoints, brush->numtriangles, brush->elements);
1935 }
1936
1937 void R_Q3BSP_DrawSkyFace(entity_render_t *ent, q3mface_t *face)
1938 {
1939         rmeshstate_t m;
1940         if (!face->num_triangles)
1941                 return;
1942         c_faces++;
1943         if (skyrendernow)
1944         {
1945                 skyrendernow = false;
1946                 if (skyrendermasked)
1947                         R_Sky();
1948         }
1949
1950         R_Mesh_Matrix(&ent->matrix);
1951
1952         GL_Color(fogcolor[0], fogcolor[1], fogcolor[2], 1);
1953         if (skyrendermasked)
1954         {
1955                 // depth-only (masking)
1956                 qglColorMask(0,0,0,0);
1957                 // just to make sure that braindead drivers don't draw anything
1958                 // despite that colormask...
1959                 GL_BlendFunc(GL_ZERO, GL_ONE);
1960         }
1961         else
1962         {
1963                 // fog sky
1964                 GL_BlendFunc(GL_ONE, GL_ZERO);
1965         }
1966         GL_DepthMask(true);
1967         GL_DepthTest(true);
1968
1969         memset(&m, 0, sizeof(m));
1970         R_Mesh_State_Texture(&m);
1971
1972         GL_VertexPointer(face->data_vertex3f);
1973         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
1974         qglColorMask(1,1,1,1);
1975 }
1976
1977 void R_Q3BSP_DrawFace_OpaqueWall_Pass_OpaqueGlow(entity_render_t *ent, q3mface_t *face)
1978 {
1979         rmeshstate_t m;
1980         memset(&m, 0, sizeof(m));
1981         GL_BlendFunc(GL_ONE, GL_ZERO);
1982         GL_DepthMask(true);
1983         GL_DepthTest(true);
1984         if (face->texture->skin.glow)
1985         {
1986                 m.tex[0] = R_GetTexture(face->texture->skin.glow);
1987                 m.pointer_texcoord[0] = face->data_texcoordtexture2f;
1988                 GL_Color(1, 1, 1, 1);
1989         }
1990         else
1991                 GL_Color(0, 0, 0, 1);
1992         R_Mesh_State_Texture(&m);
1993         GL_VertexPointer(face->data_vertex3f);
1994         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
1995 }
1996
1997 void R_Q3BSP_DrawFace_OpaqueWall_Pass_TextureLightmapCombine(entity_render_t *ent, q3mface_t *face)
1998 {
1999         rmeshstate_t m;
2000         memset(&m, 0, sizeof(m));
2001         GL_BlendFunc(GL_ONE, GL_ZERO);
2002         GL_DepthMask(true);
2003         GL_DepthTest(true);
2004         m.tex[0] = R_GetTexture(face->texture->skin.base);
2005         m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2006         m.tex[1] = R_GetTexture(face->lightmaptexture);
2007         m.pointer_texcoord[1] = face->data_texcoordlightmap2f;
2008         m.texrgbscale[1] = 2;
2009         GL_Color(1, 1, 1, 1);
2010         R_Mesh_State_Texture(&m);
2011         GL_VertexPointer(face->data_vertex3f);
2012         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2013 }
2014
2015 void R_Q3BSP_DrawFace_OpaqueWall_Pass_Texture(entity_render_t *ent, q3mface_t *face)
2016 {
2017         rmeshstate_t m;
2018         memset(&m, 0, sizeof(m));
2019         GL_BlendFunc(GL_ONE, GL_ZERO);
2020         GL_DepthMask(true);
2021         GL_DepthTest(true);
2022         m.tex[0] = R_GetTexture(face->texture->skin.base);
2023         m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2024         GL_Color(1, 1, 1, 1);
2025         R_Mesh_State_Texture(&m);
2026         GL_VertexPointer(face->data_vertex3f);
2027         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2028 }
2029
2030 void R_Q3BSP_DrawFace_OpaqueWall_Pass_Lightmap(entity_render_t *ent, q3mface_t *face)
2031 {
2032         rmeshstate_t m;
2033         memset(&m, 0, sizeof(m));
2034         GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
2035         GL_DepthMask(false);
2036         GL_DepthTest(true);
2037         m.tex[0] = R_GetTexture(face->lightmaptexture);
2038         m.pointer_texcoord[0] = face->data_texcoordlightmap2f;
2039         GL_Color(1, 1, 1, 1);
2040         R_Mesh_State_Texture(&m);
2041         GL_VertexPointer(face->data_vertex3f);
2042         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2043 }
2044
2045 void R_Q3BSP_DrawFace_OpaqueWall_Pass_LightmapOnly(entity_render_t *ent, q3mface_t *face)
2046 {
2047         rmeshstate_t m;
2048         memset(&m, 0, sizeof(m));
2049         GL_BlendFunc(GL_ONE, GL_ZERO);
2050         GL_DepthMask(true);
2051         GL_DepthTest(true);
2052         m.tex[0] = R_GetTexture(face->lightmaptexture);
2053         m.pointer_texcoord[0] = face->data_texcoordlightmap2f;
2054         if (r_shadow_realtime_world.integer)
2055                 GL_Color(r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, r_shadow_realtime_world_lightmaps.value, 1);
2056         else
2057                 GL_Color(1, 1, 1, 1);
2058         R_Mesh_State_Texture(&m);
2059         GL_VertexPointer(face->data_vertex3f);
2060         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2061 }
2062
2063 void R_Q3BSP_DrawFace_OpaqueWall_Pass_Glow(entity_render_t *ent, q3mface_t *face)
2064 {
2065         rmeshstate_t m;
2066         memset(&m, 0, sizeof(m));
2067         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2068         GL_DepthMask(false);
2069         GL_DepthTest(true);
2070         if (face->texture->skin.glow)
2071         {
2072                 m.tex[0] = R_GetTexture(face->texture->skin.glow);
2073                 m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2074                 GL_Color(1, 1, 1, 1);
2075         }
2076         else
2077                 GL_Color(0, 0, 0, 1);
2078         R_Mesh_State_Texture(&m);
2079         GL_VertexPointer(face->data_vertex3f);
2080         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2081 }
2082
2083 void R_Q3BSP_DrawFace_OpaqueWall_Pass_TextureVertex(entity_render_t *ent, q3mface_t *face)
2084 {
2085         int i;
2086         float mul;
2087         rmeshstate_t m;
2088         memset(&m, 0, sizeof(m));
2089         GL_BlendFunc(GL_ONE, GL_ZERO);
2090         GL_DepthMask(true);
2091         GL_DepthTest(true);
2092         m.tex[0] = R_GetTexture(face->texture->skin.base);
2093         m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2094         mul = 2.0f;
2095         if (r_shadow_realtime_world.integer && r_shadow_realtime_world_lightmaps.value != 1)
2096                 mul *= r_shadow_realtime_world_lightmaps.value;
2097         if (mul == 2 && gl_combine.integer)
2098         {
2099                 m.texrgbscale[0] = 2;
2100                 GL_ColorPointer(face->data_color4f);
2101         }
2102         else if (mul == 1)
2103                 GL_ColorPointer(face->data_color4f);
2104         else
2105         {
2106                 for (i = 0;i < face->num_vertices;i++)
2107                 {
2108                         varray_color4f[i*4+0] = face->data_color4f[i*4+0] * mul;
2109                         varray_color4f[i*4+1] = face->data_color4f[i*4+1] * mul;
2110                         varray_color4f[i*4+2] = face->data_color4f[i*4+2] * mul;
2111                         varray_color4f[i*4+3] = face->data_color4f[i*4+3];
2112                 }
2113                 GL_ColorPointer(varray_color4f);
2114         }
2115         R_Mesh_State_Texture(&m);
2116         GL_VertexPointer(face->data_vertex3f);
2117         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2118 }
2119
2120 void R_Q3BSP_DrawFace_OpaqueWall_Pass_VertexOnly(entity_render_t *ent, q3mface_t *face)
2121 {
2122         int i;
2123         float mul;
2124         rmeshstate_t m;
2125         memset(&m, 0, sizeof(m));
2126         GL_BlendFunc(GL_ONE, GL_ZERO);
2127         GL_DepthMask(true);
2128         GL_DepthTest(true);
2129         R_Mesh_State_Texture(&m);
2130         mul = 2.0f;
2131         if (r_shadow_realtime_world.integer && r_shadow_realtime_world_lightmaps.value != 1)
2132                 mul *= r_shadow_realtime_world_lightmaps.value;
2133         if (mul == 1)
2134                 GL_ColorPointer(face->data_color4f);
2135         else
2136         {
2137                 for (i = 0;i < face->num_vertices;i++)
2138                 {
2139                         varray_color4f[i*4+0] = face->data_color4f[i*4+0] * 2.0f;
2140                         varray_color4f[i*4+1] = face->data_color4f[i*4+1] * 2.0f;
2141                         varray_color4f[i*4+2] = face->data_color4f[i*4+2] * 2.0f;
2142                         varray_color4f[i*4+3] = face->data_color4f[i*4+3];
2143                 }
2144                 GL_ColorPointer(varray_color4f);
2145         }
2146         GL_VertexPointer(face->data_vertex3f);
2147         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2148 }
2149
2150 void R_Q3BSP_DrawFace_OpaqueWall_Pass_AddTextureAmbient(entity_render_t *ent, q3mface_t *face)
2151 {
2152         rmeshstate_t m;
2153         memset(&m, 0, sizeof(m));
2154         GL_BlendFunc(GL_ONE, GL_ONE);
2155         GL_DepthMask(true);
2156         GL_DepthTest(true);
2157         m.tex[0] = R_GetTexture(face->texture->skin.base);
2158         m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2159         GL_Color(r_ambient.value * (1.0f / 128.0f), r_ambient.value * (1.0f / 128.0f), r_ambient.value * (1.0f / 128.0f), 1);
2160         R_Mesh_State_Texture(&m);
2161         GL_VertexPointer(face->data_vertex3f);
2162         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2163 }
2164
2165 void R_Q3BSP_DrawFace_TransparentCallback(const void *voident, int facenumber)
2166 {
2167         const entity_render_t *ent = voident;
2168         q3mface_t *face = ent->model->brushq3.data_faces + facenumber;
2169         rmeshstate_t m;
2170         R_Mesh_Matrix(&ent->matrix);
2171         memset(&m, 0, sizeof(m));
2172         if (ent->effects & EF_ADDITIVE)
2173                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2174         else
2175                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2176         GL_DepthMask(false);
2177         GL_DepthTest(true);
2178         m.tex[0] = R_GetTexture(face->texture->skin.base);
2179         m.pointer_texcoord[0] = face->data_texcoordtexture2f;
2180         // LordHavoc: quake3 was not able to do this; lit transparent surfaces
2181         if (gl_combine.integer)
2182         {
2183                 m.texrgbscale[0] = 2;
2184                 if (r_textureunits.integer >= 2)
2185                 {
2186                         m.tex[1] = R_GetTexture(face->lightmaptexture);
2187                         m.pointer_texcoord[1] = face->data_texcoordlightmap2f;
2188                         GL_Color(1, 1, 1, ent->alpha);
2189                 }
2190                 else
2191                 {
2192                         if (ent->alpha == 1)
2193                                 GL_ColorPointer(face->data_color4f);
2194                         else
2195                         {
2196                                 int i;
2197                                 for (i = 0;i < face->num_vertices;i++)
2198                                 {
2199                                         varray_color4f[i*4+0] = face->data_color4f[i*4+0];
2200                                         varray_color4f[i*4+1] = face->data_color4f[i*4+1];
2201                                         varray_color4f[i*4+2] = face->data_color4f[i*4+2];
2202                                         varray_color4f[i*4+3] = face->data_color4f[i*4+3] * ent->alpha;
2203                                 }
2204                                 GL_ColorPointer(varray_color4f);
2205                         }
2206                 }
2207         }
2208         else
2209         {
2210                 int i;
2211                 for (i = 0;i < face->num_vertices;i++)
2212                 {
2213                         varray_color4f[i*4+0] = face->data_color4f[i*4+0] * 2.0f;
2214                         varray_color4f[i*4+1] = face->data_color4f[i*4+1] * 2.0f;
2215                         varray_color4f[i*4+2] = face->data_color4f[i*4+2] * 2.0f;
2216                         varray_color4f[i*4+3] = face->data_color4f[i*4+3] * ent->alpha;
2217                 }
2218                 GL_ColorPointer(varray_color4f);
2219         }
2220         R_Mesh_State_Texture(&m);
2221         GL_VertexPointer(face->data_vertex3f);
2222         qglDisable(GL_CULL_FACE);
2223         R_Mesh_Draw(face->num_vertices, face->num_triangles, face->data_element3i);
2224         qglEnable(GL_CULL_FACE);
2225 }
2226
2227 void R_Q3BSP_DrawFace(entity_render_t *ent, q3mface_t *face)
2228 {
2229         if (!face->num_triangles)
2230                 return;
2231         if (face->texture->surfaceparms)
2232         {
2233                 if (face->texture->surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NODRAW))
2234                         return;
2235         }
2236         c_faces++;
2237         face->visframe = r_framecount;
2238         if ((face->texture->surfaceparms & Q3SURFACEPARM_TRANS) || ent->alpha < 1 || (ent->effects & EF_ADDITIVE))
2239         {
2240                 vec3_t facecenter, center;
2241                 facecenter[0] = (face->mins[0] + face->maxs[0]) * 0.5f;
2242                 facecenter[1] = (face->mins[1] + face->maxs[1]) * 0.5f;
2243                 facecenter[2] = (face->mins[2] + face->maxs[2]) * 0.5f;
2244                 Matrix4x4_Transform(&ent->matrix, facecenter, center);
2245                 R_MeshQueue_AddTransparent(center, R_Q3BSP_DrawFace_TransparentCallback, ent, face - ent->model->brushq3.data_faces);
2246                 return;
2247         }
2248         R_Mesh_Matrix(&ent->matrix);
2249         if (r_shadow_realtime_world.integer && r_shadow_realtime_world_lightmaps.value <= 0)
2250                 R_Q3BSP_DrawFace_OpaqueWall_Pass_OpaqueGlow(ent, face);
2251         else if ((ent->effects & EF_FULLBRIGHT) || r_fullbright.integer)
2252         {
2253                 R_Q3BSP_DrawFace_OpaqueWall_Pass_Texture(ent, face);
2254                 if (face->texture->skin.glow)
2255                         R_Q3BSP_DrawFace_OpaqueWall_Pass_Glow(ent, face);
2256         }
2257         else if (face->lightmaptexture)
2258         {
2259                 if (gl_lightmaps.integer)
2260                         R_Q3BSP_DrawFace_OpaqueWall_Pass_LightmapOnly(ent, face);
2261                 else
2262                 {
2263                         if (r_textureunits.integer >= 2 && gl_combine.integer)
2264                                 R_Q3BSP_DrawFace_OpaqueWall_Pass_TextureLightmapCombine(ent, face);
2265                         else
2266                         {
2267                                 R_Q3BSP_DrawFace_OpaqueWall_Pass_Texture(ent, face);
2268                                 R_Q3BSP_DrawFace_OpaqueWall_Pass_Lightmap(ent, face);
2269                         }
2270                         if (face->texture->skin.glow)
2271                                 R_Q3BSP_DrawFace_OpaqueWall_Pass_Glow(ent, face);
2272                 }
2273         }
2274         else
2275         {
2276                 if (gl_lightmaps.integer)
2277                         R_Q3BSP_DrawFace_OpaqueWall_Pass_VertexOnly(ent, face);
2278                 else
2279                 {
2280                         R_Q3BSP_DrawFace_OpaqueWall_Pass_TextureVertex(ent, face);
2281                         if (face->texture->skin.glow)
2282                                 R_Q3BSP_DrawFace_OpaqueWall_Pass_Glow(ent, face);
2283                 }
2284         }
2285         if (r_ambient.value)
2286                 R_Q3BSP_DrawFace_OpaqueWall_Pass_AddTextureAmbient(ent, face);
2287 }
2288
2289 void R_Q3BSP_RecursiveWorldNode(entity_render_t *ent, q3mnode_t *node, const vec3_t modelorg, qbyte *pvs, int markframe)
2290 {
2291         int i;
2292         q3mleaf_t *leaf;
2293         for (;;)
2294         {
2295                 if (R_CullBox(node->mins, node->maxs))
2296                         return;
2297                 if (!node->plane)
2298                         break;
2299                 c_nodes++;
2300                 R_Q3BSP_RecursiveWorldNode(ent, node->children[0], modelorg, pvs, markframe);
2301                 node = node->children[1];
2302         }
2303         leaf = (q3mleaf_t *)node;
2304         if (CHECKPVSBIT(pvs, leaf->clusterindex))
2305         {
2306                 c_leafs++;
2307                 for (i = 0;i < leaf->numleaffaces;i++)
2308                         leaf->firstleafface[i]->markframe = markframe;
2309         }
2310 }
2311
2312 // FIXME: num_leafs needs to be recalculated at load time to include only
2313 // node-referenced leafs, as some maps are incorrectly compiled with leafs for
2314 // the submodels (which would render the submodels occasionally, as part of
2315 // the world - not good)
2316 void R_Q3BSP_MarkLeafPVS(entity_render_t *ent, qbyte *pvs, int markframe)
2317 {
2318         int i, j;
2319         q3mleaf_t *leaf;
2320         for (j = 0, leaf = ent->model->brushq3.data_leafs;j < ent->model->brushq3.num_leafs;j++, leaf++)
2321         {
2322                 if (CHECKPVSBIT(pvs, leaf->clusterindex))
2323                 {
2324                         c_leafs++;
2325                         for (i = 0;i < leaf->numleaffaces;i++)
2326                                 leaf->firstleafface[i]->markframe = markframe;
2327                 }
2328         }
2329 }
2330
2331 static int r_q3bsp_framecount = -1;
2332
2333 void R_Q3BSP_DrawSky(entity_render_t *ent)
2334 {
2335         int i;
2336         q3mface_t *face;
2337         vec3_t modelorg;
2338         model_t *model;
2339         qbyte *pvs;
2340         R_Mesh_Matrix(&ent->matrix);
2341         model = ent->model;
2342         if (r_drawcollisionbrushes.integer < 2)
2343         {
2344                 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
2345                 if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
2346                 {
2347                         if (r_q3bsp_framecount != r_framecount)
2348                         {
2349                                 r_q3bsp_framecount = r_framecount;
2350                                 R_Q3BSP_RecursiveWorldNode(ent, model->brushq3.data_nodes, modelorg, pvs, r_framecount);
2351                                 //R_Q3BSP_MarkLeafPVS(ent, pvs, r_framecount);
2352                         }
2353                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2354                                 if (face->markframe == r_framecount && (face->texture->surfaceflags & Q3SURFACEFLAG_SKY) && !R_CullBox(face->mins, face->maxs))
2355                                         R_Q3BSP_DrawSkyFace(ent, face);
2356                 }
2357                 else
2358                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2359                                 if ((face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2360                                         R_Q3BSP_DrawSkyFace(ent, face);
2361         }
2362 }
2363
2364 void R_Q3BSP_Draw(entity_render_t *ent)
2365 {
2366         int i;
2367         q3mface_t *face;
2368         vec3_t modelorg;
2369         model_t *model;
2370         qbyte *pvs;
2371         R_Mesh_Matrix(&ent->matrix);
2372         model = ent->model;
2373         if (r_drawcollisionbrushes.integer < 2)
2374         {
2375                 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
2376                 if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
2377                 {
2378                         if (r_q3bsp_framecount != r_framecount)
2379                         {
2380                                 r_q3bsp_framecount = r_framecount;
2381                                 R_Q3BSP_RecursiveWorldNode(ent, model->brushq3.data_nodes, modelorg, pvs, r_framecount);
2382                                 //R_Q3BSP_MarkLeafPVS(ent, pvs, r_framecount);
2383                         }
2384                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2385                                 if (face->markframe == r_framecount && !R_CullBox(face->mins, face->maxs))
2386                                         R_Q3BSP_DrawFace(ent, face);
2387                 }
2388                 else
2389                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2390                                 R_Q3BSP_DrawFace(ent, face);
2391         }
2392         if (r_drawcollisionbrushes.integer >= 1)
2393         {
2394                 rmeshstate_t m;
2395                 memset(&m, 0, sizeof(m));
2396                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2397                 GL_DepthMask(false);
2398                 GL_DepthTest(true);
2399                 R_Mesh_State_Texture(&m);
2400                 qglPolygonOffset(r_drawcollisionbrushes_polygonfactor.value, r_drawcollisionbrushes_polygonoffset.value);
2401                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
2402                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf && model->brushq3.data_thismodel->firstbrush[i].colbrushf->numtriangles)
2403                                 R_DrawCollisionBrush(model->brushq3.data_thismodel->firstbrush[i].colbrushf);
2404                 qglPolygonOffset(0, 0);
2405         }
2406 }
2407
2408 void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
2409 {
2410 #if 0
2411         int i;
2412         q3mface_t *face;
2413         vec3_t modelorg, lightmins, lightmaxs;
2414         model_t *model;
2415         float projectdistance;
2416         projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
2417         if (r_drawcollisionbrushes.integer < 2)
2418         {
2419                 model = ent->model;
2420                 R_Mesh_Matrix(&ent->matrix);
2421                 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
2422                 lightmins[0] = relativelightorigin[0] - lightradius;
2423                 lightmins[1] = relativelightorigin[1] - lightradius;
2424                 lightmins[2] = relativelightorigin[2] - lightradius;
2425                 lightmaxs[0] = relativelightorigin[0] + lightradius;
2426                 lightmaxs[1] = relativelightorigin[1] + lightradius;
2427                 lightmaxs[2] = relativelightorigin[2] + lightradius;
2428                 //if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
2429                 //      R_Q3BSP_RecursiveWorldNode(ent, model->brushq3.data_nodes, modelorg, pvs, ++markframe);
2430                 //else
2431                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2432                                 if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs))
2433                                         R_Shadow_VolumeFromSphere(face->num_vertices, face->num_triangles, face->data_vertex3f, face->data_element3i, face->data_neighbor3i, relativelightorigin, projectdistance, lightradius);
2434         }
2435 #else
2436         int i, j, t;
2437         const int *e;
2438         q3mface_t *face;
2439         vec3_t modelorg, lightmins, lightmaxs;
2440         model_t *model;
2441         float projectdistance;
2442         projectdistance = lightradius + ent->model->radius;//projectdistance = 1000000000.0f;//lightradius + ent->model->radius;
2443         if (r_drawcollisionbrushes.integer < 2)
2444         {
2445                 model = ent->model;
2446                 R_Mesh_Matrix(&ent->matrix);
2447                 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
2448                 lightmins[0] = relativelightorigin[0] - lightradius;
2449                 lightmins[1] = relativelightorigin[1] - lightradius;
2450                 lightmins[2] = relativelightorigin[2] - lightradius;
2451                 lightmaxs[0] = relativelightorigin[0] + lightradius;
2452                 lightmaxs[1] = relativelightorigin[1] + lightradius;
2453                 lightmaxs[2] = relativelightorigin[2] + lightradius;
2454                 R_Shadow_PrepareShadowMark(model->brush.shadowmesh->numtriangles);
2455                 for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2456                 {
2457                         if (BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs))
2458                         {
2459                                 for (j = 0, t = face->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;j < face->num_triangles;j++, t++, e += 3)
2460                                 {
2461                                         const float *v[3];
2462                                         v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
2463                                         v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
2464                                         v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
2465                                         if (PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]) && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
2466                                                 shadowmarklist[numshadowmark++] = t;
2467                                 }
2468                         }
2469                 }
2470                 R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, projectdistance, numshadowmark, shadowmarklist);
2471         }
2472 #endif
2473 }
2474
2475 void R_Q3BSP_DrawFaceLight(entity_render_t *ent, q3mface_t *face, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
2476 {
2477         if ((face->texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || !face->num_triangles)
2478                 return;
2479         R_Shadow_DiffuseLighting(face->num_vertices, face->num_triangles, face->data_element3i, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, relativelightorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, face->texture->skin.base, face->texture->skin.nmap, lightcubemap);
2480         R_Shadow_SpecularLighting(face->num_vertices, face->num_triangles, face->data_element3i, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, face->texture->skin.gloss, face->texture->skin.nmap, lightcubemap);
2481 }
2482
2483 void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap)
2484 {
2485         int i;
2486         q3mface_t *face;
2487         vec3_t modelorg, lightmins, lightmaxs;
2488         model_t *model;
2489         //qbyte *pvs;
2490         //static int markframe = 0;
2491         if (r_drawcollisionbrushes.integer < 2)
2492         {
2493                 model = ent->model;
2494                 R_Mesh_Matrix(&ent->matrix);
2495                 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
2496                 lightmins[0] = relativelightorigin[0] - lightradius;
2497                 lightmins[1] = relativelightorigin[1] - lightradius;
2498                 lightmins[2] = relativelightorigin[2] - lightradius;
2499                 lightmaxs[0] = relativelightorigin[0] + lightradius;
2500                 lightmaxs[1] = relativelightorigin[1] + lightradius;
2501                 lightmaxs[2] = relativelightorigin[2] + lightradius;
2502                 //if (ent == &cl_entities[0].render && model->brush.num_pvsclusters && !r_novis.integer && (pvs = model->brush.GetPVS(model, modelorg)))
2503                 //      R_Q3BSP_RecursiveWorldNode(ent, model->brushq3.data_nodes, modelorg, pvs, ++markframe);
2504                 //else
2505                         for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++)
2506                                 if ((ent != &cl_entities[0].render || face->visframe == r_framecount) && BoxesOverlap(lightmins, lightmaxs, face->mins, face->maxs))
2507                                         R_Q3BSP_DrawFaceLight(ent, face, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, lightcubemap);
2508         }
2509 }
2510
2511 static void gl_surf_start(void)
2512 {
2513 }
2514
2515 static void gl_surf_shutdown(void)
2516 {
2517 }
2518
2519 static void gl_surf_newmap(void)
2520 {
2521 }
2522
2523 void GL_Surf_Init(void)
2524 {
2525         int i;
2526         dlightdivtable[0] = 4194304;
2527         for (i = 1;i < 32768;i++)
2528                 dlightdivtable[i] = 4194304 / (i << 7);
2529
2530         Cvar_RegisterVariable(&r_ambient);
2531         Cvar_RegisterVariable(&r_drawportals);
2532         Cvar_RegisterVariable(&r_testvis);
2533         Cvar_RegisterVariable(&r_floatbuildlightmap);
2534         Cvar_RegisterVariable(&r_detailtextures);
2535         Cvar_RegisterVariable(&r_surfaceworldnode);
2536         Cvar_RegisterVariable(&r_drawcollisionbrushes_polygonfactor);
2537         Cvar_RegisterVariable(&r_drawcollisionbrushes_polygonoffset);
2538         Cvar_RegisterVariable(&gl_lightmaps);
2539
2540         R_RegisterModule("GL_Surf", gl_surf_start, gl_surf_shutdown, gl_surf_newmap);
2541 }
2542