1da7d9753f0d41cbf3d4f2b6847c454cda1562f4
[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
24 // used for dlight push checking and other things
25 int r_framecount;
26
27 // used for visibility checking
28 qbyte r_pvsbits[(MAX_MAP_LEAFS+7)>>3];
29
30 mplane_t frustum[4];
31
32 matrix4x4_t r_identitymatrix;
33
34 int c_alias_polys, c_light_polys, c_faces, c_nodes, c_leafs, c_models, c_bmodels, c_sprites, c_particles, c_dlights;
35
36 // true during envmap command capture
37 qboolean envmap;
38
39 float r_farclip;
40
41 // view origin
42 vec3_t r_vieworigin;
43 vec3_t r_viewforward;
44 vec3_t r_viewleft;
45 vec3_t r_viewright;
46 vec3_t r_viewup;
47
48 //
49 // screen size info
50 //
51 refdef_t r_refdef;
52
53 // 8.8 fraction of base light value
54 unsigned short d_lightstylevalue[256];
55
56 cvar_t r_drawentities = {0, "r_drawentities","1"};
57 cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1"};
58 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
59 cvar_t r_speeds = {0, "r_speeds","0"};
60 cvar_t r_fullbright = {0, "r_fullbright","0"};
61 cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1"};
62 cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1"};
63 cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1"};
64 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
65 cvar_t r_drawcollisionbrushes = {0, "r_drawcollisionbrushes", "0"};
66
67 cvar_t gl_fogenable = {0, "gl_fogenable", "0"};
68 cvar_t gl_fogdensity = {0, "gl_fogdensity", "0.25"};
69 cvar_t gl_fogred = {0, "gl_fogred","0.3"};
70 cvar_t gl_foggreen = {0, "gl_foggreen","0.3"};
71 cvar_t gl_fogblue = {0, "gl_fogblue","0.3"};
72 cvar_t gl_fogstart = {0, "gl_fogstart", "0"};
73 cvar_t gl_fogend = {0, "gl_fogend","0"};
74
75 cvar_t r_textureunits = {0, "r_textureunits", "32"};
76
77 cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1"};
78 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1"};
79 cvar_t r_waterscroll = {CVAR_SAVE, "r_waterscroll", "1"};
80 cvar_t r_watershader = {CVAR_SAVE, "r_watershader", "1"};
81
82
83 void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
84 {
85         int i;
86         for (i = 0;i < verts;i++)
87         {
88                 out[0] = in[0] * r;
89                 out[1] = in[1] * g;
90                 out[2] = in[2] * b;
91                 out[3] = in[3];
92                 in += 4;
93                 out += 4;
94         }
95 }
96
97 void R_FillColors(float *out, int verts, float r, float g, float b, float a)
98 {
99         int i;
100         for (i = 0;i < verts;i++)
101         {
102                 out[0] = r;
103                 out[1] = g;
104                 out[2] = b;
105                 out[3] = a;
106                 out += 4;
107         }
108 }
109
110 /*
111 ====================
112 R_TimeRefresh_f
113
114 For program optimization
115 ====================
116 */
117 qboolean intimerefresh = 0;
118 static void R_TimeRefresh_f (void)
119 {
120         int i;
121         float timestart, timedelta, oldangles[3];
122
123         intimerefresh = 1;
124         VectorCopy(cl.viewangles, oldangles);
125         VectorClear(cl.viewangles);
126
127         timestart = Sys_DoubleTime();
128         for (i = 0;i < 128;i++)
129         {
130                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2], 0, i / 128.0 * 360.0, 0, 1);
131                 CL_UpdateScreen();
132         }
133         timedelta = Sys_DoubleTime() - timestart;
134
135         VectorCopy(oldangles, cl.viewangles);
136         intimerefresh = 0;
137         Con_Printf ("%f seconds (%f fps)\n", timedelta, 128/timedelta);
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_SetupFog(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 }
214
215 void gl_main_shutdown(void)
216 {
217 }
218
219 extern void CL_ParseEntityLump(char *entitystring);
220 void gl_main_newmap(void)
221 {
222         int l;
223         char *entities, entname[MAX_QPATH];
224         r_framecount = 1;
225         if (cl.worldmodel)
226         {
227                 strcpy(entname, cl.worldmodel->name);
228                 l = strlen(entname) - 4;
229                 if (l >= 0 && !strcmp(entname + l, ".bsp"))
230                 {
231                         strcpy(entname + l, ".ent");
232                         if ((entities = FS_LoadFile(entname, true)))
233                         {
234                                 CL_ParseEntityLump(entities);
235                                 Mem_Free(entities);
236                                 return;
237                         }
238                 }
239                 if (cl.worldmodel->brush.entities)
240                         CL_ParseEntityLump(cl.worldmodel->brush.entities);
241         }
242 }
243
244 void GL_Main_Init(void)
245 {
246         Matrix4x4_CreateIdentity(&r_identitymatrix);
247 // FIXME: move this to client?
248         FOG_registercvars();
249         Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
250         Cvar_RegisterVariable(&r_drawentities);
251         Cvar_RegisterVariable(&r_drawviewmodel);
252         Cvar_RegisterVariable(&r_shadow_staticworldlights);
253         Cvar_RegisterVariable(&r_speeds);
254         Cvar_RegisterVariable(&r_fullbrights);
255         Cvar_RegisterVariable(&r_wateralpha);
256         Cvar_RegisterVariable(&r_dynamic);
257         Cvar_RegisterVariable(&r_fullbright);
258         Cvar_RegisterVariable(&r_textureunits);
259         Cvar_RegisterVariable(&r_shadow_cull);
260         Cvar_RegisterVariable(&r_lerpsprites);
261         Cvar_RegisterVariable(&r_lerpmodels);
262         Cvar_RegisterVariable(&r_waterscroll);
263         Cvar_RegisterVariable(&r_watershader);
264         Cvar_RegisterVariable(&r_drawcollisionbrushes);
265         if (gamemode == GAME_NEHAHRA || gamemode == GAME_NEXUIZ)
266                 Cvar_SetValue("r_fullbrights", 0);
267         R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
268 }
269
270 vec3_t r_farclip_origin;
271 vec3_t r_farclip_direction;
272 vec_t r_farclip_directiondist;
273 vec_t r_farclip_meshfarclip;
274 int r_farclip_directionbit0;
275 int r_farclip_directionbit1;
276 int r_farclip_directionbit2;
277
278 // start a farclip measuring session
279 void R_FarClip_Start(vec3_t origin, vec3_t direction, vec_t startfarclip)
280 {
281         VectorCopy(origin, r_farclip_origin);
282         VectorCopy(direction, r_farclip_direction);
283         r_farclip_directiondist = DotProduct(r_farclip_origin, r_farclip_direction);
284         r_farclip_directionbit0 = r_farclip_direction[0] < 0;
285         r_farclip_directionbit1 = r_farclip_direction[1] < 0;
286         r_farclip_directionbit2 = r_farclip_direction[2] < 0;
287         r_farclip_meshfarclip = r_farclip_directiondist + startfarclip;
288 }
289
290 // enlarge farclip to accomodate box
291 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 float R_FarClip_Finish(void)
303 {
304         return r_farclip_meshfarclip - r_farclip_directiondist;
305 }
306
307 extern void R_Textures_Init(void);
308 extern void Mod_RenderInit(void);
309 extern void GL_Draw_Init(void);
310 extern void GL_Main_Init(void);
311 extern void R_Shadow_Init(void);
312 extern void GL_Models_Init(void);
313 extern void R_Sky_Init(void);
314 extern void GL_Surf_Init(void);
315 extern void R_Crosshairs_Init(void);
316 extern void R_Light_Init(void);
317 extern void R_Particles_Init(void);
318 extern void R_Explosion_Init(void);
319 extern void ui_init(void);
320 extern void gl_backend_init(void);
321 extern void Sbar_Init(void);
322 extern void R_LightningBeams_Init(void);
323
324 void Render_Init(void)
325 {
326         R_Textures_Init();
327         Mod_RenderInit();
328         gl_backend_init();
329         R_MeshQueue_Init();
330         GL_Draw_Init();
331         GL_Main_Init();
332         R_Shadow_Init();
333         GL_Models_Init();
334         R_Sky_Init();
335         GL_Surf_Init();
336         R_Crosshairs_Init();
337         R_Light_Init();
338         R_Particles_Init();
339         R_Explosion_Init();
340         //ui_init();
341         UI_Init();
342         Sbar_Init();
343         R_LightningBeams_Init();
344 }
345
346 /*
347 ===============
348 GL_Init
349 ===============
350 */
351 extern char *ENGINE_EXTENSIONS;
352 void GL_Init (void)
353 {
354         VID_CheckExtensions();
355
356         // LordHavoc: report supported extensions
357         Con_DPrintf("\nengine extensions: %s\n", ENGINE_EXTENSIONS);
358 }
359
360 int R_CullBox(const vec3_t mins, const vec3_t maxs)
361 {
362         int i;
363         mplane_t *p;
364         for (i = 0;i < 4;i++)
365         {
366                 p = frustum + i;
367                 switch(p->signbits)
368                 {
369                 default:
370                 case 0:
371                         if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
372                                 return true;
373                         break;
374                 case 1:
375                         if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist)
376                                 return true;
377                         break;
378                 case 2:
379                         if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
380                                 return true;
381                         break;
382                 case 3:
383                         if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist)
384                                 return true;
385                         break;
386                 case 4:
387                         if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
388                                 return true;
389                         break;
390                 case 5:
391                         if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist)
392                                 return true;
393                         break;
394                 case 6:
395                         if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
396                                 return true;
397                         break;
398                 case 7:
399                         if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist)
400                                 return true;
401                         break;
402                 }
403         }
404         return false;
405 }
406
407 #define VIS_CullBox(mins,maxs) (R_CullBox((mins), (maxs)) || (cl.worldmodel && cl.worldmodel->brush.BoxTouchingPVS && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, r_pvsbits, (mins), (maxs))))
408
409 //==================================================================================
410
411 static void R_MarkEntities (void)
412 {
413         int i;
414         entity_render_t *ent;
415
416         ent = &cl_entities[0].render;
417         Matrix4x4_CreateIdentity(&ent->matrix);
418         Matrix4x4_CreateIdentity(&ent->inversematrix);
419
420         if (cl.worldmodel)
421                 R_FarClip_Box(cl.worldmodel->normalmins, cl.worldmodel->normalmaxs);
422
423         if (!r_drawentities.integer)
424                 return;
425
426         for (i = 0;i < r_refdef.numentities;i++)
427         {
428                 ent = r_refdef.entities[i];
429                 Mod_CheckLoaded(ent->model);
430                 // some of the renderer still relies on origin...
431                 Matrix4x4_OriginFromMatrix(&ent->matrix, ent->origin);
432                 // some of the renderer still relies on scale...
433                 ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
434                 R_LerpAnimation(ent);
435                 R_UpdateEntLights(ent);
436                 if ((chase_active.integer || !(ent->flags & RENDER_EXTERIORMODEL))
437                  && !VIS_CullBox(ent->mins, ent->maxs)
438                  && (!envmap || !(ent->flags & (RENDER_VIEWMODEL | RENDER_EXTERIORMODEL))))
439                 {
440                         ent->visframe = r_framecount;
441                         R_FarClip_Box(ent->mins, ent->maxs);
442                 }
443         }
444 }
445
446 // only used if skyrendermasked, and normally returns false
447 int R_DrawBrushModelsSky (void)
448 {
449         int i, sky;
450         entity_render_t *ent;
451
452         if (!r_drawentities.integer)
453                 return false;
454
455         sky = false;
456         for (i = 0;i < r_refdef.numentities;i++)
457         {
458                 ent = r_refdef.entities[i];
459                 if (ent->visframe == r_framecount && ent->model && ent->model->DrawSky)
460                 {
461                         ent->model->DrawSky(ent);
462                         sky = true;
463                 }
464         }
465         return sky;
466 }
467
468 /*
469 =============
470 R_DrawViewModel
471 =============
472 */
473 /*
474 void R_DrawViewModel (void)
475 {
476         entity_render_t *ent;
477
478         // FIXME: move these checks to client
479         if (!r_drawviewmodel.integer || chase_active.integer || envmap || !r_drawentities.integer || cl.items & IT_INVISIBILITY || cl.stats[STAT_HEALTH] <= 0 || !cl.viewent.render.model)
480                 return;
481
482         ent = &cl.viewent.render;
483         Mod_CheckLoaded(ent->model);
484         R_LerpAnimation(ent);
485         Matrix4x4_CreateFromQuakeEntity(&ent->matrix, ent->origin[0], ent->origin[1], ent->origin[2], -ent->angles[0], ent->angles[1], ent->angles[2], ent->scale);
486         Matrix4x4_Invert_Simple(&ent->inversematrix, &ent->matrix);
487         R_UpdateEntLights(ent);
488         ent->model->Draw(ent);
489 }
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 #include "r_shadow.h"
515
516 int shadowframecount = 0;
517
518 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t lightorigin, float cullradius, float lightradius, vec3_t lightmins, vec3_t lightmaxs, vec3_t clipmins, vec3_t clipmaxs, int lightmarked)
519 {
520         vec3_t relativelightorigin;
521         // rough checks
522         if ((ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume && !(r_shadow_cull.integer && (ent->maxs[0] < lightmins[0] || ent->mins[0] > lightmaxs[0] || ent->maxs[1] < lightmins[1] || ent->mins[1] > lightmaxs[1] || ent->maxs[2] < lightmins[2] || ent->mins[2] > lightmaxs[2])))
523         {
524                 Matrix4x4_Transform(&ent->inversematrix, lightorigin, relativelightorigin);
525                 ent->model->DrawShadowVolume (ent, relativelightorigin, lightradius);
526         }
527 }
528
529 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light);
530
531 void R_ShadowVolumeLighting(int visiblevolumes)
532 {
533         int i;
534         entity_render_t *ent;
535         int lnum;
536         float f, lightradius, cullradius;
537         vec3_t relativelightorigin, relativeeyeorigin, lightcolor, clipmins, clipmaxs;
538         worldlight_t *wl;
539         rdlight_t *rd;
540         rmeshstate_t m;
541         matrix4x4_t matrix;
542         matrix4x4_t matrix_worldtofilter, matrix_worldtoattenuationxyz, matrix_worldtoattenuationz;
543         matrix4x4_t matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
544
545         if (visiblevolumes)
546         {
547                 memset(&m, 0, sizeof(m));
548                 R_Mesh_State_Texture(&m);
549
550                 GL_BlendFunc(GL_ONE, GL_ONE);
551                 GL_DepthMask(false);
552                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
553                 qglDisable(GL_CULL_FACE);
554                 GL_Color(0.0, 0.0125, 0.1, 1);
555         }
556         else
557                 R_Shadow_Stage_Begin();
558         shadowframecount++;
559         if (r_shadow_realtime_world.integer)
560         {
561                 R_Shadow_LoadWorldLightsIfNeeded();
562                 for (lnum = 0, wl = r_shadow_worldlightchain;wl;wl = wl->next, lnum++)
563                 {
564                         if (d_lightstylevalue[wl->style] <= 0)
565                                 continue;
566                         if (VIS_CullBox(wl->mins, wl->maxs))
567                                 continue;
568                         if (r_shadow_debuglight.integer >= 0 && lnum != r_shadow_debuglight.integer)
569                                 continue;
570                         if (R_Shadow_ScissorForBBox(wl->mins, wl->maxs))
571                                 continue;
572
573                         cullradius = wl->cullradius;
574                         lightradius = wl->lightradius;
575                         VectorCopy(wl->mins, clipmins);
576                         VectorCopy(wl->maxs, clipmaxs);
577
578                         f = d_lightstylevalue[wl->style] * (1.0f / 256.0f);
579                         VectorScale(wl->light, f, lightcolor);
580                         if (wl->selected)
581                         {
582                                 f = 2 + sin(realtime * M_PI * 4.0);
583                                 VectorScale(lightcolor, f, lightcolor);
584                         }
585
586                         if (wl->castshadows && (gl_stencil || visiblevolumes))
587                         {
588                                 if (!visiblevolumes)
589                                         R_Shadow_Stage_ShadowVolumes();
590                                 ent = &cl_entities[0].render;
591                                 if (r_shadow_staticworldlights.integer)
592                                         R_Shadow_DrawStaticWorldLight_Shadow(wl, &ent->matrix);
593                                 else
594                                         R_TestAndDrawShadowVolume(ent, wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs, true);
595                                 if (r_drawentities.integer)
596                                         for (i = 0;i < r_refdef.numentities;i++)
597                                                 R_TestAndDrawShadowVolume(r_refdef.entities[i], wl->origin, cullradius, lightradius, wl->mins, wl->maxs, clipmins, clipmaxs, true);
598                         }
599
600                         if (!visiblevolumes)
601                         {
602                                 if (wl->castshadows && gl_stencil)
603                                         R_Shadow_Stage_LightWithShadows();
604                                 else
605                                         R_Shadow_Stage_LightWithoutShadows();
606
607                                 // calculate world to filter matrix
608                                 Matrix4x4_CreateFromQuakeEntity(&matrix, wl->origin[0], wl->origin[1], wl->origin[2], wl->angles[0], wl->angles[1], wl->angles[2], lightradius);
609                                 Matrix4x4_Invert_Simple(&matrix_worldtofilter, &matrix);
610                                 // calculate world to attenuationxyz/xy matrix
611                                 Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5, 0.5, 0.5, 0, 0, 0, 0.5);
612                                 Matrix4x4_Concat(&matrix_worldtoattenuationxyz, &matrix, &matrix_worldtofilter);
613                                 // calculate world to attenuationz matrix
614                                 matrix.m[0][0] = 0;matrix.m[0][1] = 0;matrix.m[0][2] = 0.5;matrix.m[0][3] = 0.5;
615                                 matrix.m[1][0] = 0;matrix.m[1][1] = 0;matrix.m[1][2] = 0  ;matrix.m[1][3] = 0.5;
616                                 matrix.m[2][0] = 0;matrix.m[2][1] = 0;matrix.m[2][2] = 0  ;matrix.m[2][3] = 0.5;
617                                 matrix.m[3][0] = 0;matrix.m[3][1] = 0;matrix.m[3][2] = 0  ;matrix.m[3][3] = 1;
618                                 Matrix4x4_Concat(&matrix_worldtoattenuationz, &matrix, &matrix_worldtofilter);
619
620                                 ent = &cl_entities[0].render;
621                                 if (ent->model && ent->model->DrawLight)
622                                 {
623                                         Matrix4x4_Transform(&ent->inversematrix, wl->origin, relativelightorigin);
624                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
625                                         Matrix4x4_Concat(&matrix_modeltofilter, &matrix_worldtofilter, &ent->matrix);
626                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &matrix_worldtoattenuationxyz, &ent->matrix);
627                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &matrix_worldtoattenuationz, &ent->matrix);
628                                         if (r_shadow_staticworldlights.integer)
629                                                 R_Shadow_DrawStaticWorldLight_Light(wl, &ent->matrix, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, &matrix_modeltofilter, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz);
630                                         else
631                                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius / ent->scale, lightcolor, &matrix_modeltofilter, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz);
632                                 }
633                                 if (r_drawentities.integer)
634                                 {
635                                         for (i = 0;i < r_refdef.numentities;i++)
636                                         {
637                                                 ent = r_refdef.entities[i];
638                                                 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
639                                                  && BoxesOverlap(ent->mins, ent->maxs, clipmins, clipmaxs)
640                                                  && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
641                                                 {
642                                                         Matrix4x4_Transform(&ent->inversematrix, wl->origin, relativelightorigin);
643                                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
644                                                         Matrix4x4_Concat(&matrix_modeltofilter, &matrix_worldtofilter, &ent->matrix);
645                                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &matrix_worldtoattenuationxyz, &ent->matrix);
646                                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &matrix_worldtoattenuationz, &ent->matrix);
647                                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius / ent->scale, lightcolor, &matrix_modeltofilter, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz);
648                                                 }
649                                         }
650                                 }
651                         }
652                 }
653         }
654         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
655         {
656                 for (lnum = 0, rd = r_dlight;lnum < r_numdlights;lnum++, rd++)
657                 {
658                         lightradius = rd->cullradius;
659                         clipmins[0] = rd->origin[0] - lightradius;
660                         clipmins[1] = rd->origin[1] - lightradius;
661                         clipmins[2] = rd->origin[2] - lightradius;
662                         clipmaxs[0] = rd->origin[0] + lightradius;
663                         clipmaxs[1] = rd->origin[1] + lightradius;
664                         clipmaxs[2] = rd->origin[2] + lightradius;
665                         if (VIS_CullBox(clipmins, clipmaxs) || R_Shadow_ScissorForBBox(clipmins, clipmaxs))
666                                 continue;
667
668                         cullradius = RadiusFromBoundsAndOrigin(clipmins, clipmaxs, rd->origin);
669                         VectorScale(rd->light, (1.0f / 4096.0f), lightcolor);
670
671                         if (r_shadow_shadows.integer && (gl_stencil || visiblevolumes))
672                         {
673                                 if (!visiblevolumes)
674                                         R_Shadow_Stage_ShadowVolumes();
675                                 ent = &cl_entities[0].render;
676                                 R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius, clipmins, clipmaxs, clipmins, clipmaxs, false);
677                                 if (r_drawentities.integer)
678                                 {
679                                         for (i = 0;i < r_refdef.numentities;i++)
680                                         {
681                                                 ent = r_refdef.entities[i];
682                                                 if (ent != rd->ent)
683                                                         R_TestAndDrawShadowVolume(ent, rd->origin, cullradius, lightradius, clipmins, clipmaxs, clipmins, clipmaxs, false);
684                                         }
685                                 }
686                         }
687
688                         if (!visiblevolumes)
689                         {
690                                 if (r_shadow_shadows.integer && gl_stencil)
691                                         R_Shadow_Stage_LightWithShadows();
692                                 else
693                                         R_Shadow_Stage_LightWithoutShadows();
694
695                                 // calculate world to filter matrix
696                                 Matrix4x4_CreateFromQuakeEntity(&matrix, rd->origin[0], rd->origin[1], rd->origin[2], 0, 0, 0, lightradius);
697                                 Matrix4x4_Invert_Simple(&matrix_worldtofilter, &matrix);
698                                 // calculate world to attenuationxyz/xy matrix
699                                 Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5, 0.5, 0.5, 0, 0, 0, 0.5);
700                                 Matrix4x4_Concat(&matrix_worldtoattenuationxyz, &matrix, &matrix_worldtofilter);
701                                 // calculate world to attenuationz matrix
702                                 matrix.m[0][0] = 0;matrix.m[0][1] = 0;matrix.m[0][2] = 0.5;matrix.m[0][3] = 0.5;
703                                 matrix.m[1][0] = 0;matrix.m[1][1] = 0;matrix.m[1][2] = 0  ;matrix.m[1][3] = 0.5;
704                                 matrix.m[2][0] = 0;matrix.m[2][1] = 0;matrix.m[2][2] = 0  ;matrix.m[2][3] = 0.5;
705                                 matrix.m[3][0] = 0;matrix.m[3][1] = 0;matrix.m[3][2] = 0  ;matrix.m[3][3] = 1;
706                                 Matrix4x4_Concat(&matrix_worldtoattenuationz, &matrix, &matrix_worldtofilter);
707
708                                 ent = &cl_entities[0].render;
709                                 if (ent->model && ent->model->DrawLight)
710                                 {
711                                         Matrix4x4_Transform(&ent->inversematrix, rd->origin, relativelightorigin);
712                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
713                                         Matrix4x4_Concat(&matrix_modeltofilter, &matrix_worldtofilter, &ent->matrix);
714                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &matrix_worldtoattenuationxyz, &ent->matrix);
715                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &matrix_worldtoattenuationz, &ent->matrix);
716                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius / ent->scale, lightcolor, &matrix_modeltofilter, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz);
717                                 }
718                                 if (r_drawentities.integer)
719                                 {
720                                         for (i = 0;i < r_refdef.numentities;i++)
721                                         {
722                                                 ent = r_refdef.entities[i];
723                                                 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
724                                                  && BoxesOverlap(ent->mins, ent->maxs, clipmins, clipmaxs)
725                                                  && !(ent->effects & EF_ADDITIVE) && ent->alpha == 1)
726                                                 {
727                                                         Matrix4x4_Transform(&ent->inversematrix, rd->origin, relativelightorigin);
728                                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
729                                                         Matrix4x4_Concat(&matrix_modeltofilter, &matrix_worldtofilter, &ent->matrix);
730                                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &matrix_worldtoattenuationxyz, &ent->matrix);
731                                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &matrix_worldtoattenuationz, &ent->matrix);
732                                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, lightradius / ent->scale, lightcolor, &matrix_modeltofilter, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz);
733                                                 }
734                                         }
735                                 }
736                         }
737                 }
738         }
739
740         if (visiblevolumes)
741         {
742                 qglEnable(GL_CULL_FACE);
743                 qglDisable(GL_SCISSOR_TEST);
744         }
745         else
746                 R_Shadow_Stage_End();
747 }
748
749 static void R_SetFrustum (void)
750 {
751         // LordHavoc: note to all quake engine coders, the special case for 90
752         // degrees assumed a square view (wrong), so I removed it, Quake2 has it
753         // disabled as well.
754
755         // rotate R_VIEWFORWARD right by FOV_X/2 degrees
756         RotatePointAroundVector( frustum[0].normal, r_viewup, r_viewforward, -(90 - r_refdef.fov_x / 2));
757         frustum[0].dist = DotProduct (r_vieworigin, frustum[0].normal);
758         PlaneClassify(&frustum[0]);
759
760         // rotate R_VIEWFORWARD left by FOV_X/2 degrees
761         RotatePointAroundVector( frustum[1].normal, r_viewup, r_viewforward, (90 - r_refdef.fov_x / 2));
762         frustum[1].dist = DotProduct (r_vieworigin, frustum[1].normal);
763         PlaneClassify(&frustum[1]);
764
765         // rotate R_VIEWFORWARD up by FOV_X/2 degrees
766         RotatePointAroundVector( frustum[2].normal, r_viewleft, r_viewforward, -(90 - r_refdef.fov_y / 2));
767         frustum[2].dist = DotProduct (r_vieworigin, frustum[2].normal);
768         PlaneClassify(&frustum[2]);
769
770         // rotate R_VIEWFORWARD down by FOV_X/2 degrees
771         RotatePointAroundVector( frustum[3].normal, r_viewleft, r_viewforward, (90 - r_refdef.fov_y / 2));
772         frustum[3].dist = DotProduct (r_vieworigin, frustum[3].normal);
773         PlaneClassify(&frustum[3]);
774 }
775
776 /*
777 ===============
778 R_SetupFrame
779 ===============
780 */
781 static void R_SetupFrame (void)
782 {
783 // don't allow cheats in multiplayer
784         if (!cl.islocalgame)
785         {
786                 if (r_fullbright.integer != 0)
787                         Cvar_Set ("r_fullbright", "0");
788                 if (r_ambient.value != 0)
789                         Cvar_Set ("r_ambient", "0");
790         }
791
792         r_framecount++;
793
794         // break apart the viewentity matrix into vectors for various purposes
795         Matrix4x4_ToVectors(&r_refdef.viewentitymatrix, r_viewforward, r_viewleft, r_viewup, r_vieworigin);
796         VectorNegate(r_viewleft, r_viewright);
797
798         GL_SetupView_ViewPort(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
799         if ((r_shadow_realtime_world.integer || r_shadow_shadows.integer) && gl_stencil)
800                 GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_refdef.fov_x, r_refdef.fov_y, 1.0f);
801         else
802                 GL_SetupView_Mode_Perspective(r_refdef.fov_x, r_refdef.fov_y, 1.0f, r_farclip);
803         GL_SetupView_Orientation_FromEntity(&r_refdef.viewentitymatrix);
804
805         R_AnimateLight();
806 }
807
808
809 static void R_BlendView(void)
810 {
811         rmeshstate_t m;
812         float r;
813         float vertex3f[3*3];
814
815         if (r_refdef.viewblend[3] < 0.01f)
816                 return;
817
818         R_Mesh_Matrix(&r_identitymatrix);
819
820         memset(&m, 0, sizeof(m));
821         R_Mesh_State_Texture(&m);
822
823         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
824         GL_DepthMask(true);
825         GL_DepthTest(false); // magic
826         GL_VertexPointer(vertex3f);
827         GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
828         r = 64000;
829         vertex3f[0] = r_vieworigin[0] + r_viewforward[0] * 1.5 + r_viewleft[0] * r - r_viewup[0] * r;
830         vertex3f[1] = r_vieworigin[1] + r_viewforward[1] * 1.5 + r_viewleft[1] * r - r_viewup[1] * r;
831         vertex3f[2] = r_vieworigin[2] + r_viewforward[2] * 1.5 + r_viewleft[2] * r - r_viewup[2] * r;
832         vertex3f[3] = r_vieworigin[0] + r_viewforward[0] * 1.5 + r_viewleft[0] * r + r_viewup[0] * r * 3;
833         vertex3f[4] = r_vieworigin[1] + r_viewforward[1] * 1.5 + r_viewleft[1] * r + r_viewup[1] * r * 3;
834         vertex3f[5] = r_vieworigin[2] + r_viewforward[2] * 1.5 + r_viewleft[2] * r + r_viewup[2] * r * 3;
835         vertex3f[6] = r_vieworigin[0] + r_viewforward[0] * 1.5 - r_viewleft[0] * r * 3 - r_viewup[0] * r;
836         vertex3f[7] = r_vieworigin[1] + r_viewforward[1] * 1.5 - r_viewleft[1] * r * 3 - r_viewup[1] * r;
837         vertex3f[8] = r_vieworigin[2] + r_viewforward[2] * 1.5 - r_viewleft[2] * r * 3 - r_viewup[2] * r;
838         R_Mesh_Draw(3, 1, polygonelements);
839 }
840
841 /*
842 ================
843 R_RenderView
844
845 r_refdef must be set before the first call
846 ================
847 */
848 extern void R_DrawLightningBeams (void);
849 void R_RenderView (void)
850 {
851         entity_render_t *world;
852         if (!r_refdef.entities/* || !cl.worldmodel*/)
853                 return; //Host_Error ("R_RenderView: NULL worldmodel");
854
855         if (r_shadow_realtime_world.integer)
856         {
857                 if (!gl_stencil)
858                 {
859                         Con_Printf("Realtime world lighting requires 32bit color turning off r_shadow_realtime_world, please type vid_bitsperpixel 32;vid_restart and try again\n");
860                         Cvar_SetValueQuick(&r_shadow_realtime_world, 0);
861                 }
862         }
863
864         world = &cl_entities[0].render;
865
866         // FIXME: move to client
867         R_MoveExplosions();
868         R_TimeReport("mexplosion");
869
870         qglPolygonOffset(0, 0);
871         qglEnable(GL_POLYGON_OFFSET_FILL);
872
873         R_Textures_Frame();
874         R_SetupFrame();
875         R_SetFrustum();
876         R_SetupFog();
877         R_SkyStartFrame();
878         R_BuildLightList();
879         R_TimeReport("setup");
880
881         if (cl.worldmodel && cl.worldmodel->brush.FatPVS)
882                 cl.worldmodel->brush.FatPVS(cl.worldmodel, r_vieworigin, 2, r_pvsbits, sizeof(r_pvsbits));
883
884         R_WorldVisibility(world);
885         R_TimeReport("worldvis");
886
887         R_FarClip_Start(r_vieworigin, r_viewforward, 768.0f);
888         R_MarkEntities();
889         r_farclip = R_FarClip_Finish() + 256.0f;
890         R_TimeReport("markentity");
891
892         qglDepthFunc(GL_LEQUAL);
893
894         R_Mesh_Start();
895         R_MeshQueue_BeginScene();
896
897         R_Shadow_UpdateWorldLightSelection();
898
899         if (R_DrawBrushModelsSky())
900                 R_TimeReport("bmodelsky");
901
902         // must occur early because it can draw sky
903         R_DrawWorld(world);
904         R_TimeReport("world");
905
906         // don't let sound skip if going slow
907         if (!intimerefresh && !r_speeds.integer)
908                 S_ExtraUpdate ();
909
910         R_DrawModels();
911         R_TimeReport("models");
912
913         R_ShadowVolumeLighting(false);
914         R_TimeReport("rtlights");
915
916         R_DrawLightningBeams();
917         R_TimeReport("lightning");
918
919         R_DrawParticles();
920         R_TimeReport("particles");
921
922         R_DrawExplosions();
923         R_TimeReport("explosions");
924
925         R_MeshQueue_RenderTransparent();
926         R_TimeReport("drawtrans");
927
928         R_DrawCoronas();
929         R_TimeReport("coronas");
930
931         R_DrawWorldCrosshair();
932         R_TimeReport("crosshair");
933
934         R_BlendView();
935         R_TimeReport("blendview");
936
937         R_MeshQueue_Render();
938         R_MeshQueue_EndScene();
939
940         if (r_shadow_visiblevolumes.integer)
941         {
942                 R_ShadowVolumeLighting(true);
943                 R_TimeReport("shadowvolume");
944         }
945
946         R_Mesh_Finish();
947         R_TimeReport("meshfinish");
948
949         qglPolygonOffset(0, 0);
950         qglDisable(GL_POLYGON_OFFSET_FILL);
951 }
952
953 /*
954 void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, float ca)
955 {
956         int i;
957         float *v, *c, f1, f2, diff[3], vertex3f[8*3], color4f[8*4];
958         rmeshstate_t m;
959         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
960         GL_DepthMask(false);
961         GL_DepthTest(true);
962         R_Mesh_Matrix(&r_identitymatrix);
963
964         memset(&m, 0, sizeof(m));
965         R_Mesh_State_Texture(&m);
966
967         R_Mesh_GetSpace(8);
968         vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2];
969         vertex3f[ 3] = maxs[0];vertex3f[ 4] = mins[1];vertex3f[ 5] = mins[2];
970         vertex3f[ 6] = mins[0];vertex3f[ 7] = maxs[1];vertex3f[ 8] = mins[2];
971         vertex3f[ 9] = maxs[0];vertex3f[10] = maxs[1];vertex3f[11] = mins[2];
972         vertex3f[12] = mins[0];vertex3f[13] = mins[1];vertex3f[14] = maxs[2];
973         vertex3f[15] = maxs[0];vertex3f[16] = mins[1];vertex3f[17] = maxs[2];
974         vertex3f[18] = mins[0];vertex3f[19] = maxs[1];vertex3f[20] = maxs[2];
975         vertex3f[21] = maxs[0];vertex3f[22] = maxs[1];vertex3f[23] = maxs[2];
976         GL_ColorPointer(color);
977         R_FillColors(color, 8, cr, cg, cb, ca);
978         if (fogenabled)
979         {
980                 for (i = 0, v = vertex, c = color;i < 8;i++, v += 4, c += 4)
981                 {
982                         VectorSubtract(v, r_vieworigin, diff);
983                         f2 = exp(fogdensity/DotProduct(diff, diff));
984                         f1 = 1 - f2;
985                         c[0] = c[0] * f1 + fogcolor[0] * f2;
986                         c[1] = c[1] * f1 + fogcolor[1] * f2;
987                         c[2] = c[2] * f1 + fogcolor[2] * f2;
988                 }
989         }
990         R_Mesh_Draw(8, 12);
991 }
992 */
993
994 int nomodelelements[24] =
995 {
996         5, 2, 0,
997         5, 1, 2,
998         5, 0, 3,
999         5, 3, 1,
1000         0, 2, 4,
1001         2, 1, 4,
1002         3, 0, 4,
1003         1, 3, 4
1004 };
1005
1006 float nomodelvertex3f[6*3] =
1007 {
1008         -16,   0,   0,
1009          16,   0,   0,
1010           0, -16,   0,
1011           0,  16,   0,
1012           0,   0, -16,
1013           0,   0,  16
1014 };
1015
1016 float nomodelcolor4f[6*4] =
1017 {
1018         0.0f, 0.0f, 0.5f, 1.0f,
1019         0.0f, 0.0f, 0.5f, 1.0f,
1020         0.0f, 0.5f, 0.0f, 1.0f,
1021         0.0f, 0.5f, 0.0f, 1.0f,
1022         0.5f, 0.0f, 0.0f, 1.0f,
1023         0.5f, 0.0f, 0.0f, 1.0f
1024 };
1025
1026 void R_DrawNoModelCallback(const void *calldata1, int calldata2)
1027 {
1028         const entity_render_t *ent = calldata1;
1029         int i;
1030         float f1, f2, *c, diff[3];
1031         float color4f[6*4];
1032         rmeshstate_t m;
1033         R_Mesh_Matrix(&ent->matrix);
1034
1035         memset(&m, 0, sizeof(m));
1036         R_Mesh_State_Texture(&m);
1037
1038         if (ent->flags & EF_ADDITIVE)
1039         {
1040                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1041                 GL_DepthMask(false);
1042         }
1043         else if (ent->alpha < 1)
1044         {
1045                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1046                 GL_DepthMask(false);
1047         }
1048         else
1049         {
1050                 GL_BlendFunc(GL_ONE, GL_ZERO);
1051                 GL_DepthMask(true);
1052         }
1053         GL_DepthTest(true);
1054         GL_VertexPointer(nomodelvertex3f);
1055         if (fogenabled)
1056         {
1057                 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
1058                 GL_ColorPointer(color4f);
1059                 VectorSubtract(ent->origin, r_vieworigin, diff);
1060                 f2 = exp(fogdensity/DotProduct(diff, diff));
1061                 f1 = 1 - f2;
1062                 for (i = 0, c = color4f;i < 6;i++, c += 4)
1063                 {
1064                         c[0] = (c[0] * f1 + fogcolor[0] * f2);
1065                         c[1] = (c[1] * f1 + fogcolor[1] * f2);
1066                         c[2] = (c[2] * f1 + fogcolor[2] * f2);
1067                         c[3] *= ent->alpha;
1068                 }
1069         }
1070         else if (ent->alpha != 1)
1071         {
1072                 memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
1073                 GL_ColorPointer(color4f);
1074                 for (i = 0, c = color4f;i < 6;i++, c += 4)
1075                         c[3] *= ent->alpha;
1076         }
1077         else
1078                 GL_ColorPointer(nomodelcolor4f);
1079         R_Mesh_Draw(6, 8, nomodelelements);
1080 }
1081
1082 void R_DrawNoModel(entity_render_t *ent)
1083 {
1084         //if ((ent->effects & EF_ADDITIVE) || (ent->alpha < 1))
1085                 R_MeshQueue_AddTransparent(ent->origin, R_DrawNoModelCallback, ent, 0);
1086         //else
1087         //      R_DrawNoModelCallback(ent, 0);
1088 }
1089
1090 void R_CalcBeam_Vertex3f (float *vert, const vec3_t org1, const vec3_t org2, float width)
1091 {
1092         vec3_t right1, right2, diff, normal;
1093
1094         VectorSubtract (org2, org1, normal);
1095         VectorNormalizeFast (normal);
1096
1097         // calculate 'right' vector for start
1098         VectorSubtract (r_vieworigin, org1, diff);
1099         VectorNormalizeFast (diff);
1100         CrossProduct (normal, diff, right1);
1101
1102         // calculate 'right' vector for end
1103         VectorSubtract (r_vieworigin, org2, diff);
1104         VectorNormalizeFast (diff);
1105         CrossProduct (normal, diff, right2);
1106
1107         vert[ 0] = org1[0] + width * right1[0];
1108         vert[ 1] = org1[1] + width * right1[1];
1109         vert[ 2] = org1[2] + width * right1[2];
1110         vert[ 3] = org1[0] - width * right1[0];
1111         vert[ 4] = org1[1] - width * right1[1];
1112         vert[ 5] = org1[2] - width * right1[2];
1113         vert[ 6] = org2[0] - width * right2[0];
1114         vert[ 7] = org2[1] - width * right2[1];
1115         vert[ 8] = org2[2] - width * right2[2];
1116         vert[ 9] = org2[0] + width * right2[0];
1117         vert[10] = org2[1] + width * right2[1];
1118         vert[11] = org2[2] + width * right2[2];
1119 }
1120
1121 float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
1122
1123 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)
1124 {
1125         float diff[3];
1126         rmeshstate_t m;
1127
1128         if (fogenabled)
1129         {
1130                 VectorSubtract(origin, r_vieworigin, diff);
1131                 ca *= 1 - exp(fogdensity/DotProduct(diff,diff));
1132         }
1133
1134         R_Mesh_Matrix(&r_identitymatrix);
1135         GL_Color(cr, cg, cb, ca);
1136         GL_VertexPointer(varray_vertex3f);
1137         GL_BlendFunc(blendfunc1, blendfunc2);
1138         GL_DepthMask(false);
1139         GL_DepthTest(!depthdisable);
1140
1141         memset(&m, 0, sizeof(m));
1142         m.tex[0] = R_GetTexture(texture);
1143         m.pointer_texcoord[0] = spritetexcoord2f;
1144         R_Mesh_State_Texture(&m);
1145
1146         varray_vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
1147         varray_vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
1148         varray_vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
1149         varray_vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
1150         varray_vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
1151         varray_vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
1152         varray_vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
1153         varray_vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
1154         varray_vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
1155         varray_vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
1156         varray_vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
1157         varray_vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
1158         R_Mesh_Draw(4, 2, polygonelements);
1159 }
1160