1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2009 John Fitzgibbons and others
4 Copyright (C) 2007-2008 Kristian Duske
5 Copyright (C) 2010-2014 QuakeSpasm developers
6 Copyright (C) 2016 Axel Gneiting
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24 //gl_sky.c
25
26 #include "quakedef.h"
27
28 #define MAX_CLIP_VERTS 64
29
30 float Fog_GetDensity(void);
31 float *Fog_GetColor(void);
32
33 extern qmodel_t *loadmodel;
34 extern unsigned int rs_skypolys; //for r_speeds readout
35 extern unsigned int rs_skypasses; //for r_speeds readout
36 float skyflatcolor[3];
37 float skymins[2][6], skymaxs[2][6];
38
39 char skybox_name[1024]; //name of current skybox, or "" if no skybox
40
41 gltexture_t *skybox_textures[6];
42 gltexture_t *solidskytexture, *alphaskytexture;
43
44 extern cvar_t gl_farclip;
45 cvar_t r_fastsky = {"r_fastsky", "0", CVAR_NONE};
46 cvar_t r_sky_quality = {"r_sky_quality", "12", CVAR_NONE};
47 cvar_t r_skyalpha = {"r_skyalpha", "1", CVAR_NONE};
48 cvar_t r_skyfog = {"r_skyfog","0.5",CVAR_NONE};
49
50 int skytexorder[6] = {0,2,1,3,4,5}; //for skybox
51
52 vec3_t skyclip[6] = {
53 {1,1,0},
54 {1,-1,0},
55 {0,-1,1},
56 {0,1,1},
57 {1,0,1},
58 {-1,0,1}
59 };
60
61 int st_to_vec[6][3] =
62 {
63 {3,-1,2},
64 {-3,1,2},
65 {1,3,2},
66 {-1,-3,2},
67 {-2,-1,3}, // straight up
68 {2,-1,-3} // straight down
69 };
70
71 int vec_to_st[6][3] =
72 {
73 {-2,3,1},
74 {2,3,-1},
75 {1,3,2},
76 {-1,3,-2},
77 {-2,-1,3},
78 {-2,1,-3}
79 };
80
81 float skyfog; // ericw
82
83 typedef struct
84 {
85 float position[3];
86 float texcoord1[2];
87 float texcoord2[2];
88 byte color[4];
89 } skylayervertex_t;
90
91 //==============================================================================
92 //
93 // INIT
94 //
95 //==============================================================================
96
97 /*
98 =============
99 Sky_LoadTexture
100
101 A sky texture is 256*128, with the left side being a masked overlay
102 ==============
103 */
Sky_LoadTexture(texture_t * mt)104 void Sky_LoadTexture (texture_t *mt)
105 {
106 char texturename[64];
107 unsigned x, y, p, r, g, b, count, halfwidth, *rgba;
108 byte *src, *front_data, *back_data;
109
110 if (mt->width != 256 || mt->height != 128)
111 {
112 Con_Warning ("Sky texture %s is %d x %d, expected 256 x 128\n", mt->name, mt->width, mt->height);
113 if (mt->width < 2 || mt->height < 1)
114 return;
115 }
116
117 halfwidth = mt->width / 2;
118 back_data = (byte *) Hunk_AllocName (halfwidth*mt->height*2, "skytex");
119 front_data = back_data + halfwidth*mt->height;
120 src = (byte *)(mt + 1);
121
122 // extract back layer and upload
123 for (y=0 ; y<mt->height ; y++)
124 memcpy (back_data + y*halfwidth, src + halfwidth + y*mt->width, halfwidth);
125
126 q_snprintf(texturename, sizeof(texturename), "%s:%s_back", loadmodel->name, mt->name);
127 solidskytexture = TexMgr_LoadImage (loadmodel, texturename, halfwidth, mt->height, SRC_INDEXED, back_data, "", (src_offset_t)back_data, TEXPREF_NONE);
128
129 // extract front layer and upload
130 r = g = b = count = 0;
131 for (y=0 ; y<mt->height ; src+=mt->width, front_data+=halfwidth, y++)
132 {
133 for (x=0 ; x<halfwidth ; x++)
134 {
135 p = src[x];
136 if (p == 0)
137 p = 255;
138 else
139 {
140 rgba = &d_8to24table[p];
141 r += ((byte *)rgba)[0];
142 g += ((byte *)rgba)[1];
143 b += ((byte *)rgba)[2];
144 count++;
145 }
146 front_data[x] = p;
147 }
148 }
149
150 front_data = back_data + halfwidth*mt->height;
151 q_snprintf(texturename, sizeof(texturename), "%s:%s_front", loadmodel->name, mt->name);
152 alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, halfwidth, mt->height, SRC_INDEXED, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA);
153
154 // calculate r_fastsky color based on average of all opaque foreground colors
155 skyflatcolor[0] = (float)r/(count*255);
156 skyflatcolor[1] = (float)g/(count*255);
157 skyflatcolor[2] = (float)b/(count*255);
158 }
159
160 /*
161 =============
162 Sky_LoadTextureQ64
163
164 Quake64 sky textures are 32*64
165 ==============
166 */
Sky_LoadTextureQ64(texture_t * mt)167 void Sky_LoadTextureQ64 (texture_t *mt)
168 {
169 char texturename[64];
170 unsigned i, p, r, g, b, count, halfheight, *rgba;
171 byte *front, *back, *front_rgba;
172
173 if (mt->width != 32 || mt->height != 64)
174 {
175 Con_DWarning ("Q64 sky texture %s is %d x %d, expected 32 x 64\n", mt->name, mt->width, mt->height);
176 if (mt->width < 1 || mt->height < 2)
177 return;
178 }
179
180 // pointers to both layer textures
181 halfheight = mt->height / 2;
182 front = (byte *)(mt+1);
183 back = (byte *)(mt+1) + mt->width*halfheight;
184 front_rgba = (byte *) Hunk_AllocName (4*mt->width*halfheight, "q64_skytex");
185
186 // Normal indexed texture for the back layer
187 q_snprintf(texturename, sizeof(texturename), "%s:%s_back", loadmodel->name, mt->name);
188 solidskytexture = TexMgr_LoadImage (loadmodel, texturename, mt->width, halfheight, SRC_INDEXED, back, "", (src_offset_t)back, TEXPREF_NONE);
189
190 // front layer, convert to RGBA and upload
191 p = r = g = b = count = 0;
192
193 for (i=mt->width*halfheight ; i!=0 ; i--)
194 {
195 rgba = &d_8to24table[*front++];
196
197 // RGB
198 front_rgba[p++] = ((byte*)rgba)[0];
199 front_rgba[p++] = ((byte*)rgba)[1];
200 front_rgba[p++] = ((byte*)rgba)[2];
201 // Alpha
202 front_rgba[p++] = 128; // this look ok to me!
203
204 // Fast sky
205 r += ((byte *)rgba)[0];
206 g += ((byte *)rgba)[1];
207 b += ((byte *)rgba)[2];
208 count++;
209 }
210
211 q_snprintf(texturename, sizeof(texturename), "%s:%s_front", loadmodel->name, mt->name);
212 alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, mt->width, halfheight, SRC_RGBA, front_rgba, "", (src_offset_t)front_rgba, TEXPREF_ALPHA);
213
214 // calculate r_fastsky color based on average of all opaque foreground colors
215 skyflatcolor[0] = (float)r/(count*255);
216 skyflatcolor[1] = (float)g/(count*255);
217 skyflatcolor[2] = (float)b/(count*255);
218 }
219
220 /*
221 ==================
222 Sky_LoadSkyBox
223 ==================
224 */
225 const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
Sky_LoadSkyBox(const char * name)226 void Sky_LoadSkyBox (const char *name)
227 {
228 int i, mark, width, height;
229 char filename[MAX_OSPATH];
230 byte *data;
231 qboolean nonefound = true;
232
233 if (strcmp(skybox_name, name) == 0)
234 return; //no change
235
236 //purge old textures
237 for (i=0; i<6; i++)
238 {
239 if (skybox_textures[i] && skybox_textures[i] != notexture)
240 TexMgr_FreeTexture (skybox_textures[i]);
241 skybox_textures[i] = NULL;
242 }
243
244 //turn off skybox if sky is set to ""
245 if (name[0] == 0)
246 {
247 skybox_name[0] = 0;
248 return;
249 }
250
251 //load textures
252 for (i=0; i<6; i++)
253 {
254 mark = Hunk_LowMark ();
255 q_snprintf (filename, sizeof(filename), "gfx/env/%s%s", name, suf[i]);
256 data = Image_LoadImage (filename, &width, &height);
257 if (data)
258 {
259 skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename, width, height, SRC_RGBA, data, filename, 0, TEXPREF_NONE);
260 nonefound = false;
261 }
262 else
263 {
264 Con_Printf ("Couldn't load %s\n", filename);
265 skybox_textures[i] = notexture;
266 }
267 Hunk_FreeToLowMark (mark);
268 }
269
270 if (nonefound) // go back to scrolling sky if skybox is totally missing
271 {
272 for (i=0; i<6; i++)
273 {
274 if (skybox_textures[i] && skybox_textures[i] != notexture)
275 TexMgr_FreeTexture (skybox_textures[i]);
276 skybox_textures[i] = NULL;
277 }
278 skybox_name[0] = 0;
279 return;
280 }
281
282 q_strlcpy(skybox_name, name, sizeof(skybox_name));
283 }
284
285 /*
286 =================
287 Sky_ClearAll
288
289 Called on map unload/game change to avoid keeping pointers to freed data
290 =================
291 */
Sky_ClearAll(void)292 void Sky_ClearAll (void)
293 {
294 int i;
295
296 skybox_name[0] = 0;
297 for (i=0; i<6; i++)
298 skybox_textures[i] = NULL;
299 solidskytexture = NULL;
300 alphaskytexture = NULL;
301 }
302
303 /*
304 =================
305 Sky_NewMap
306 =================
307 */
Sky_NewMap(void)308 void Sky_NewMap (void)
309 {
310 char key[128], value[4096];
311 const char *data;
312
313 skyfog = r_skyfog.value;
314
315 //
316 // read worldspawn (this is so ugly, and shouldn't it be done on the server?)
317 //
318 data = cl.worldmodel->entities;
319 if (!data)
320 return; //FIXME: how could this possibly ever happen? -- if there's no
321 // worldspawn then the sever wouldn't send the loadmap message to the client
322
323 data = COM_Parse(data);
324 if (!data) //should never happen
325 return; // error
326 if (com_token[0] != '{') //should never happen
327 return; // error
328 while (1)
329 {
330 data = COM_Parse(data);
331 if (!data)
332 return; // error
333 if (com_token[0] == '}')
334 break; // end of worldspawn
335 if (com_token[0] == '_')
336 q_strlcpy(key, com_token + 1, sizeof(key));
337 else
338 q_strlcpy(key, com_token, sizeof(key));
339 while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces
340 key[strlen(key)-1] = 0;
341 data = COM_Parse(data);
342 if (!data)
343 return; // error
344 q_strlcpy(value, com_token, sizeof(value));
345
346 if (!strcmp("sky", key))
347 Sky_LoadSkyBox(value);
348
349 if (!strcmp("skyfog", key))
350 skyfog = atof(value);
351
352 #if 1 //also accept non-standard keys
353 else if (!strcmp("skyname", key)) //half-life
354 Sky_LoadSkyBox(value);
355 else if (!strcmp("qlsky", key)) //quake lives
356 Sky_LoadSkyBox(value);
357 #endif
358 }
359 }
360
361 /*
362 =================
363 Sky_SkyCommand_f
364 =================
365 */
Sky_SkyCommand_f(void)366 void Sky_SkyCommand_f (void)
367 {
368 switch (Cmd_Argc())
369 {
370 case 1:
371 Con_Printf("\"sky\" is \"%s\"\n", skybox_name);
372 break;
373 case 2:
374 Sky_LoadSkyBox(Cmd_Argv(1));
375 break;
376 default:
377 Con_Printf("usage: sky <skyname>\n");
378 }
379 }
380
381 /*
382 ====================
383 R_SetSkyfog_f -- ericw
384 ====================
385 */
R_SetSkyfog_f(cvar_t * var)386 static void R_SetSkyfog_f (cvar_t *var)
387 {
388 // clear any skyfog setting from worldspawn
389 skyfog = var->value;
390 }
391
392 /*
393 =============
394 Sky_Init
395 =============
396 */
Sky_Init(void)397 void Sky_Init (void)
398 {
399 int i;
400
401 Cvar_RegisterVariable (&r_fastsky);
402 Cvar_RegisterVariable (&r_sky_quality);
403 Cvar_RegisterVariable (&r_skyalpha);
404 Cvar_RegisterVariable (&r_skyfog);
405 Cvar_SetCallback (&r_skyfog, R_SetSkyfog_f);
406
407 Cmd_AddCommand ("sky",Sky_SkyCommand_f);
408
409 skybox_name[0] = 0;
410 for (i=0; i<6; i++)
411 skybox_textures[i] = NULL;
412 }
413
414 //==============================================================================
415 //
416 // PROCESS SKY SURFS
417 //
418 //==============================================================================
419
420 /*
421 =================
422 Sky_ProjectPoly
423
424 update sky bounds
425 =================
426 */
Sky_ProjectPoly(int nump,vec3_t vecs)427 void Sky_ProjectPoly (int nump, vec3_t vecs)
428 {
429 int i,j;
430 vec3_t v, av;
431 float s, t, dv;
432 int axis;
433 float *vp;
434
435 // decide which face it maps to
436 VectorCopy (vec3_origin, v);
437 for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
438 {
439 VectorAdd (vp, v, v);
440 }
441 av[0] = fabs(v[0]);
442 av[1] = fabs(v[1]);
443 av[2] = fabs(v[2]);
444 if (av[0] > av[1] && av[0] > av[2])
445 {
446 if (v[0] < 0)
447 axis = 1;
448 else
449 axis = 0;
450 }
451 else if (av[1] > av[2] && av[1] > av[0])
452 {
453 if (v[1] < 0)
454 axis = 3;
455 else
456 axis = 2;
457 }
458 else
459 {
460 if (v[2] < 0)
461 axis = 5;
462 else
463 axis = 4;
464 }
465
466 // project new texture coords
467 for (i=0 ; i<nump ; i++, vecs+=3)
468 {
469 j = vec_to_st[axis][2];
470 if (j > 0)
471 dv = vecs[j - 1];
472 else
473 dv = -vecs[-j - 1];
474
475 j = vec_to_st[axis][0];
476 if (j < 0)
477 s = -vecs[-j -1] / dv;
478 else
479 s = vecs[j-1] / dv;
480 j = vec_to_st[axis][1];
481 if (j < 0)
482 t = -vecs[-j -1] / dv;
483 else
484 t = vecs[j-1] / dv;
485
486 if (s < skymins[0][axis])
487 skymins[0][axis] = s;
488 if (t < skymins[1][axis])
489 skymins[1][axis] = t;
490 if (s > skymaxs[0][axis])
491 skymaxs[0][axis] = s;
492 if (t > skymaxs[1][axis])
493 skymaxs[1][axis] = t;
494 }
495 }
496
497 /*
498 =================
499 Sky_ClipPoly
500 =================
501 */
Sky_ClipPoly(int nump,vec3_t vecs,int stage)502 void Sky_ClipPoly (int nump, vec3_t vecs, int stage)
503 {
504 float *norm;
505 float *v;
506 qboolean front, back;
507 float d, e;
508 float dists[MAX_CLIP_VERTS];
509 int sides[MAX_CLIP_VERTS];
510 vec3_t newv[2][MAX_CLIP_VERTS];
511 int newc[2];
512 int i, j;
513
514 if (nump > MAX_CLIP_VERTS-2)
515 Sys_Error ("Sky_ClipPoly: MAX_CLIP_VERTS");
516 if (stage == 6) // fully clipped
517 {
518 Sky_ProjectPoly (nump, vecs);
519 return;
520 }
521
522 front = back = false;
523 norm = skyclip[stage];
524 for (i=0, v = vecs ; i<nump ; i++, v+=3)
525 {
526 d = DotProduct (v, norm);
527 if (d > ON_EPSILON)
528 {
529 front = true;
530 sides[i] = SIDE_FRONT;
531 }
532 else if (d < ON_EPSILON)
533 {
534 back = true;
535 sides[i] = SIDE_BACK;
536 }
537 else
538 sides[i] = SIDE_ON;
539 dists[i] = d;
540 }
541
542 if (!front || !back)
543 { // not clipped
544 Sky_ClipPoly (nump, vecs, stage+1);
545 return;
546 }
547
548 // clip it
549 sides[i] = sides[0];
550 dists[i] = dists[0];
551 VectorCopy (vecs, (vecs+(i*3)) );
552 newc[0] = newc[1] = 0;
553
554 for (i=0, v = vecs ; i<nump ; i++, v+=3)
555 {
556 switch (sides[i])
557 {
558 case SIDE_FRONT:
559 VectorCopy (v, newv[0][newc[0]]);
560 newc[0]++;
561 break;
562 case SIDE_BACK:
563 VectorCopy (v, newv[1][newc[1]]);
564 newc[1]++;
565 break;
566 case SIDE_ON:
567 VectorCopy (v, newv[0][newc[0]]);
568 newc[0]++;
569 VectorCopy (v, newv[1][newc[1]]);
570 newc[1]++;
571 break;
572 }
573
574 if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
575 continue;
576
577 d = dists[i] / (dists[i] - dists[i+1]);
578 for (j=0 ; j<3 ; j++)
579 {
580 e = v[j] + d*(v[j+3] - v[j]);
581 newv[0][newc[0]][j] = e;
582 newv[1][newc[1]][j] = e;
583 }
584 newc[0]++;
585 newc[1]++;
586 }
587
588 // continue
589 Sky_ClipPoly (newc[0], newv[0][0], stage+1);
590 Sky_ClipPoly (newc[1], newv[1][0], stage+1);
591 }
592
593 /*
594 ================
595 Sky_ProcessPoly
596 ================
597 */
Sky_ProcessPoly(glpoly_t * p,float color[3])598 void Sky_ProcessPoly (glpoly_t *p, float color[3])
599 {
600 int i;
601 vec3_t verts[MAX_CLIP_VERTS];
602 float *poly_vert;
603
604 //draw it
605 DrawGLPoly(p, color, 1.0f);
606 rs_brushpasses++;
607
608 //update sky bounds
609 if (!r_fastsky.value)
610 {
611 for (i=0 ; i<p->numverts ; i++)
612 {
613 poly_vert = &p->verts[0][0] + (i * VERTEXSIZE);
614 VectorSubtract (poly_vert, r_origin, verts[i]);
615 }
616 Sky_ClipPoly (p->numverts, verts[0], 0);
617 }
618 }
619
620 /*
621 ================
622 Sky_ProcessTextureChains -- handles sky polys in world model
623 ================
624 */
Sky_ProcessTextureChains(float color[3])625 void Sky_ProcessTextureChains (float color[3])
626 {
627 int i;
628 msurface_t *s;
629 texture_t *t;
630
631 if (!r_drawworld_cheatsafe)
632 return;
633
634 for (i=0 ; i<cl.worldmodel->numtextures ; i++)
635 {
636 t = cl.worldmodel->textures[i];
637
638 if (!t || !t->texturechains[chain_world] || !(t->texturechains[chain_world]->flags & SURF_DRAWSKY))
639 continue;
640
641 for (s = t->texturechains[chain_world]; s; s = s->texturechain)
642 Sky_ProcessPoly (s->polys, color);
643 }
644 }
645
646 /*
647 ================
648 Sky_ProcessEntities -- handles sky polys on brush models
649 ================
650 */
Sky_ProcessEntities(float color[3])651 void Sky_ProcessEntities (float color[3])
652 {
653 entity_t *e;
654 msurface_t *s;
655 glpoly_t *p;
656 int i,j,k,mark;
657 float dot;
658 qboolean rotated;
659 vec3_t temp, forward, right, up;
660 float *s_poly_vert;
661 float *poly_vert;
662
663 if (!r_drawentities.value)
664 return;
665
666 for (i=0 ; i<cl_numvisedicts ; i++)
667 {
668 e = cl_visedicts[i];
669
670 if (e->model->type != mod_brush)
671 continue;
672
673 if (R_CullModelForEntity(e))
674 continue;
675
676 if (e->alpha == ENTALPHA_ZERO)
677 continue;
678
679 VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
680 if (e->angles[0] || e->angles[1] || e->angles[2])
681 {
682 rotated = true;
683 AngleVectors (e->angles, forward, right, up);
684 VectorCopy (modelorg, temp);
685 modelorg[0] = DotProduct (temp, forward);
686 modelorg[1] = -DotProduct (temp, right);
687 modelorg[2] = DotProduct (temp, up);
688 }
689 else
690 rotated = false;
691
692 s = &e->model->surfaces[e->model->firstmodelsurface];
693
694 for (j=0 ; j<e->model->nummodelsurfaces ; j++, s++)
695 {
696 if (s->flags & SURF_DRAWSKY)
697 {
698 dot = DotProduct (modelorg, s->plane->normal) - s->plane->dist;
699 if (((s->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
700 (!(s->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
701 {
702 //copy the polygon and translate manually, since Sky_ProcessPoly needs it to be in world space
703 mark = Hunk_LowMark();
704 p = (glpoly_t *) Hunk_Alloc (sizeof(*s->polys)); //FIXME: don't allocate for each poly
705 p->numverts = s->polys->numverts;
706 for (k=0; k<p->numverts; k++)
707 {
708 if (rotated)
709 {
710 p->verts[k][0] = e->origin[0] + s->polys->verts[k][0] * forward[0]
711 - s->polys->verts[k][1] * right[0]
712 + s->polys->verts[k][2] * up[0];
713 p->verts[k][1] = e->origin[1] + s->polys->verts[k][0] * forward[1]
714 - s->polys->verts[k][1] * right[1]
715 + s->polys->verts[k][2] * up[1];
716 p->verts[k][2] = e->origin[2] + s->polys->verts[k][0] * forward[2]
717 - s->polys->verts[k][1] * right[2]
718 + s->polys->verts[k][2] * up[2];
719 }
720 else
721 {
722 s_poly_vert = &s->polys->verts[0][0] + (k * VERTEXSIZE);
723 poly_vert = &p->verts[0][0] + (k * VERTEXSIZE);
724 VectorAdd(s_poly_vert, e->origin, poly_vert);
725 }
726 }
727 Sky_ProcessPoly (p, color);
728 Hunk_FreeToLowMark (mark);
729 }
730 }
731 }
732 }
733 }
734
735 //==============================================================================
736 //
737 // RENDER SKYBOX
738 //
739 //==============================================================================
740
741 /*
742 ==============
743 Sky_EmitSkyBoxVertex
744 ==============
745 */
Sky_EmitSkyBoxVertex(basicvertex_t * vertex,float s,float t,int axis)746 void Sky_EmitSkyBoxVertex (basicvertex_t * vertex, float s, float t, int axis)
747 {
748 vec3_t v, b;
749 int j, k;
750 float w, h;
751
752 b[0] = s * gl_farclip.value / sqrt(3.0);
753 b[1] = t * gl_farclip.value / sqrt(3.0);
754 b[2] = gl_farclip.value / sqrt(3.0);
755
756 for (j=0 ; j<3 ; j++)
757 {
758 k = st_to_vec[axis][j];
759 if (k < 0)
760 v[j] = -b[-k - 1];
761 else
762 v[j] = b[k - 1];
763 v[j] += r_origin[j];
764 }
765
766 // convert from range [-1,1] to [0,1]
767 s = (s+1)*0.5;
768 t = (t+1)*0.5;
769
770 // avoid bilerp seam
771 w = skybox_textures[skytexorder[axis]]->width;
772 h = skybox_textures[skytexorder[axis]]->height;
773 s = s * (w-1)/w + 0.5/w;
774 t = t * (h-1)/h + 0.5/h;
775
776 t = 1.0 - t;
777
778 vertex->position[0] = v[0];
779 vertex->position[1] = v[1];
780 vertex->position[2] = v[2];
781
782 vertex->texcoord[0] = s;
783 vertex->texcoord[1] = t;
784
785 vertex->color[0] = 255;
786 vertex->color[1] = 255;
787 vertex->color[2] = 255;
788 vertex->color[3] = 255;
789 }
790
791 /*
792 ==============
793 Sky_DrawSkyBox
794
795 FIXME: eliminate cracks by adding an extra vert on tjuncs
796 ==============
797 */
Sky_DrawSkyBox(void)798 void Sky_DrawSkyBox (void)
799 {
800 int i;
801
802 for (i=0 ; i<6 ; i++)
803 {
804 if (skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i])
805 continue;
806
807 vkCmdBindDescriptorSets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_pipeline_layout.handle, 0, 1, &skybox_textures[skytexorder[i]]->descriptor_set, 0, NULL);
808
809 VkBuffer buffer;
810 VkDeviceSize buffer_offset;
811 basicvertex_t * vertices = (basicvertex_t*)R_VertexAllocate(4 * sizeof(basicvertex_t), &buffer, &buffer_offset);
812
813 #if 1 //FIXME: this is to avoid tjunctions until i can do it the right way
814 skymins[0][i] = -1;
815 skymins[1][i] = -1;
816 skymaxs[0][i] = 1;
817 skymaxs[1][i] = 1;
818 #endif
819 Sky_EmitSkyBoxVertex (vertices + 0, skymins[0][i], skymins[1][i], i);
820 Sky_EmitSkyBoxVertex (vertices + 1, skymins[0][i], skymaxs[1][i], i);
821 Sky_EmitSkyBoxVertex (vertices + 2, skymaxs[0][i], skymaxs[1][i], i);
822 Sky_EmitSkyBoxVertex (vertices + 3, skymaxs[0][i], skymins[1][i], i);
823
824 vkCmdBindVertexBuffers(vulkan_globals.command_buffer, 0, 1, &buffer, &buffer_offset);
825 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.sky_box_pipeline);
826 vkCmdDrawIndexed(vulkan_globals.command_buffer, 6, 1, 0, 0, 0);
827
828 rs_skypolys++;
829 rs_skypasses++;
830 }
831 }
832
833 //==============================================================================
834 //
835 // RENDER CLOUDS
836 //
837 //==============================================================================
838
839 /*
840 ==============
841 Sky_SetBoxVert
842 ==============
843 */
Sky_SetBoxVert(float s,float t,int axis,vec3_t v)844 void Sky_SetBoxVert (float s, float t, int axis, vec3_t v)
845 {
846 vec3_t b;
847 int j, k;
848
849 b[0] = s * gl_farclip.value / sqrt(3.0);
850 b[1] = t * gl_farclip.value / sqrt(3.0);
851 b[2] = gl_farclip.value / sqrt(3.0);
852
853 for (j=0 ; j<3 ; j++)
854 {
855 k = st_to_vec[axis][j];
856 if (k < 0)
857 v[j] = -b[-k - 1];
858 else
859 v[j] = b[k - 1];
860 v[j] += r_origin[j];
861 }
862 }
863
864 /*
865 =============
866 Sky_GetTexCoord
867 =============
868 */
Sky_GetTexCoord(vec3_t v,float speed,float * s,float * t)869 void Sky_GetTexCoord (vec3_t v, float speed, float *s, float *t)
870 {
871 vec3_t dir;
872 float length, scroll;
873
874 VectorSubtract (v, r_origin, dir);
875 dir[2] *= 3; // flatten the sphere
876
877 length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
878 length = sqrt (length);
879 length = 6*63/length;
880
881 scroll = cl.time*speed;
882 scroll -= (int)scroll & ~127;
883
884 *s = (scroll + dir[0] * length) * (1.0/128);
885 *t = (scroll + dir[1] * length) * (1.0/128);
886 }
887
888 /*
889 ===============
890 Sky_DrawFaceQuad
891 ===============
892 */
Sky_DrawFaceQuad(glpoly_t * p,float alpha)893 void Sky_DrawFaceQuad (glpoly_t *p, float alpha)
894 {
895 float *v;
896 int i;
897
898 VkBuffer vertex_buffer;
899 VkDeviceSize vertex_buffer_offset;
900 skylayervertex_t * vertices = (skylayervertex_t*)R_VertexAllocate(4 * sizeof(skylayervertex_t), &vertex_buffer, &vertex_buffer_offset);
901
902 for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE)
903 {
904 vertices[i].position[0] = v[0];
905 vertices[i].position[1] = v[1];
906 vertices[i].position[2] = v[2];
907
908 Sky_GetTexCoord (v, 8, &vertices[i].texcoord1[0], &vertices[i].texcoord1[1]);
909 Sky_GetTexCoord (v, 16, &vertices[i].texcoord2[0], &vertices[i].texcoord2[1]);
910
911 vertices[i].color[0] = 255;
912 vertices[i].color[1] = 255;
913 vertices[i].color[2] = 255;
914 vertices[i].color[3] = alpha * 255.0f;
915 }
916
917 vkCmdBindVertexBuffers(vulkan_globals.command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset);
918 vkCmdDrawIndexed(vulkan_globals.command_buffer, 6, 1, 0, 0, 0);
919
920 rs_skypolys++;
921 rs_skypasses++;
922 }
923
924 /*
925 ==============
926 Sky_DrawFace
927 ==============
928 */
929
Sky_DrawFace(int axis,float alpha)930 void Sky_DrawFace (int axis, float alpha)
931 {
932 glpoly_t *p;
933 vec3_t verts[4];
934 int i, j, start;
935 float di,qi,dj,qj;
936 vec3_t up, right, temp, temp2;
937
938 Sky_SetBoxVert(-1.0, -1.0, axis, verts[0]);
939 Sky_SetBoxVert(-1.0, 1.0, axis, verts[1]);
940 Sky_SetBoxVert(1.0, 1.0, axis, verts[2]);
941 Sky_SetBoxVert(1.0, -1.0, axis, verts[3]);
942
943 start = Hunk_LowMark ();
944 p = (glpoly_t *) Hunk_Alloc(sizeof(glpoly_t));
945
946 VectorSubtract(verts[2],verts[3],up);
947 VectorSubtract(verts[2],verts[1],right);
948
949 di = q_max((int)r_sky_quality.value, 1);
950 qi = 1.0 / di;
951 dj = (axis < 4) ? di*2 : di; //subdivide vertically more than horizontally on skybox sides
952 qj = 1.0 / dj;
953
954 for (i=0; i<di; i++)
955 {
956 for (j=0; j<dj; j++)
957 {
958 if (i*qi < skymins[0][axis]/2+0.5 - qi || i*qi > skymaxs[0][axis]/2+0.5 ||
959 j*qj < skymins[1][axis]/2+0.5 - qj || j*qj > skymaxs[1][axis]/2+0.5)
960 continue;
961
962 //if (i&1 ^ j&1) continue; //checkerboard test
963 VectorScale (right, qi*i, temp);
964 VectorScale (up, qj*j, temp2);
965 VectorAdd(temp,temp2,temp);
966 VectorAdd(verts[0],temp,p->verts[0]);
967
968 VectorScale (up, qj, temp);
969 VectorAdd (p->verts[0],temp,p->verts[1]);
970
971 VectorScale (right, qi, temp);
972 VectorAdd (p->verts[1],temp,p->verts[2]);
973
974 VectorAdd (p->verts[0],temp,p->verts[3]);
975
976 Sky_DrawFaceQuad (p, alpha);
977 }
978 }
979 Hunk_FreeToLowMark (start);
980 }
981
982 /*
983 ==============
984 Sky_DrawSkyLayers
985
986 draws the old-style scrolling cloud layers
987 ==============
988 */
Sky_DrawSkyLayers(void)989 void Sky_DrawSkyLayers (void)
990 {
991 int i;
992 if (!solidskytexture || !alphaskytexture)
993 return;
994
995 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.sky_layer_pipeline);
996
997 VkDescriptorSet descriptor_sets[2] = { solidskytexture->descriptor_set, alphaskytexture->descriptor_set };
998 vkCmdBindDescriptorSets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.sky_layer_pipeline.layout.handle, 0, 2, descriptor_sets, 0, NULL);
999
1000 for (i=0 ; i<6 ; i++)
1001 if (skymins[0][i] < skymaxs[0][i] && skymins[1][i] < skymaxs[1][i])
1002 Sky_DrawFace (i, r_skyalpha.value);
1003 }
1004
1005 /*
1006 ==============
1007 Sky_DrawSky
1008
1009 called once per frame before drawing anything else
1010 ==============
1011 */
Sky_DrawSky(void)1012 void Sky_DrawSky (void)
1013 {
1014 int i;
1015
1016 if (r_lightmap_cheatsafe)
1017 return;
1018
1019 R_BeginDebugUtilsLabel ("Sky");
1020
1021 const qboolean slow_sky = !r_fastsky.value && !(Fog_GetDensity() > 0 && skyfog >= 1);
1022
1023 //
1024 // reset sky bounds
1025 //
1026 for (i=0 ; i<6 ; i++)
1027 {
1028 skymins[0][i] = skymins[1][i] = FLT_MAX;
1029 skymaxs[0][i] = skymaxs[1][i] = -FLT_MAX;
1030 }
1031
1032 // With slow sky we first write stencil for the part of the screen that is covered by sky geometry and passes the depth test
1033 // Sky_DrawSkyBox/Sky_DrawSkyLayers then only fill the parts that had stencil written
1034 if(slow_sky)
1035 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.sky_stencil_pipeline);
1036 else
1037 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.sky_color_pipeline);
1038 vkCmdBindIndexBuffer(vulkan_globals.command_buffer, vulkan_globals.fan_index_buffer, 0, VK_INDEX_TYPE_UINT16);
1039
1040 //
1041 // process world and bmodels: draw flat-shaded sky surfs, and update skybounds
1042 //
1043 Fog_DisableGFog ();
1044
1045 float * color;
1046 if (Fog_GetDensity() > 0)
1047 color = Fog_GetColor();
1048 else
1049 color = skyflatcolor;
1050
1051 Sky_ProcessTextureChains (color);
1052 Sky_ProcessEntities (color);
1053
1054 //
1055 // render slow sky: cloud layers or skybox
1056 //
1057 if (slow_sky)
1058 {
1059 float fog_density = (Fog_GetDensity() > 0) ? skyfog : 0.0f;
1060 float * fog_color = Fog_GetColor();
1061 float fog_values[4] = { CLAMP( 0.0f, fog_color[0], 1.0f ), CLAMP(0.0f, fog_color[1], 1.0f), CLAMP(0.0f, fog_color[2], 1.0f), fog_density };
1062 R_PushConstants(VK_SHADER_STAGE_ALL_GRAPHICS, 16 * sizeof(float), 4 * sizeof(float), fog_values);
1063
1064 if (skybox_name[0])
1065 Sky_DrawSkyBox ();
1066 else
1067 Sky_DrawSkyLayers ();
1068 }
1069
1070 Fog_EnableGFog ();
1071
1072 R_EndDebugUtilsLabel ();
1073 }
1074