2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
25 // used for dlight push checking and other things
30 matrix4x4_t r_identitymatrix;
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;
34 // true during envmap command capture
37 // maximum visible distance (recalculated from world box each frame)
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
44 qboolean r_rtworldshadows;
46 qboolean r_rtdlightshadows;
49 // forces all rendering to draw triangle outlines
66 matrix4x4_t r_view_matrix;
73 // 8.8 fraction of base light value
74 unsigned short d_lightstylevalue[256];
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"};
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"};
94 cvar_t r_textureunits = {0, "r_textureunits", "32"};
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"};
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"};
107 cvar_t developer_texturelogging = {0, "developer_texturelogging", "1"};
109 rtexturepool_t *r_main_texturepool;
110 rtexture_t *r_bloom_texture_screen;
111 rtexture_t *r_bloom_texture_bloom;
112 rtexture_t *r_texture_blanknormalmap;
113 rtexture_t *r_texture_white;
114 rtexture_t *r_texture_black;
115 rtexture_t *r_texture_notexture;
116 rtexture_t *r_texture_whitecube;
117 rtexture_t *r_texture_normalizationcube;
118 rtexture_t *r_texture_detailtextures[NUM_DETAILTEXTURES];
119 rtexture_t *r_texture_distorttexture[64];
121 void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
124 for (i = 0;i < verts;i++)
135 void R_FillColors(float *out, int verts, float r, float g, float b, float a)
138 for (i = 0;i < verts;i++)
150 float fog_density, fog_red, fog_green, fog_blue;
152 qboolean oldgl_fogenable;
153 void R_UpdateFog(void)
155 if (gamemode == GAME_NEHAHRA)
157 if (gl_fogenable.integer)
159 oldgl_fogenable = true;
160 fog_density = gl_fogdensity.value;
161 fog_red = gl_fogred.value;
162 fog_green = gl_foggreen.value;
163 fog_blue = gl_fogblue.value;
165 else if (oldgl_fogenable)
167 oldgl_fogenable = false;
176 fogcolor[0] = fog_red = bound(0.0f, fog_red , 1.0f);
177 fogcolor[1] = fog_green = bound(0.0f, fog_green, 1.0f);
178 fogcolor[2] = fog_blue = bound(0.0f, fog_blue , 1.0f);
183 fogdensity = -4000.0f / (fog_density * fog_density);
184 // fog color was already set
190 // FIXME: move this to client?
193 if (gamemode == GAME_NEHAHRA)
195 Cvar_Set("gl_fogenable", "0");
196 Cvar_Set("gl_fogdensity", "0.2");
197 Cvar_Set("gl_fogred", "0.3");
198 Cvar_Set("gl_foggreen", "0.3");
199 Cvar_Set("gl_fogblue", "0.3");
201 fog_density = fog_red = fog_green = fog_blue = 0.0f;
204 // FIXME: move this to client?
205 void FOG_registercvars(void)
207 if (gamemode == GAME_NEHAHRA)
209 Cvar_RegisterVariable (&gl_fogenable);
210 Cvar_RegisterVariable (&gl_fogdensity);
211 Cvar_RegisterVariable (&gl_fogred);
212 Cvar_RegisterVariable (&gl_foggreen);
213 Cvar_RegisterVariable (&gl_fogblue);
214 Cvar_RegisterVariable (&gl_fogstart);
215 Cvar_RegisterVariable (&gl_fogend);
219 static void R_BuildDetailTextures (void)
222 float vc[3], vx[3], vy[3], vn[3], lightdir[3];
223 #define DETAILRESOLUTION 256
224 qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
228 VectorNormalize(lightdir);
229 for (i = 0;i < NUM_DETAILTEXTURES;i++)
231 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
232 for (y = 0;y < DETAILRESOLUTION;y++)
234 for (x = 0;x < DETAILRESOLUTION;x++)
238 vc[2] = noise[y][x] * (1.0f / 32.0f);
241 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
244 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
245 VectorSubtract(vx, vc, vx);
246 VectorSubtract(vy, vc, vy);
247 CrossProduct(vx, vy, vn);
249 light = 128 - DotProduct(vn, lightdir) * 128;
250 light = bound(0, light, 255);
251 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
255 r_texture_detailtextures[i] = R_LoadTexture2D(r_main_texturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE, NULL);
259 static qbyte R_MorphDistortTexture (double y0, double y1, double y2, double y3, double morph)
261 int m = (int)(((y1 + y3 - (y0 + y2)) * morph * morph * morph) +
262 ((2 * (y0 - y1) + y2 - y3) * morph * morph) +
263 ((y2 - y0) * morph) +
265 return (qbyte)bound(0, m, 255);
268 static void R_BuildDistortTexture (void)
271 #define DISTORTRESOLUTION 32
272 qbyte data[5][DISTORTRESOLUTION][DISTORTRESOLUTION][2];
276 for (y=0; y<DISTORTRESOLUTION; y++)
278 for (x=0; x<DISTORTRESOLUTION; x++)
280 data[i][y][x][0] = rand () & 255;
281 data[i][y][x][1] = rand () & 255;
290 r_texture_distorttexture[i*16+j] = NULL;
291 if (gl_textureshader)
293 for (y=0; y<DISTORTRESOLUTION; y++)
295 for (x=0; x<DISTORTRESOLUTION; x++)
297 data[4][y][x][0] = R_MorphDistortTexture (data[(i-1)&3][y][x][0], data[i][y][x][0], data[(i+1)&3][y][x][0], data[(i+2)&3][y][x][0], 0.0625*j);
298 data[4][y][x][1] = R_MorphDistortTexture (data[(i-1)&3][y][x][1], data[i][y][x][1], data[(i+1)&3][y][x][1], data[(i+2)&3][y][x][1], 0.0625*j);
301 r_texture_distorttexture[i*16+j] = R_LoadTexture2D(r_main_texturepool, va("distorttexture%i", i*16+j), DISTORTRESOLUTION, DISTORTRESOLUTION, &data[4][0][0][0], TEXTYPE_DSDT, TEXF_PRECACHE, NULL);
307 static void R_BuildBlankTextures(void)
310 data[0] = 128; // normal X
311 data[1] = 128; // normal Y
312 data[2] = 255; // normal Z
313 data[3] = 128; // height
314 r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
319 r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
324 r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
327 static void R_BuildNoTexture(void)
330 qbyte pix[16][16][4];
331 // this makes a light grey/dark grey checkerboard texture
332 for (y = 0;y < 16;y++)
334 for (x = 0;x < 16;x++)
336 if ((y < 8) ^ (x < 8))
352 r_texture_notexture = R_LoadTexture2D(r_main_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP, NULL);
355 static void R_BuildWhiteCube(void)
358 data[ 0] = 255;data[ 1] = 255;data[ 2] = 255;data[ 3] = 255;
359 data[ 4] = 255;data[ 5] = 255;data[ 6] = 255;data[ 7] = 255;
360 data[ 8] = 255;data[ 9] = 255;data[10] = 255;data[11] = 255;
361 data[12] = 255;data[13] = 255;data[14] = 255;data[15] = 255;
362 data[16] = 255;data[17] = 255;data[18] = 255;data[19] = 255;
363 data[20] = 255;data[21] = 255;data[22] = 255;data[23] = 255;
364 r_texture_whitecube = R_LoadTextureCubeMap(r_main_texturepool, "whitecube", 1, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
367 static void R_BuildNormalizationCube(void)
371 vec_t s, t, intensity;
373 qbyte data[6][NORMSIZE][NORMSIZE][4];
374 for (side = 0;side < 6;side++)
376 for (y = 0;y < NORMSIZE;y++)
378 for (x = 0;x < NORMSIZE;x++)
380 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
381 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
415 intensity = 127.0f / sqrt(DotProduct(v, v));
416 data[side][y][x][0] = 128.0f + intensity * v[0];
417 data[side][y][x][1] = 128.0f + intensity * v[1];
418 data[side][y][x][2] = 128.0f + intensity * v[2];
419 data[side][y][x][3] = 255;
423 r_texture_normalizationcube = R_LoadTextureCubeMap(r_main_texturepool, "normalcube", NORMSIZE, &data[0][0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
426 void gl_main_start(void)
428 r_main_texturepool = R_AllocTexturePool();
429 r_bloom_texture_screen = NULL;
430 r_bloom_texture_bloom = NULL;
431 R_BuildBlankTextures();
433 R_BuildDetailTextures();
434 R_BuildDistortTexture();
435 if (gl_texturecubemap)
438 R_BuildNormalizationCube();
442 void gl_main_shutdown(void)
444 R_FreeTexturePool(&r_main_texturepool);
445 r_bloom_texture_screen = NULL;
446 r_bloom_texture_bloom = NULL;
447 r_texture_blanknormalmap = NULL;
448 r_texture_white = NULL;
449 r_texture_black = NULL;
450 r_texture_whitecube = NULL;
451 r_texture_normalizationcube = NULL;
454 extern void CL_ParseEntityLump(char *entitystring);
455 void gl_main_newmap(void)
457 // FIXME: move this code to client
459 char *entities, entname[MAX_QPATH];
463 strlcpy(entname, cl.worldmodel->name, sizeof(entname));
464 l = strlen(entname) - 4;
465 if (l >= 0 && !strcmp(entname + l, ".bsp"))
467 strcpy(entname + l, ".ent");
468 if ((entities = FS_LoadFile(entname, tempmempool, true)))
470 CL_ParseEntityLump(entities);
475 if (cl.worldmodel->brush.entities)
476 CL_ParseEntityLump(cl.worldmodel->brush.entities);
480 void GL_Main_Init(void)
482 Matrix4x4_CreateIdentity(&r_identitymatrix);
483 // FIXME: move this to client?
485 Cvar_RegisterVariable(&r_showtris);
486 Cvar_RegisterVariable(&r_drawentities);
487 Cvar_RegisterVariable(&r_drawviewmodel);
488 Cvar_RegisterVariable(&r_speeds);
489 Cvar_RegisterVariable(&r_fullbrights);
490 Cvar_RegisterVariable(&r_wateralpha);
491 Cvar_RegisterVariable(&r_dynamic);
492 Cvar_RegisterVariable(&r_fullbright);
493 Cvar_RegisterVariable(&r_textureunits);
494 Cvar_RegisterVariable(&r_lerpsprites);
495 Cvar_RegisterVariable(&r_lerpmodels);
496 Cvar_RegisterVariable(&r_waterscroll);
497 Cvar_RegisterVariable(&r_watershader);
498 Cvar_RegisterVariable(&r_drawcollisionbrushes);
499 Cvar_RegisterVariable(&r_bloom);
500 Cvar_RegisterVariable(&r_bloom_intensity);
501 Cvar_RegisterVariable(&r_bloom_blur);
502 Cvar_RegisterVariable(&r_bloom_resolution);
503 Cvar_RegisterVariable(&r_bloom_power);
504 Cvar_RegisterVariable(&developer_texturelogging);
505 if (gamemode == GAME_NEHAHRA || gamemode == GAME_NEXUIZ || gamemode == GAME_TENEBRAE)
506 Cvar_SetValue("r_fullbrights", 0);
507 R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
510 static vec3_t r_farclip_origin;
511 static vec3_t r_farclip_direction;
512 static vec_t r_farclip_directiondist;
513 static vec_t r_farclip_meshfarclip;
514 static int r_farclip_directionbit0;
515 static int r_farclip_directionbit1;
516 static int r_farclip_directionbit2;
518 // enlarge farclip to accomodate box
519 static void R_FarClip_Box(vec3_t mins, vec3_t maxs)
522 d = (r_farclip_directionbit0 ? mins[0] : maxs[0]) * r_farclip_direction[0]
523 + (r_farclip_directionbit1 ? mins[1] : maxs[1]) * r_farclip_direction[1]
524 + (r_farclip_directionbit2 ? mins[2] : maxs[2]) * r_farclip_direction[2];
525 if (r_farclip_meshfarclip < d)
526 r_farclip_meshfarclip = d;
529 // return farclip value
530 static float R_FarClip(vec3_t origin, vec3_t direction, vec_t startfarclip)
534 VectorCopy(origin, r_farclip_origin);
535 VectorCopy(direction, r_farclip_direction);
536 r_farclip_directiondist = DotProduct(r_farclip_origin, r_farclip_direction);
537 r_farclip_directionbit0 = r_farclip_direction[0] < 0;
538 r_farclip_directionbit1 = r_farclip_direction[1] < 0;
539 r_farclip_directionbit2 = r_farclip_direction[2] < 0;
540 r_farclip_meshfarclip = r_farclip_directiondist + startfarclip;
542 if (r_refdef.worldmodel)
543 R_FarClip_Box(r_refdef.worldmodel->normalmins, r_refdef.worldmodel->normalmaxs);
544 for (i = 0;i < r_refdef.numentities;i++)
545 R_FarClip_Box(r_refdef.entities[i]->mins, r_refdef.entities[i]->maxs);
547 return r_farclip_meshfarclip - r_farclip_directiondist;
550 extern void R_Textures_Init(void);
551 extern void GL_Draw_Init(void);
552 extern void GL_Main_Init(void);
553 extern void R_Shadow_Init(void);
554 extern void R_Sky_Init(void);
555 extern void GL_Surf_Init(void);
556 extern void R_Crosshairs_Init(void);
557 extern void R_Light_Init(void);
558 extern void R_Particles_Init(void);
559 extern void R_Explosion_Init(void);
560 extern void gl_backend_init(void);
561 extern void Sbar_Init(void);
562 extern void R_LightningBeams_Init(void);
563 extern void Mod_RenderInit(void);
565 void Render_Init(void)
582 R_LightningBeams_Init();
590 extern char *ENGINE_EXTENSIONS;
593 VID_CheckExtensions();
595 // LordHavoc: report supported extensions
596 Con_DPrintf("\nengine extensions: %s\n", ENGINE_EXTENSIONS);
598 // clear to black (loading plaque will be seen over this)
599 qglClearColor(0,0,0,1);
600 qglClear(GL_COLOR_BUFFER_BIT);
603 int R_CullBox(const vec3_t mins, const vec3_t maxs)
607 for (i = 0;i < 4;i++)
614 if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
618 if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
622 if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
626 if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
630 if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
634 if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
638 if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
642 if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
650 //==================================================================================
652 static void R_MarkEntities (void)
655 entity_render_t *ent;
657 if (!r_drawentities.integer)
660 r_refdef.worldentity->visframe = r_framecount;
661 renderimask = envmap ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) : (chase_active.integer ? 0 : RENDER_EXTERIORMODEL);
662 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs)
664 // worldmodel can check visibility
665 for (i = 0;i < r_refdef.numentities;i++)
667 ent = r_refdef.entities[i];
668 Mod_CheckLoaded(ent->model);
669 // some of the renderer still relies on origin...
670 Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
671 // some of the renderer still relies on scale...
672 ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
673 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)))
675 R_UpdateEntLights(ent);
676 ent->visframe = r_framecount;
682 // no worldmodel or it can't check visibility
683 for (i = 0;i < r_refdef.numentities;i++)
685 ent = r_refdef.entities[i];
686 Mod_CheckLoaded(ent->model);
687 // some of the renderer still relies on origin...
688 Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
689 // some of the renderer still relies on scale...
690 ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
691 if (!(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && (ent->effects & EF_NODEPTHTEST))
693 R_UpdateEntLights(ent);
694 ent->visframe = r_framecount;
700 // only used if skyrendermasked, and normally returns false
701 int R_DrawBrushModelsSky (void)
704 entity_render_t *ent;
706 if (!r_drawentities.integer)
710 for (i = 0;i < r_refdef.numentities;i++)
712 ent = r_refdef.entities[i];
713 if (ent->visframe == r_framecount && ent->model && ent->model->DrawSky)
715 ent->model->DrawSky(ent);
722 void R_DrawNoModel(entity_render_t *ent);
723 void R_DrawModels(void)
726 entity_render_t *ent;
728 if (!r_drawentities.integer)
731 for (i = 0;i < r_refdef.numentities;i++)
733 ent = r_refdef.entities[i];
734 if (ent->visframe == r_framecount)
736 if (ent->model && ent->model->Draw != NULL)
737 ent->model->Draw(ent);
744 static void R_SetFrustum(void)
746 // break apart the view matrix into vectors for various purposes
747 Matrix4x4_ToVectors(&r_view_matrix, r_viewforward, r_viewleft, r_viewup, r_vieworigin);
748 VectorNegate(r_viewleft, r_viewright);
750 // LordHavoc: note to all quake engine coders, the special case for 90
751 // degrees assumed a square view (wrong), so I removed it, Quake2 has it
754 // rotate R_VIEWFORWARD right by FOV_X/2 degrees
755 RotatePointAroundVector( frustum[0].normal, r_viewup, r_viewforward, -(90 - r_view_fov_x / 2));
756 frustum[0].dist = DotProduct (r_vieworigin, frustum[0].normal);
757 PlaneClassify(&frustum[0]);
759 // rotate R_VIEWFORWARD left by FOV_X/2 degrees
760 RotatePointAroundVector( frustum[1].normal, r_viewup, r_viewforward, (90 - r_view_fov_x / 2));
761 frustum[1].dist = DotProduct (r_vieworigin, frustum[1].normal);
762 PlaneClassify(&frustum[1]);
764 // rotate R_VIEWFORWARD up by FOV_X/2 degrees
765 RotatePointAroundVector( frustum[2].normal, r_viewleft, r_viewforward, -(90 - r_view_fov_y / 2));
766 frustum[2].dist = DotProduct (r_vieworigin, frustum[2].normal);
767 PlaneClassify(&frustum[2]);
769 // rotate R_VIEWFORWARD down by FOV_X/2 degrees
770 RotatePointAroundVector( frustum[3].normal, r_viewleft, r_viewforward, (90 - r_view_fov_y / 2));
771 frustum[3].dist = DotProduct (r_vieworigin, frustum[3].normal);
772 PlaneClassify(&frustum[3]);
775 static void R_BlendView(void)
779 if (r_refdef.viewblend[3] < 0.01f && !r_bloom.integer)
782 GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
785 R_Mesh_Matrix(&r_identitymatrix);
786 // vertex coordinates for a quad that covers the screen exactly
787 varray_vertex3f[0] = 0;varray_vertex3f[1] = 0;varray_vertex3f[2] = 0;
788 varray_vertex3f[3] = 1;varray_vertex3f[4] = 0;varray_vertex3f[5] = 0;
789 varray_vertex3f[6] = 1;varray_vertex3f[7] = 1;varray_vertex3f[8] = 0;
790 varray_vertex3f[9] = 0;varray_vertex3f[10] = 1;varray_vertex3f[11] = 0;
791 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)
793 int screenwidth, screenheight, bloomwidth, bloomheight, x, dobloomblend, range;
794 float xoffset, yoffset, r;
796 // set the (poorly named) screenwidth and screenheight variables to
797 // a power of 2 at least as large as the screen, these will define the
798 // size of the texture to allocate
799 for (screenwidth = 1;screenwidth < vid.realwidth;screenwidth *= 2);
800 for (screenheight = 1;screenheight < vid.realheight;screenheight *= 2);
801 // allocate textures as needed
802 if (!r_bloom_texture_screen)
803 r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
804 if (!r_bloom_texture_bloom)
805 r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
806 // set bloomwidth and bloomheight to the bloom resolution that will be
807 // used (often less than the screen resolution for faster rendering)
808 bloomwidth = min(r_view_width, r_bloom_resolution.integer);
809 bloomheight = min(r_view_height, bloomwidth * r_view_height / r_view_width);
810 // set up a texcoord array for the full resolution screen image
811 // (we have to keep this around to copy back during final render)
812 varray_texcoord2f[0][0] = 0;
813 varray_texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
814 varray_texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
815 varray_texcoord2f[0][3] = (float)r_view_height / (float)screenheight;
816 varray_texcoord2f[0][4] = (float)r_view_width / (float)screenwidth;
817 varray_texcoord2f[0][5] = 0;
818 varray_texcoord2f[0][6] = 0;
819 varray_texcoord2f[0][7] = 0;
820 // set up a texcoord array for the reduced resolution bloom image
821 // (which will be additive blended over the screen image)
822 varray_texcoord2f[1][0] = 0;
823 varray_texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
824 varray_texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
825 varray_texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
826 varray_texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
827 varray_texcoord2f[1][5] = 0;
828 varray_texcoord2f[1][6] = 0;
829 varray_texcoord2f[1][7] = 0;
830 memset(&m, 0, sizeof(m));
831 m.pointer_vertex = varray_vertex3f;
832 m.pointer_texcoord[0] = varray_texcoord2f[0];
833 m.tex[0] = R_GetTexture(r_bloom_texture_screen);
835 // copy view into the full resolution screen image texture
837 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
839 c_bloomcopypixels += r_view_width * r_view_height;
840 // now scale it down to the bloom size and raise to a power of itself
841 // to darken it (this leaves the really bright stuff bright, and
842 // everything else becomes very dark)
843 // TODO: optimize with multitexture or GLSL
844 qglViewport(r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
845 GL_BlendFunc(GL_ONE, GL_ZERO);
846 GL_Color(1, 1, 1, 1);
847 R_Mesh_Draw(0, 4, 2, polygonelements);
849 c_bloomdrawpixels += bloomwidth * bloomheight;
850 // render multiple times with a multiply blendfunc to raise to a power
851 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
852 for (x = 1;x < r_bloom_power.integer;x++)
854 R_Mesh_Draw(0, 4, 2, polygonelements);
856 c_bloomdrawpixels += bloomwidth * bloomheight;
858 // we now have a darkened bloom image in the framebuffer, copy it into
859 // the bloom image texture for more processing
860 memset(&m, 0, sizeof(m));
861 m.pointer_vertex = varray_vertex3f;
862 m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
863 m.pointer_texcoord[0] = varray_texcoord2f[2];
866 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
868 c_bloomcopypixels += bloomwidth * bloomheight;
869 // blend on at multiple vertical offsets to achieve a vertical blur
870 // TODO: do offset blends using GLSL
871 range = r_bloom_blur.integer * bloomwidth / 320;
872 GL_BlendFunc(GL_ONE, GL_ZERO);
873 for (x = -range;x <= range;x++)
875 xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
876 yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
877 // compute a texcoord array with the specified x and y offset
878 varray_texcoord2f[2][0] = xoffset+0;
879 varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
880 varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
881 varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
882 varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
883 varray_texcoord2f[2][5] = yoffset+0;
884 varray_texcoord2f[2][6] = xoffset+0;
885 varray_texcoord2f[2][7] = yoffset+0;
886 // this r value looks like a 'dot' particle, fading sharply to
887 // black at the edges
888 // (probably not realistic but looks good enough)
889 r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
892 GL_Color(r, r, r, 1);
893 R_Mesh_Draw(0, 4, 2, polygonelements);
895 c_bloomdrawpixels += bloomwidth * bloomheight;
896 GL_BlendFunc(GL_ONE, GL_ONE);
898 // copy the vertically blurred bloom view to a texture
900 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
902 c_bloomcopypixels += bloomwidth * bloomheight;
903 // blend the vertically blurred image at multiple offsets horizontally
904 // to finish the blur effect
905 // TODO: do offset blends using GLSL
906 range = r_bloom_blur.integer * bloomwidth / 320;
907 GL_BlendFunc(GL_ONE, GL_ZERO);
908 for (x = -range;x <= range;x++)
910 xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
911 yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
912 // compute a texcoord array with the specified x and y offset
913 varray_texcoord2f[2][0] = xoffset+0;
914 varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
915 varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
916 varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
917 varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
918 varray_texcoord2f[2][5] = yoffset+0;
919 varray_texcoord2f[2][6] = xoffset+0;
920 varray_texcoord2f[2][7] = yoffset+0;
921 // this r value looks like a 'dot' particle, fading sharply to
922 // black at the edges
923 // (probably not realistic but looks good enough)
924 r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
927 GL_Color(r, r, r, 1);
928 R_Mesh_Draw(0, 4, 2, polygonelements);
930 c_bloomdrawpixels += bloomwidth * bloomheight;
931 GL_BlendFunc(GL_ONE, GL_ONE);
933 // copy the blurred bloom view to a texture
935 qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.realheight - (r_view_y + bloomheight), bloomwidth, bloomheight);
937 c_bloomcopypixels += bloomwidth * bloomheight;
938 // go back to full view area
939 qglViewport(r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
940 // put the original screen image back in place and blend the bloom
942 memset(&m, 0, sizeof(m));
943 m.pointer_vertex = varray_vertex3f;
944 m.tex[0] = R_GetTexture(r_bloom_texture_screen);
945 m.pointer_texcoord[0] = varray_texcoord2f[0];
947 dobloomblend = false;
949 // do both in one pass if possible
950 if (r_textureunits.integer >= 2 && gl_combine.integer)
952 dobloomblend = false;
953 m.texcombinergb[1] = GL_ADD;
954 m.tex[1] = R_GetTexture(r_bloom_texture_bloom);
955 m.pointer_texcoord[1] = varray_texcoord2f[1];
961 GL_BlendFunc(GL_ONE, GL_ZERO);
963 R_Mesh_Draw(0, 4, 2, polygonelements);
965 c_bloomdrawpixels += r_view_width * r_view_height;
966 // now blend on the bloom texture if multipass
969 memset(&m, 0, sizeof(m));
970 m.pointer_vertex = varray_vertex3f;
971 m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
972 m.pointer_texcoord[0] = varray_texcoord2f[1];
974 GL_BlendFunc(GL_ONE, GL_ONE);
976 R_Mesh_Draw(0, 4, 2, polygonelements);
978 c_bloomdrawpixels += r_view_width * r_view_height;
981 if (r_refdef.viewblend[3] >= 0.01f)
983 // apply a color tint to the whole view
984 memset(&m, 0, sizeof(m));
985 m.pointer_vertex = varray_vertex3f;
987 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
988 GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
989 R_Mesh_Draw(0, 4, 2, polygonelements);
993 void R_RenderScene(void);
1000 void R_RenderView(void)
1002 if (!r_refdef.entities/* || !r_refdef.worldmodel*/)
1003 return; //Host_Error ("R_RenderView: NULL worldmodel");
1005 r_view_width = bound(0, r_refdef.width, vid.realwidth);
1006 r_view_height = bound(0, r_refdef.height, vid.realheight);
1008 r_view_x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width);
1009 r_view_y = bound(0, r_refdef.y, vid.realheight - r_refdef.height);
1011 r_view_fov_x = bound(1, r_refdef.fov_x, 170);
1012 r_view_fov_y = bound(1, r_refdef.fov_y, 170);
1013 r_view_matrix = r_refdef.viewentitymatrix;
1014 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1015 r_rtworld = r_shadow_realtime_world.integer;
1016 r_rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil;
1017 r_rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer;
1018 r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil;
1019 r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
1021 // GL is weird because it's bottom to top, r_view_y is top to bottom
1022 qglViewport(r_view_x, vid.realheight - (r_view_y + r_view_height), r_view_width, r_view_height);
1023 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1024 GL_ScissorTest(true);
1030 R_TimeReport("setup");
1032 qglDepthFunc(GL_LEQUAL);
1033 qglPolygonOffset(0, 0);
1034 qglEnable(GL_POLYGON_OFFSET_FILL);
1038 qglPolygonOffset(0, 0);
1039 qglDisable(GL_POLYGON_OFFSET_FILL);
1042 R_TimeReport("blendview");
1044 GL_Scissor(0, 0, vid.realwidth, vid.realheight);
1045 GL_ScissorTest(false);
1048 extern void R_DrawLightningBeams (void);
1049 void R_RenderScene(void)
1051 // don't let sound skip if going slow
1052 if (r_refdef.extraupdate)
1057 R_MeshQueue_BeginScene();
1059 GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
1063 r_farclip = R_FarClip(r_vieworigin, r_viewforward, 768.0f) + 256.0f;
1064 if (r_rtworldshadows || r_rtdlightshadows)
1065 GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_view_fov_x, r_view_fov_y, 1.0f);
1067 GL_SetupView_Mode_Perspective(r_view_fov_x, r_view_fov_y, 1.0f, r_farclip);
1069 GL_SetupView_Orientation_FromEntity(&r_view_matrix);
1073 R_WorldVisibility();
1074 R_TimeReport("worldvis");
1077 R_TimeReport("markentity");
1079 R_Shadow_UpdateWorldLightSelection();
1081 // don't let sound skip if going slow
1082 if (r_refdef.extraupdate)
1085 GL_ShowTrisColor(0.025, 0.025, 0, 1);
1086 if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
1088 r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
1089 R_TimeReport("worldsky");
1092 if (R_DrawBrushModelsSky())
1093 R_TimeReport("bmodelsky");
1095 GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
1096 if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
1098 r_refdef.worldmodel->Draw(r_refdef.worldentity);
1099 R_TimeReport("world");
1102 // don't let sound skip if going slow
1103 if (r_refdef.extraupdate)
1106 GL_ShowTrisColor(0, 0.015, 0, 1);
1109 R_TimeReport("models");
1111 // don't let sound skip if going slow
1112 if (r_refdef.extraupdate)
1115 GL_ShowTrisColor(0, 0, 0.033, 1);
1116 R_ShadowVolumeLighting(false);
1117 R_TimeReport("rtlights");
1119 // don't let sound skip if going slow
1120 if (r_refdef.extraupdate)
1123 GL_ShowTrisColor(0.1, 0, 0, 1);
1125 R_DrawLightningBeams();
1126 R_TimeReport("lightning");
1129 R_TimeReport("particles");
1132 R_TimeReport("explosions");
1134 R_MeshQueue_RenderTransparent();
1135 R_TimeReport("drawtrans");
1138 R_TimeReport("coronas");
1140 R_DrawWorldCrosshair();
1141 R_TimeReport("crosshair");
1143 R_MeshQueue_Render();
1144 R_MeshQueue_EndScene();
1146 if ((r_shadow_visiblelighting.integer || r_shadow_visiblevolumes.integer) && !r_showtrispass)
1148 R_ShadowVolumeLighting(true);
1149 R_TimeReport("visiblevolume");
1152 GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
1154 // don't let sound skip if going slow
1155 if (r_refdef.extraupdate)
1160 void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, float ca)
1163 float *v, *c, f1, f2, diff[3], vertex3f[8*3], color4f[8*4];
1165 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1166 GL_DepthMask(false);
1168 R_Mesh_Matrix(&r_identitymatrix);
1170 vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2];
1171 vertex3f[ 3] = maxs[0];vertex3f[ 4] = mins[1];vertex3f[ 5] = mins[2];
1172 vertex3f[ 6] = mins[0];vertex3f[ 7] = maxs[1];vertex3f[ 8] = mins[2];
1173 vertex3f[ 9] = maxs[0];vertex3f[10] = maxs[1];vertex3f[11] = mins[2];
1174 vertex3f[12] = mins[0];vertex3f[13] = mins[1];vertex3f[14] = maxs[2];
1175 vertex3f[15] = maxs[0];vertex3f[16] = mins[1];vertex3f[17] = maxs[2];
1176 vertex3f[18] = mins[0];vertex3f[19] = maxs[1];vertex3f[20] = maxs[2];
1177 vertex3f[21] = maxs[0];vertex3f[22] = maxs[1];vertex3f[23] = maxs[2];
1178 R_FillColors(color, 8, cr, cg, cb, ca);
1181 for (i = 0, v = vertex, c = color;i < 8;i++, v += 4, c += 4)
1183 VectorSubtract(v, r_vieworigin, diff);
1184 f2 = exp(fogdensity/DotProduct(diff, diff));
1186 c[0] = c[0] * f1 + fogcolor[0] * f2;
1187 c[1] = c[1] * f1 + fogcolor[1] * f2;
1188 c[2] = c[2] * f1 + fogcolor[2] * f2;
1191 memset(&m, 0, sizeof(m));
1192 m.pointer_vertex = vertex3f;
1193 m.pointer_color = color;
1199 int nomodelelements[24] =
1211 float nomodelvertex3f[6*3] =
1221 float nomodelcolor4f[6*4] =
1223 0.0f, 0.0f, 0.5f, 1.0f,
1224 0.0f, 0.0f, 0.5f, 1.0f,
1225 0.0f, 0.5f, 0.0f, 1.0f,
1226 0.0f, 0.5f, 0.0f, 1.0f,
1227 0.5f, 0.0f, 0.0f, 1.0f,
1228 0.5f, 0.0f, 0.0f, 1.0f
1231 void R_DrawNoModelCallback(const void *calldata1, int calldata2)
1233 const entity_render_t *ent = calldata1;
1235 float f1, f2, *c, diff[3];
1238 R_Mesh_Matrix(&ent->matrix);
1240 memset(&m, 0, sizeof(m));
1241 m.pointer_vertex = nomodelvertex3f;
1243 if (ent->flags & EF_ADDITIVE)
1245 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1246 GL_DepthMask(false);
1248 else if (ent->alpha < 1)
1250 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1251 GL_DepthMask(false);
1255 GL_BlendFunc(GL_ONE, GL_ZERO);
1258 GL_DepthTest(!(ent->effects & EF_NODEPTHTEST));
1261 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
1262 m.pointer_color = color4f;
1263 VectorSubtract(ent->origin, r_vieworigin, diff);
1264 f2 = exp(fogdensity/DotProduct(diff, diff));
1266 for (i = 0, c = color4f;i < 6;i++, c += 4)
1268 c[0] = (c[0] * f1 + fogcolor[0] * f2);
1269 c[1] = (c[1] * f1 + fogcolor[1] * f2);
1270 c[2] = (c[2] * f1 + fogcolor[2] * f2);
1274 else if (ent->alpha != 1)
1276 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
1277 m.pointer_color = color4f;
1278 for (i = 0, c = color4f;i < 6;i++, c += 4)
1282 m.pointer_color = nomodelcolor4f;
1284 R_Mesh_Draw(0, 6, 8, nomodelelements);
1287 void R_DrawNoModel(entity_render_t *ent)
1289 //if ((ent->effects & EF_ADDITIVE) || (ent->alpha < 1))
1290 R_MeshQueue_AddTransparent(ent->effects & EF_NODEPTHTEST ? r_vieworigin : ent->origin, R_DrawNoModelCallback, ent, 0);
1292 // R_DrawNoModelCallback(ent, 0);
1295 void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width)
1297 vec3_t right1, right2, diff, normal;
1299 VectorSubtract (org2, org1, normal);
1301 // calculate 'right' vector for start
1302 VectorSubtract (r_vieworigin, org1, diff);
1303 CrossProduct (normal, diff, right1);
1304 VectorNormalize (right1);
1306 // calculate 'right' vector for end
1307 VectorSubtract (r_vieworigin, org2, diff);
1308 CrossProduct (normal, diff, right2);
1309 VectorNormalize (right2);
1311 vert[ 0] = org1[0] + width * right1[0];
1312 vert[ 1] = org1[1] + width * right1[1];
1313 vert[ 2] = org1[2] + width * right1[2];
1314 vert[ 3] = org1[0] - width * right1[0];
1315 vert[ 4] = org1[1] - width * right1[1];
1316 vert[ 5] = org1[2] - width * right1[2];
1317 vert[ 6] = org2[0] - width * right2[0];
1318 vert[ 7] = org2[1] - width * right2[1];
1319 vert[ 8] = org2[2] - width * right2[2];
1320 vert[ 9] = org2[0] + width * right2[0];
1321 vert[10] = org2[1] + width * right2[1];
1322 vert[11] = org2[2] + width * right2[2];
1325 float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
1327 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)
1334 VectorSubtract(origin, r_vieworigin, diff);
1335 ca *= 1 - exp(fogdensity/DotProduct(diff,diff));
1338 R_Mesh_Matrix(&r_identitymatrix);
1339 GL_BlendFunc(blendfunc1, blendfunc2);
1340 GL_DepthMask(false);
1341 GL_DepthTest(!depthdisable);
1343 varray_vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
1344 varray_vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
1345 varray_vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
1346 varray_vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
1347 varray_vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
1348 varray_vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
1349 varray_vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
1350 varray_vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
1351 varray_vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
1352 varray_vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
1353 varray_vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
1354 varray_vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
1356 memset(&m, 0, sizeof(m));
1357 m.tex[0] = R_GetTexture(texture);
1358 m.pointer_texcoord[0] = spritetexcoord2f;
1359 m.pointer_vertex = varray_vertex3f;
1361 GL_Color(cr, cg, cb, ca);
1362 R_Mesh_Draw(0, 4, 2, polygonelements);