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