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