]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_rmain.c
vid_vsync 0 now causes a warning due to the fact GLX_SGI_swap_control has no way...
[divverent/darkplaces.git] / gl_rmain.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_main.c
21
22 #include "quakedef.h"
23 #include "r_shadow.h"
24
25 // used for dlight push checking and other things
26 int r_framecount;
27
28 // used for visibility checking
29 qbyte r_pvsbits[(MAX_MAP_LEAFS+7)>>3];
30
31 mplane_t frustum[4];
32
33 matrix4x4_t r_identitymatrix;
34
35 int c_alias_polys, c_light_polys, c_faces, c_nodes, c_leafs, c_models, c_bmodels, c_sprites, c_particles, c_dlights, c_meshs, c_meshelements, c_rt_lights, c_rt_clears, c_rt_scissored, c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris, c_rtcached_shadowmeshes, c_rtcached_shadowtris, c_bloom, c_bloomcopies, c_bloomcopypixels, c_bloomdraws, c_bloomdrawpixels;
36
37 // true during envmap command capture
38 qboolean envmap;
39
40 // maximum visible distance (recalculated from world box each frame)
41 float r_farclip;
42 // brightness of world lightmaps and related lighting
43 // (often reduced when world rtlights are enabled)
44 float r_lightmapintensity;
45 // whether to draw world lights realtime, dlights realtime, and their shadows
46 qboolean r_rtworld;
47 qboolean r_rtworldshadows;
48 qboolean r_rtdlight;
49 qboolean r_rtdlightshadows;
50
51
52 // forces all rendering to draw triangle outlines
53 int r_showtrispass;
54
55 // view origin
56 vec3_t r_vieworigin;
57 vec3_t r_viewforward;
58 vec3_t r_viewleft;
59 vec3_t r_viewright;
60 vec3_t r_viewup;
61 int r_view_x;
62 int r_view_y;
63 int r_view_z;
64 int r_view_width;
65 int r_view_height;
66 int r_view_depth;
67 float r_view_fov_x;
68 float r_view_fov_y;
69 matrix4x4_t r_view_matrix;
70
71 //
72 // screen size info
73 //
74 refdef_t r_refdef;
75
76 // 8.8 fraction of base light value
77 unsigned short d_lightstylevalue[256];
78
79 cvar_t r_showtris = {0, "r_showtris", "0"};
80 cvar_t r_drawentities = {0, "r_drawentities","1"};
81 cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1"};
82 cvar_t r_speeds = {0, "r_speeds","0"};
83 cvar_t r_fullbright = {0, "r_fullbright","0"};
84 cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1"};
85 cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1"};
86 cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1"};
87 cvar_t r_drawcollisionbrushes = {0, "r_drawcollisionbrushes", "0"};
88
89 cvar_t gl_fogenable = {0, "gl_fogenable", "0"};
90 cvar_t gl_fogdensity = {0, "gl_fogdensity", "0.25"};
91 cvar_t gl_fogred = {0, "gl_fogred","0.3"};
92 cvar_t gl_foggreen = {0, "gl_foggreen","0.3"};
93 cvar_t gl_fogblue = {0, "gl_fogblue","0.3"};
94 cvar_t gl_fogstart = {0, "gl_fogstart", "0"};
95 cvar_t gl_fogend = {0, "gl_fogend","0"};
96
97 cvar_t r_textureunits = {0, "r_textureunits", "32"};
98
99 cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1"};
100 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1"};
101 cvar_t r_waterscroll = {CVAR_SAVE, "r_waterscroll", "1"};
102 cvar_t r_watershader = {CVAR_SAVE, "r_watershader", "1"};
103
104 cvar_t r_bloom = {CVAR_SAVE, "r_bloom", "0"};
105 cvar_t r_bloom_intensity = {CVAR_SAVE, "r_bloom_intensity", "2"};
106 cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "8"};
107 cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320"};
108 cvar_t r_bloom_power = {CVAR_SAVE, "r_bloom_power", "4"};
109 rtexturepool_t *r_main_texturepool;
110 rtexture_t *r_bloom_texture_screen;
111 rtexture_t *r_bloom_texture_bloom;
112
113 void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
114 {
115         int i;
116         for (i = 0;i < verts;i++)
117         {
118                 out[0] = in[0] * r;
119                 out[1] = in[1] * g;
120                 out[2] = in[2] * b;
121                 out[3] = in[3];
122                 in += 4;
123                 out += 4;
124         }
125 }
126
127 void R_FillColors(float *out, int verts, float r, float g, float b, float a)
128 {
129         int i;
130         for (i = 0;i < verts;i++)
131         {
132                 out[0] = r;
133                 out[1] = g;
134                 out[2] = b;
135                 out[3] = a;
136                 out += 4;
137         }
138 }
139
140 vec3_t fogcolor;
141 vec_t fogdensity;
142 float fog_density, fog_red, fog_green, fog_blue;
143 qboolean fogenabled;
144 qboolean oldgl_fogenable;
145 void R_UpdateFog(void)
146 {
147         if (gamemode == GAME_NEHAHRA)
148         {
149                 if (gl_fogenable.integer)
150                 {
151                         oldgl_fogenable = true;
152                         fog_density = gl_fogdensity.value;
153                         fog_red = gl_fogred.value;
154                         fog_green = gl_foggreen.value;
155                         fog_blue = gl_fogblue.value;
156                 }
157                 else if (oldgl_fogenable)
158                 {
159                         oldgl_fogenable = false;
160                         fog_density = 0;
161                         fog_red = 0;
162                         fog_green = 0;
163                         fog_blue = 0;
164                 }
165         }
166         if (fog_density)
167         {
168                 fogcolor[0] = fog_red   = bound(0.0f, fog_red  , 1.0f);
169                 fogcolor[1] = fog_green = bound(0.0f, fog_green, 1.0f);
170                 fogcolor[2] = fog_blue  = bound(0.0f, fog_blue , 1.0f);
171         }
172         if (fog_density)
173         {
174                 fogenabled = true;
175                 fogdensity = -4000.0f / (fog_density * fog_density);
176                 // fog color was already set
177         }
178         else
179                 fogenabled = false;
180 }
181
182 // FIXME: move this to client?
183 void FOG_clear(void)
184 {
185         if (gamemode == GAME_NEHAHRA)
186         {
187                 Cvar_Set("gl_fogenable", "0");
188                 Cvar_Set("gl_fogdensity", "0.2");
189                 Cvar_Set("gl_fogred", "0.3");
190                 Cvar_Set("gl_foggreen", "0.3");
191                 Cvar_Set("gl_fogblue", "0.3");
192         }
193         fog_density = fog_red = fog_green = fog_blue = 0.0f;
194 }
195
196 // FIXME: move this to client?
197 void FOG_registercvars(void)
198 {
199         if (gamemode == GAME_NEHAHRA)
200         {
201                 Cvar_RegisterVariable (&gl_fogenable);
202                 Cvar_RegisterVariable (&gl_fogdensity);
203                 Cvar_RegisterVariable (&gl_fogred);
204                 Cvar_RegisterVariable (&gl_foggreen);
205                 Cvar_RegisterVariable (&gl_fogblue);
206                 Cvar_RegisterVariable (&gl_fogstart);
207                 Cvar_RegisterVariable (&gl_fogend);
208         }
209 }
210
211 void gl_main_start(void)
212 {
213         r_main_texturepool = R_AllocTexturePool();
214         r_bloom_texture_screen = NULL;
215         r_bloom_texture_bloom = NULL;
216 }
217
218 void gl_main_shutdown(void)
219 {
220         R_FreeTexturePool(&r_main_texturepool);
221         r_bloom_texture_screen = NULL;
222         r_bloom_texture_bloom = NULL;
223 }
224
225 extern void CL_ParseEntityLump(char *entitystring);
226 void gl_main_newmap(void)
227 {
228         // FIXME: move this code to client
229         int l;
230         char *entities, entname[MAX_QPATH];
231         r_framecount = 1;
232         if (cl.worldmodel)
233         {
234                 strlcpy(entname, cl.worldmodel->name, sizeof(entname));
235                 l = strlen(entname) - 4;
236                 if (l >= 0 && !strcmp(entname + l, ".bsp"))
237                 {
238                         strcpy(entname + l, ".ent");
239                         if ((entities = FS_LoadFile(entname, tempmempool, true)))
240                         {
241                                 CL_ParseEntityLump(entities);
242                                 Mem_Free(entities);
243                                 return;
244                         }
245                 }
246                 if (cl.worldmodel->brush.entities)
247                         CL_ParseEntityLump(cl.worldmodel->brush.entities);
248         }
249 }
250
251 void GL_Main_Init(void)
252 {
253         Matrix4x4_CreateIdentity(&r_identitymatrix);
254 // FIXME: move this to client?
255         FOG_registercvars();
256         Cvar_RegisterVariable(&r_showtris);
257         Cvar_RegisterVariable(&r_drawentities);
258         Cvar_RegisterVariable(&r_drawviewmodel);
259         Cvar_RegisterVariable(&r_speeds);
260         Cvar_RegisterVariable(&r_fullbrights);
261         Cvar_RegisterVariable(&r_wateralpha);
262         Cvar_RegisterVariable(&r_dynamic);
263         Cvar_RegisterVariable(&r_fullbright);
264         Cvar_RegisterVariable(&r_textureunits);
265         Cvar_RegisterVariable(&r_lerpsprites);
266         Cvar_RegisterVariable(&r_lerpmodels);
267         Cvar_RegisterVariable(&r_waterscroll);
268         Cvar_RegisterVariable(&r_watershader);
269         Cvar_RegisterVariable(&r_drawcollisionbrushes);
270         Cvar_RegisterVariable(&r_bloom);
271         Cvar_RegisterVariable(&r_bloom_intensity);
272         Cvar_RegisterVariable(&r_bloom_blur);
273         Cvar_RegisterVariable(&r_bloom_resolution);
274         Cvar_RegisterVariable(&r_bloom_power);
275         if (gamemode == GAME_NEHAHRA || gamemode == GAME_NEXUIZ || gamemode == GAME_TENEBRAE)
276                 Cvar_SetValue("r_fullbrights", 0);
277         R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
278 }
279
280 static vec3_t r_farclip_origin;
281 static vec3_t r_farclip_direction;
282 static vec_t r_farclip_directiondist;
283 static vec_t r_farclip_meshfarclip;
284 static int r_farclip_directionbit0;
285 static int r_farclip_directionbit1;
286 static int r_farclip_directionbit2;
287
288 // enlarge farclip to accomodate box
289 static void R_FarClip_Box(vec3_t mins, vec3_t maxs)
290 {
291         float d;
292         d = (r_farclip_directionbit0 ? mins[0] : maxs[0]) * r_farclip_direction[0]
293           + (r_farclip_directionbit1 ? mins[1] : maxs[1]) * r_farclip_direction[1]
294           + (r_farclip_directionbit2 ? mins[2] : maxs[2]) * r_farclip_direction[2];
295         if (r_farclip_meshfarclip < d)
296                 r_farclip_meshfarclip = d;
297 }
298
299 // return farclip value
300 static float R_FarClip(vec3_t origin, vec3_t direction, vec_t startfarclip)
301 {
302         int i;
303
304         VectorCopy(origin, r_farclip_origin);
305         VectorCopy(direction, r_farclip_direction);
306         r_farclip_directiondist = DotProduct(r_farclip_origin, r_farclip_direction);
307         r_farclip_directionbit0 = r_farclip_direction[0] < 0;
308         r_farclip_directionbit1 = r_farclip_direction[1] < 0;
309         r_farclip_directionbit2 = r_farclip_direction[2] < 0;
310         r_farclip_meshfarclip = r_farclip_directiondist + startfarclip;
311
312         if (r_refdef.worldmodel)
313                 R_FarClip_Box(r_refdef.worldmodel->normalmins, r_refdef.worldmodel->normalmaxs);
314         for (i = 0;i < r_refdef.numentities;i++)
315                 R_FarClip_Box(r_refdef.entities[i]->mins, r_refdef.entities[i]->maxs);
316         
317         return r_farclip_meshfarclip - r_farclip_directiondist;
318 }
319
320 extern void R_Textures_Init(void);
321 extern void Mod_RenderInit(void);
322 extern void GL_Draw_Init(void);
323 extern void GL_Main_Init(void);
324 extern void R_Shadow_Init(void);
325 extern void GL_Models_Init(void);
326 extern void R_Sky_Init(void);
327 extern void GL_Surf_Init(void);
328 extern void R_Crosshairs_Init(void);
329 extern void R_Light_Init(void);
330 extern void R_Particles_Init(void);
331 extern void R_Explosion_Init(void);
332 extern void ui_init(void);
333 extern void gl_backend_init(void);
334 extern void Sbar_Init(void);
335 extern void R_LightningBeams_Init(void);
336
337 void Render_Init(void)
338 {
339         R_Textures_Init();
340         Mod_RenderInit();
341         gl_backend_init();
342         R_MeshQueue_Init();
343         GL_Draw_Init();
344         GL_Main_Init();
345         R_Shadow_Init();
346         GL_Models_Init();
347         R_Sky_Init();
348         GL_Surf_Init();
349         R_Crosshairs_Init();
350         R_Light_Init();
351         R_Particles_Init();
352         R_Explosion_Init();
353         //ui_init();
354         UI_Init();
355         Sbar_Init();
356         R_LightningBeams_Init();
357 }
358
359 /*
360 ===============
361 GL_Init
362 ===============
363 */
364 extern char *ENGINE_EXTENSIONS;
365 void GL_Init (void)
366 {
367         VID_CheckExtensions();
368
369         // LordHavoc: report supported extensions
370         Con_DPrintf("\nengine extensions: %s\n", ENGINE_EXTENSIONS);
371
372         // clear to black (loading plaque will be seen over this)
373         qglClearColor(0,0,0,1);
374         qglClear(GL_COLOR_BUFFER_BIT);
375 }
376
377 int R_CullBox(const vec3_t mins, const vec3_t maxs)
378 {
379         int i;
380         mplane_t *p;
381         for (i = 0;i < 4;i++)
382         {
383                 p = frustum + i;
384                 switch(p->signbits)
385                 {
386                 default:
387                 case 0:
388                         if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
389                                 return true;
390                         break;
391                 case 1:
392                         if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
393                                 return true;
394                         break;
395                 case 2:
396                         if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
397                                 return true;
398                         break;
399                 case 3:
400                         if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
401                                 return true;
402                         break;
403                 case 4:
404                         if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
405                                 return true;
406                         break;
407                 case 5:
408                         if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
409                                 return true;
410                         break;
411                 case 6:
412                         if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
413                                 return true;
414                         break;
415                 case 7:
416                         if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
417                                 return true;
418                         break;
419                 }
420         }
421         return false;
422 }
423
424 //==================================================================================
425
426 static void R_MarkEntities (void)
427 {
428         int i;
429         entity_render_t *ent;
430
431         if (!r_drawentities.integer)
432                 return;
433
434         for (i = 0;i < r_refdef.numentities;i++)
435         {
436                 ent = r_refdef.entities[i];
437                 Mod_CheckLoaded(ent->model);
438                 // some of the renderer still relies on origin...
439                 Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
440                 // some of the renderer still relies on scale...
441                 ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
442                 R_UpdateEntLights(ent);
443                 if ((chase_active.integer || !(ent->flags & RENDER_EXTERIORMODEL))
444                  && (!VIS_CullBox(ent->mins, ent->maxs) || (ent->effects & EF_NODEPTHTEST))
445                  && (!envmap || !(ent->flags & (RENDER_VIEWMODEL | RENDER_EXTERIORMODEL))))
446                         ent->visframe = r_framecount;
447         }
448 }
449
450 // only used if skyrendermasked, and normally returns false
451 int R_DrawBrushModelsSky (void)
452 {
453         int i, sky;
454         entity_render_t *ent;
455
456         if (!r_drawentities.integer)
457                 return false;
458
459         sky = false;
460         for (i = 0;i < r_refdef.numentities;i++)
461         {
462                 ent = r_refdef.entities[i];
463                 if (ent->visframe == r_framecount && ent->model && ent->model->DrawSky)
464                 {
465                         ent->model->DrawSky(ent);
466                         sky = true;
467                 }
468         }
469         return sky;
470 }
471
472 void R_DrawNoModel(entity_render_t *ent);
473 void R_DrawModels(void)
474 {
475         int i;
476         entity_render_t *ent;
477
478         if (!r_drawentities.integer)
479                 return;
480
481         for (i = 0;i < r_refdef.numentities;i++)
482         {
483                 ent = r_refdef.entities[i];
484                 if (ent->visframe == r_framecount)
485                 {
486                         if (ent->model && ent->model->Draw != NULL)
487                                 ent->model->Draw(ent);
488                         else
489                                 R_DrawNoModel(ent);
490                 }
491         }
492 }
493
494 static void R_SetFrustum(void)
495 {
496         // break apart the view matrix into vectors for various purposes
497         Matrix4x4_ToVectors(&r_view_matrix, r_viewforward, r_viewleft, r_viewup, r_vieworigin);
498         VectorNegate(r_viewleft, r_viewright);
499
500         // LordHavoc: note to all quake engine coders, the special case for 90
501         // degrees assumed a square view (wrong), so I removed it, Quake2 has it
502         // disabled as well.
503
504         // rotate R_VIEWFORWARD right by FOV_X/2 degrees
505         RotatePointAroundVector( frustum[0].normal, r_viewup, r_viewforward, -(90 - r_view_fov_x / 2));
506         frustum[0].dist = DotProduct (r_vieworigin, frustum[0].normal);
507         PlaneClassify(&frustum[0]);
508
509         // rotate R_VIEWFORWARD left by FOV_X/2 degrees
510         RotatePointAroundVector( frustum[1].normal, r_viewup, r_viewforward, (90 - r_view_fov_x / 2));
511         frustum[1].dist = DotProduct (r_vieworigin, frustum[1].normal);
512         PlaneClassify(&frustum[1]);
513
514         // rotate R_VIEWFORWARD up by FOV_X/2 degrees
515         RotatePointAroundVector( frustum[2].normal, r_viewleft, r_viewforward, -(90 - r_view_fov_y / 2));
516         frustum[2].dist = DotProduct (r_vieworigin, frustum[2].normal);
517         PlaneClassify(&frustum[2]);
518
519         // rotate R_VIEWFORWARD down by FOV_X/2 degrees
520         RotatePointAroundVector( frustum[3].normal, r_viewleft, r_viewforward, (90 - r_view_fov_y / 2));
521         frustum[3].dist = DotProduct (r_vieworigin, frustum[3].normal);
522         PlaneClassify(&frustum[3]);
523 }
524
525 static void R_BlendView(void)
526 {
527         rmeshstate_t m;
528
529         if (r_refdef.viewblend[3] < 0.01f && !r_bloom.integer)
530                 return;
531
532         GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
533         GL_DepthMask(true);
534         GL_DepthTest(false);
535         R_Mesh_Matrix(&r_identitymatrix);
536         varray_vertex3f[0] = 0;varray_vertex3f[1] = 0;varray_vertex3f[2] = 0;
537         varray_vertex3f[3] = 1;varray_vertex3f[4] = 0;varray_vertex3f[5] = 0;
538         varray_vertex3f[6] = 1;varray_vertex3f[7] = 1;varray_vertex3f[8] = 0;
539         varray_vertex3f[9] = 0;varray_vertex3f[10] = 1;varray_vertex3f[11] = 0;
540         if (r_bloom.integer && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512)
541         {
542                 int screenwidth, screenheight, bloomwidth, bloomheight, x, dobloomblend, range;
543                 float xoffset, yoffset, r;
544                 c_bloom++;
545                 for (screenwidth = 1;screenwidth < vid.realwidth;screenwidth *= 2);
546                 for (screenheight = 1;screenheight < vid.realheight;screenheight *= 2);
547                 bloomwidth = min(r_view_width, r_bloom_resolution.integer);
548                 bloomheight = min(r_view_height, bloomwidth * r_view_height / r_view_width);
549                 varray_texcoord2f[0][0] = 0;
550                 varray_texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
551                 varray_texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
552                 varray_texcoord2f[0][3] = (float)r_view_height / (float)screenheight;
553                 varray_texcoord2f[0][4] = (float)r_view_width / (float)screenwidth;
554                 varray_texcoord2f[0][5] = 0;
555                 varray_texcoord2f[0][6] = 0;
556                 varray_texcoord2f[0][7] = 0;
557                 varray_texcoord2f[1][0] = 0;
558                 varray_texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
559                 varray_texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
560                 varray_texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
561                 varray_texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
562                 varray_texcoord2f[1][5] = 0;
563                 varray_texcoord2f[1][6] = 0;
564                 varray_texcoord2f[1][7] = 0;
565                 if (!r_bloom_texture_screen)
566                         r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
567                 if (!r_bloom_texture_bloom)
568                         r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
569                 memset(&m, 0, sizeof(m));
570                 m.pointer_vertex = varray_vertex3f;
571                 m.pointer_texcoord[0] = varray_texcoord2f[0];
572                 m.tex[0] = R_GetTexture(r_bloom_texture_screen);
573                 R_Mesh_State(&m);
574                 // copy view to a texture
575                 GL_ActiveTexture(0);
576                 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
577                 c_bloomcopies++;
578                 c_bloomcopypixels += r_view_width * r_view_height;
579                 // now scale it down to the bloom size and raise to a power of itself
580                 qglViewport(r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
581                 // TODO: optimize with multitexture or GLSL
582                 GL_BlendFunc(GL_ONE, GL_ZERO);
583                 GL_Color(1, 1, 1, 1);
584                 R_Mesh_Draw(4, 2, polygonelements);
585                 c_bloomdraws++;
586                 c_bloomdrawpixels += bloomwidth * bloomheight;
587                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
588                 for (x = 1;x < r_bloom_power.integer;x++)
589                 {
590                         R_Mesh_Draw(4, 2, polygonelements);
591                         c_bloomdraws++;
592                         c_bloomdrawpixels += bloomwidth * bloomheight;
593                 }
594                 // copy the bloom view to a texture
595                 memset(&m, 0, sizeof(m));
596                 m.pointer_vertex = varray_vertex3f;
597                 m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
598                 m.pointer_texcoord[0] = varray_texcoord2f[2];
599                 R_Mesh_State(&m);
600                 GL_ActiveTexture(0);
601                 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
602                 c_bloomcopies++;
603                 c_bloomcopypixels += bloomwidth * bloomheight;
604                 // blend on at multiple offsets vertically
605                 // TODO: do offset blends using GLSL
606                 range = r_bloom_blur.integer * bloomwidth / 320;
607                 GL_BlendFunc(GL_ONE, GL_ZERO);
608                 for (x = -range;x <= range;x++)
609                 {
610                         xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
611                         yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
612                         varray_texcoord2f[2][0] = xoffset+0;
613                         varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
614                         varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
615                         varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
616                         varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
617                         varray_texcoord2f[2][5] = yoffset+0;
618                         varray_texcoord2f[2][6] = xoffset+0;
619                         varray_texcoord2f[2][7] = yoffset+0;
620                         r = r_bloom_intensity.value/(range*2+1)*(1 - fabs(x*x)/(float)(range*range));
621                         if (r < 0.01f)
622                                 continue;
623                         GL_Color(r, r, r, 1);
624                         R_Mesh_Draw(4, 2, polygonelements);
625                         c_bloomdraws++;
626                         c_bloomdrawpixels += bloomwidth * bloomheight;
627                         GL_BlendFunc(GL_ONE, GL_ONE);
628                 }
629                 // copy the blurred bloom view to a texture
630                 GL_ActiveTexture(0);
631                 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
632                 c_bloomcopies++;
633                 c_bloomcopypixels += bloomwidth * bloomheight;
634                 // blend on at multiple offsets horizontally
635                 // TODO: do offset blends using GLSL
636                 range = r_bloom_blur.integer * bloomwidth / 320;
637                 GL_BlendFunc(GL_ONE, GL_ZERO);
638                 for (x = -range;x <= range;x++)
639                 {
640                         xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
641                         yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
642                         varray_texcoord2f[2][0] = xoffset+0;
643                         varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
644                         varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
645                         varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
646                         varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
647                         varray_texcoord2f[2][5] = yoffset+0;
648                         varray_texcoord2f[2][6] = xoffset+0;
649                         varray_texcoord2f[2][7] = yoffset+0;
650                         r = r_bloom_intensity.value/(range*2+1)*(1 - fabs(x*x)/(float)(range*range));
651                         if (r < 0.01f)
652                                 continue;
653                         GL_Color(r, r, r, 1);
654                         R_Mesh_Draw(4, 2, polygonelements);
655                         c_bloomdraws++;
656                         c_bloomdrawpixels += bloomwidth * bloomheight;
657                         GL_BlendFunc(GL_ONE, GL_ONE);
658                 }
659                 // copy the blurred bloom view to a texture
660                 GL_ActiveTexture(0);
661                 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
662                 c_bloomcopies++;
663                 c_bloomcopypixels += bloomwidth * bloomheight;
664                 // go back to full view area
665                 qglViewport(r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
666                 // put the original view back in place
667                 memset(&m, 0, sizeof(m));
668                 m.pointer_vertex = varray_vertex3f;
669                 m.tex[0] = R_GetTexture(r_bloom_texture_screen);
670                 m.pointer_texcoord[0] = varray_texcoord2f[0];
671 #if 0
672                 dobloomblend = false;
673 #else
674                 // do both in one pass if possible
675                 if (r_textureunits.integer >= 2 && gl_combine.integer)
676                 {
677                         dobloomblend = false;
678                         m.texcombinergb[1] = GL_ADD;
679                         m.tex[1] = R_GetTexture(r_bloom_texture_bloom);
680                         m.pointer_texcoord[1] = varray_texcoord2f[1];
681                 }
682                 else
683                         dobloomblend = true;
684 #endif
685                 R_Mesh_State(&m);
686                 GL_BlendFunc(GL_ONE, GL_ZERO);
687                 GL_Color(1,1,1,1);
688                 R_Mesh_Draw(4, 2, polygonelements);
689                 c_bloomdraws++;
690                 c_bloomdrawpixels += r_view_width * r_view_height;
691                 // now blend on the bloom texture if multipass
692                 if (dobloomblend)
693                 {
694                         memset(&m, 0, sizeof(m));
695                         m.pointer_vertex = varray_vertex3f;
696                         m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
697                         m.pointer_texcoord[0] = varray_texcoord2f[1];
698                         R_Mesh_State(&m);
699                         GL_BlendFunc(GL_ONE, GL_ONE);
700                         GL_Color(1,1,1,1);
701                         R_Mesh_Draw(4, 2, polygonelements);
702                         c_bloomdraws++;
703                         c_bloomdrawpixels += r_view_width * r_view_height;
704                 }
705         }
706         if (r_refdef.viewblend[3] >= 0.01f)
707         {
708                 // apply a color tint to the whole view
709                 memset(&m, 0, sizeof(m));
710                 m.pointer_vertex = varray_vertex3f;
711                 R_Mesh_State(&m);
712                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
713                 GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
714                 R_Mesh_Draw(4, 2, polygonelements);
715         }
716 }
717
718 void R_RenderScene(void);
719
720 /*
721 ================
722 R_RenderView
723 ================
724 */
725 void R_RenderView(void)
726 {
727         if (!r_refdef.entities/* || !r_refdef.worldmodel*/)
728                 return; //Host_Error ("R_RenderView: NULL worldmodel");
729
730         r_view_width = bound(0, r_refdef.width, vid.realwidth);
731         r_view_height = bound(0, r_refdef.height, vid.realheight);
732         r_view_depth = 1;
733         r_view_x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width);
734         r_view_y = bound(0, r_refdef.y, vid.realheight - r_refdef.height);
735         r_view_z = 0;
736         r_view_fov_x = bound(1, r_refdef.fov_x, 170);
737         r_view_fov_y = bound(1, r_refdef.fov_y, 170);
738         r_view_matrix = r_refdef.viewentitymatrix;
739         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
740         r_rtworld = r_shadow_realtime_world.integer;
741         r_rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil;
742         r_rtdlight = r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer;
743         r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil;
744         r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
745
746         // GL is weird because it's bottom to top, r_view_y is top to bottom
747         qglViewport(r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
748         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
749         GL_ScissorTest(true);
750         GL_DepthMask(true);
751         R_ClearScreen();
752         R_Textures_Frame();
753         R_UpdateFog();
754         R_UpdateLights();
755         R_TimeReport("setup");
756
757         qglDepthFunc(GL_LEQUAL);
758         qglPolygonOffset(0, 0);
759         qglEnable(GL_POLYGON_OFFSET_FILL);
760
761         R_RenderScene();
762
763         qglPolygonOffset(0, 0);
764         qglDisable(GL_POLYGON_OFFSET_FILL);
765
766         R_BlendView();
767         R_TimeReport("blendview");
768         
769         GL_Scissor(0, 0, vid.realwidth, vid.realheight);
770         GL_ScissorTest(false);
771 }
772
773 extern void R_DrawLightningBeams (void);
774 void R_RenderScene(void)
775 {
776         // don't let sound skip if going slow
777         if (r_refdef.extraupdate)
778                 S_ExtraUpdate ();
779
780         r_framecount++;
781
782         R_MeshQueue_BeginScene();
783
784         GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
785
786         R_SetFrustum();
787
788         r_farclip = R_FarClip(r_vieworigin, r_viewforward, 768.0f) + 256.0f;
789         if (r_rtworldshadows || r_rtdlightshadows)
790                 GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view_fov_x, r_view_fov_y, 1.0f);
791         else
792                 GL_SetupView_Mode_Perspective(r_view_fov_x, r_view_fov_y, 1.0f, r_farclip);
793
794         GL_SetupView_Orientation_FromEntity(&r_view_matrix);
795
796         R_SkyStartFrame();
797
798         if (r_refdef.worldmodel && r_refdef.worldmodel->brush.FatPVS)
799                 r_refdef.worldmodel->brush.FatPVS(r_refdef.worldmodel, r_vieworigin, 2, r_pvsbits, sizeof(r_pvsbits));
800         R_WorldVisibility();
801         R_TimeReport("worldvis");
802
803         R_MarkEntities();
804         R_TimeReport("markentity");
805
806         R_Shadow_UpdateWorldLightSelection();
807
808         // don't let sound skip if going slow
809         if (r_refdef.extraupdate)
810                 S_ExtraUpdate ();
811
812         GL_ShowTrisColor(0.025, 0.025, 0, 1);
813         if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
814         {
815                 r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
816                 R_TimeReport("worldsky");
817         }
818
819         if (R_DrawBrushModelsSky())
820                 R_TimeReport("bmodelsky");
821
822         GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
823         if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
824         {
825                 r_refdef.worldmodel->Draw(r_refdef.worldentity);
826                 R_TimeReport("world");
827         }
828
829         // don't let sound skip if going slow
830         if (r_refdef.extraupdate)
831                 S_ExtraUpdate ();
832
833         GL_ShowTrisColor(0, 0.015, 0, 1);
834
835         R_DrawModels();
836         R_TimeReport("models");
837
838         // don't let sound skip if going slow
839         if (r_refdef.extraupdate)
840                 S_ExtraUpdate ();
841
842         GL_ShowTrisColor(0, 0, 0.033, 1);
843         R_ShadowVolumeLighting(false);
844         R_TimeReport("rtlights");
845
846         // don't let sound skip if going slow
847         if (r_refdef.extraupdate)
848                 S_ExtraUpdate ();
849
850         GL_ShowTrisColor(0.1, 0, 0, 1);
851
852         R_DrawLightningBeams();
853         R_TimeReport("lightning");
854
855         R_DrawParticles();
856         R_TimeReport("particles");
857
858         R_DrawExplosions();
859         R_TimeReport("explosions");
860
861         R_MeshQueue_RenderTransparent();
862         R_TimeReport("drawtrans");
863
864         R_DrawCoronas();
865         R_TimeReport("coronas");
866
867         R_DrawWorldCrosshair();
868         R_TimeReport("crosshair");
869
870         R_MeshQueue_Render();
871         R_MeshQueue_EndScene();
872
873         if (r_shadow_visiblevolumes.integer && !r_showtrispass)
874         {
875                 R_ShadowVolumeLighting(true);
876                 R_TimeReport("shadowvolume");
877         }
878
879         GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
880
881         // don't let sound skip if going slow
882         if (r_refdef.extraupdate)
883                 S_ExtraUpdate ();
884 }
885
886 /*
887 void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, float ca)
888 {
889         int i;
890         float *v, *c, f1, f2, diff[3], vertex3f[8*3], color4f[8*4];
891         rmeshstate_t m;
892         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
893         GL_DepthMask(false);
894         GL_DepthTest(true);
895         R_Mesh_Matrix(&r_identitymatrix);
896
897         vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2];
898         vertex3f[ 3] = maxs[0];vertex3f[ 4] = mins[1];vertex3f[ 5] = mins[2];
899         vertex3f[ 6] = mins[0];vertex3f[ 7] = maxs[1];vertex3f[ 8] = mins[2];
900         vertex3f[ 9] = maxs[0];vertex3f[10] = maxs[1];vertex3f[11] = mins[2];
901         vertex3f[12] = mins[0];vertex3f[13] = mins[1];vertex3f[14] = maxs[2];
902         vertex3f[15] = maxs[0];vertex3f[16] = mins[1];vertex3f[17] = maxs[2];
903         vertex3f[18] = mins[0];vertex3f[19] = maxs[1];vertex3f[20] = maxs[2];
904         vertex3f[21] = maxs[0];vertex3f[22] = maxs[1];vertex3f[23] = maxs[2];
905         R_FillColors(color, 8, cr, cg, cb, ca);
906         if (fogenabled)
907         {
908                 for (i = 0, v = vertex, c = color;i < 8;i++, v += 4, c += 4)
909                 {
910                         VectorSubtract(v, r_vieworigin, diff);
911                         f2 = exp(fogdensity/DotProduct(diff, diff));
912                         f1 = 1 - f2;
913                         c[0] = c[0] * f1 + fogcolor[0] * f2;
914                         c[1] = c[1] * f1 + fogcolor[1] * f2;
915                         c[2] = c[2] * f1 + fogcolor[2] * f2;
916                 }
917         }
918         memset(&m, 0, sizeof(m));
919         m.pointer_vertex = vertex3f;
920         m.pointer_color = color;
921         R_Mesh_State(&m);
922         R_Mesh_Draw(8, 12);
923 }
924 */
925
926 int nomodelelements[24] =
927 {
928         5, 2, 0,
929         5, 1, 2,
930         5, 0, 3,
931         5, 3, 1,
932         0, 2, 4,
933         2, 1, 4,
934         3, 0, 4,
935         1, 3, 4
936 };
937
938 float nomodelvertex3f[6*3] =
939 {
940         -16,   0,   0,
941          16,   0,   0,
942           0, -16,   0,
943           0,  16,   0,
944           0,   0, -16,
945           0,   0,  16
946 };
947
948 float nomodelcolor4f[6*4] =
949 {
950         0.0f, 0.0f, 0.5f, 1.0f,
951         0.0f, 0.0f, 0.5f, 1.0f,
952         0.0f, 0.5f, 0.0f, 1.0f,
953         0.0f, 0.5f, 0.0f, 1.0f,
954         0.5f, 0.0f, 0.0f, 1.0f,
955         0.5f, 0.0f, 0.0f, 1.0f
956 };
957
958 void R_DrawNoModelCallback(const void *calldata1, int calldata2)
959 {
960         const entity_render_t *ent = calldata1;
961         int i;
962         float f1, f2, *c, diff[3];
963         float color4f[6*4];
964         rmeshstate_t m;
965         R_Mesh_Matrix(&ent->matrix);
966
967         memset(&m, 0, sizeof(m));
968         m.pointer_vertex = nomodelvertex3f;
969
970         if (ent->flags & EF_ADDITIVE)
971         {
972                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
973                 GL_DepthMask(false);
974         }
975         else if (ent->alpha < 1)
976         {
977                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
978                 GL_DepthMask(false);
979         }
980         else
981         {
982                 GL_BlendFunc(GL_ONE, GL_ZERO);
983                 GL_DepthMask(true);
984         }
985         GL_DepthTest(!(ent->effects & EF_NODEPTHTEST));
986         if (fogenabled)
987         {
988                 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
989                 m.pointer_color = color4f;
990                 VectorSubtract(ent->origin, r_vieworigin, diff);
991                 f2 = exp(fogdensity/DotProduct(diff, diff));
992                 f1 = 1 - f2;
993                 for (i = 0, c = color4f;i < 6;i++, c += 4)
994                 {
995                         c[0] = (c[0] * f1 + fogcolor[0] * f2);
996                         c[1] = (c[1] * f1 + fogcolor[1] * f2);
997                         c[2] = (c[2] * f1 + fogcolor[2] * f2);
998                         c[3] *= ent->alpha;
999                 }
1000         }
1001         else if (ent->alpha != 1)
1002         {
1003                 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
1004                 m.pointer_color = color4f;
1005                 for (i = 0, c = color4f;i < 6;i++, c += 4)
1006                         c[3] *= ent->alpha;
1007         }
1008         else
1009                 m.pointer_color = nomodelcolor4f;
1010         R_Mesh_State(&m);
1011         R_Mesh_Draw(6, 8, nomodelelements);
1012 }
1013
1014 void R_DrawNoModel(entity_render_t *ent)
1015 {
1016         //if ((ent->effects & EF_ADDITIVE) || (ent->alpha < 1))
1017                 R_MeshQueue_AddTransparent(ent->effects & EF_NODEPTHTEST ? r_vieworigin : ent->origin, R_DrawNoModelCallback, ent, 0);
1018         //else
1019         //      R_DrawNoModelCallback(ent, 0);
1020 }
1021
1022 void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width)
1023 {
1024         vec3_t right1, right2, diff, normal;
1025
1026         VectorSubtract (org2, org1, normal);
1027
1028         // calculate 'right' vector for start
1029         VectorSubtract (r_vieworigin, org1, diff);
1030         CrossProduct (normal, diff, right1);
1031         VectorNormalize (right1);
1032
1033         // calculate 'right' vector for end
1034         VectorSubtract (r_vieworigin, org2, diff);
1035         CrossProduct (normal, diff, right2);
1036         VectorNormalize (right2);
1037
1038         vert[ 0] = org1[0] + width * right1[0];
1039         vert[ 1] = org1[1] + width * right1[1];
1040         vert[ 2] = org1[2] + width * right1[2];
1041         vert[ 3] = org1[0] - width * right1[0];
1042         vert[ 4] = org1[1] - width * right1[1];
1043         vert[ 5] = org1[2] - width * right1[2];
1044         vert[ 6] = org2[0] - width * right2[0];
1045         vert[ 7] = org2[1] - width * right2[1];
1046         vert[ 8] = org2[2] - width * right2[2];
1047         vert[ 9] = org2[0] + width * right2[0];
1048         vert[10] = org2[1] + width * right2[1];
1049         vert[11] = org2[2] + width * right2[2];
1050 }
1051
1052 float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
1053
1054 void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, int depthdisable, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2, float cr, float cg, float cb, float ca)
1055 {
1056         float diff[3];
1057         rmeshstate_t m;
1058
1059         if (fogenabled)
1060         {
1061                 VectorSubtract(origin, r_vieworigin, diff);
1062                 ca *= 1 - exp(fogdensity/DotProduct(diff,diff));
1063         }
1064
1065         R_Mesh_Matrix(&r_identitymatrix);
1066         GL_BlendFunc(blendfunc1, blendfunc2);
1067         GL_DepthMask(false);
1068         GL_DepthTest(!depthdisable);
1069
1070         varray_vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
1071         varray_vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
1072         varray_vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
1073         varray_vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
1074         varray_vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
1075         varray_vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
1076         varray_vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
1077         varray_vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
1078         varray_vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
1079         varray_vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
1080         varray_vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
1081         varray_vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
1082
1083         memset(&m, 0, sizeof(m));
1084         m.tex[0] = R_GetTexture(texture);
1085         m.pointer_texcoord[0] = spritetexcoord2f;
1086         m.pointer_vertex = varray_vertex3f;
1087         R_Mesh_State(&m);
1088         GL_Color(cr, cg, cb, ca);
1089         R_Mesh_Draw(4, 2, polygonelements);
1090 }
1091