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