Tomaz added support for loading a cubemap as skybox
[xonotic/darkplaces.git] / r_sky.c
1
2 #include "quakedef.h"
3 #include "image.h"
4
5 // FIXME: fix skybox after vid_restart
6 cvar_t r_sky = {CVAR_SAVE, "r_sky", "1"};
7 qboolean skyavailable_quake;
8 qboolean skyavailable_box;
9 int skyrendernow;
10 int skyrendermasked;
11
12 static rtexture_t *solidskytexture;
13 static rtexture_t *alphaskytexture;
14 static int skyrendersphere;
15 static int skyrenderbox;
16 static rtexturepool_t *skytexturepool;
17 static char skyname[256];
18
19 typedef struct suffixinfo_s
20 {
21         char *suffix;
22         qboolean flipx, flipy, flipdiagonal;
23 }
24 suffixinfo_t;
25 static suffixinfo_t suffix[2][6] =
26 {
27         {
28                 {"rt", false, false, false},
29                 {"bk", false, false, false},
30                 {"lf", false, false, false},
31                 {"ft", false, false, false},
32                 {"up", false, false, false},
33                 {"dn", false, false, false},
34         },
35         {
36                 {"px", false,  true,  true},
37                 {"ny", false, false, false},
38                 {"nx",  true, false,  true},
39                 {"py",  true,  true, false},
40                 {"pz", false,  true,  true},
41                 {"nz", false,  true,  true}
42         }
43 };
44
45 static rtexture_t *skyboxside[6];
46
47 void R_SkyStartFrame(void)
48 {
49         skyrendernow = false;
50         skyrendersphere = false;
51         skyrenderbox = false;
52         skyrendermasked = false;
53         if (r_sky.integer && !fogenabled)
54         {
55                 if (skyavailable_box)
56                         skyrenderbox = true;
57                 else if (skyavailable_quake)
58                         skyrendersphere = true;
59                 // for depth-masked sky, render the sky on the first sky surface encountered
60                 skyrendernow = true;
61                 skyrendermasked = true;
62         }
63 }
64
65 /*
66 ==================
67 R_SetSkyBox
68 ==================
69 */
70 void R_UnloadSkyBox(void)
71 {
72         int i;
73         for (i = 0;i < 6;i++)
74         {
75                 if (skyboxside[i])
76                         R_FreeTexture(skyboxside[i]);
77                 skyboxside[i] = NULL;
78         }
79 }
80
81 void R_LoadSkyBox(void)
82 {
83         int i, j;
84         int indices[4] = {0,1,2,3};
85         char name[1024];
86         qbyte *image_rgba;
87         qbyte *temp;
88         R_UnloadSkyBox();
89         if (!skyname[0])
90                 return;
91
92         for (j=0; j<2; j++)
93         {
94                 for (i=0; i<6; i++)
95                 {
96                         if (snprintf(name, sizeof(name), "%s_%s", skyname, suffix[j][i].suffix) >= (int)sizeof(name) || !(image_rgba = loadimagepixels(name, false, 0, 0)))
97                         {
98                                 if (snprintf(name, sizeof(name), "%s%s", skyname, suffix[j][i].suffix) >= (int)sizeof(name) || !(image_rgba = loadimagepixels(name, false, 0, 0)))
99                                 {
100                                         if (snprintf(name, sizeof(name), "env/%s%s", skyname, suffix[j][i].suffix) >= (int)sizeof(name) || !(image_rgba = loadimagepixels(name, false, 0, 0)))
101                                         {
102                                                 if (snprintf(name, sizeof(name), "gfx/env/%s%s", skyname, suffix[j][i].suffix) >= (int)sizeof(name) || !(image_rgba = loadimagepixels(name, false, 0, 0)))
103                                                         continue;
104                                         }
105                                 }
106                         }
107                         temp = Mem_Alloc(tempmempool, image_width*image_height*4);
108                         Image_CopyMux (temp, image_rgba, image_width, image_height, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, indices);
109                         skyboxside[i] = R_LoadTexture2D(skytexturepool, va("skyboxside%d", i), image_width, image_height, temp, TEXTYPE_RGBA, TEXF_CLAMP | TEXF_PRECACHE, NULL);
110                         Mem_Free(image_rgba);
111                         Mem_Free(temp);
112                 }
113
114                 for (i=0; i<6; i++)
115                 {
116                         if (skyboxside[i] == NULL)
117                         {
118                                 R_UnloadSkyBox();
119                                 break;
120                         }
121                 }
122
123                 if (skyboxside[0] != NULL)
124                         return;
125         }
126 }
127
128 int R_SetSkyBox(const char *sky)
129 {
130         if (strcmp(sky, skyname) == 0) // no change
131                 return true;
132
133         if (strlen(sky) > 1000)
134         {
135                 Con_Printf("sky name too long (%i, max is 1000)\n", strlen(sky));
136                 return false;
137         }
138
139         skyavailable_box = false;
140         strcpy(skyname, sky);
141
142         R_UnloadSkyBox();
143         R_LoadSkyBox();
144
145         if (!skyname[0])
146                 return true;
147
148         if (skyboxside[0] || skyboxside[1] || skyboxside[2] || skyboxside[3] || skyboxside[4] || skyboxside[5])
149         {
150                 skyavailable_box = true;
151                 return true;
152         }
153         return false;
154 }
155
156 // LordHavoc: added LoadSky console command
157 void LoadSky_f (void)
158 {
159         switch (Cmd_Argc())
160         {
161         case 1:
162                 if (skyname[0])
163                         Con_Printf("current sky: %s\n", skyname);
164                 else
165                         Con_Print("no skybox has been set\n");
166                 break;
167         case 2:
168                 if (R_SetSkyBox(Cmd_Argv(1)))
169                 {
170                         if (skyname[0])
171                                 Con_Printf("skybox set to %s\n", skyname);
172                         else
173                                 Con_Print("skybox disabled\n");
174                 }
175                 else
176                         Con_Printf("failed to load skybox %s\n", Cmd_Argv(1));
177                 break;
178         default:
179                 Con_Print("usage: loadsky skyname\n");
180                 break;
181         }
182 }
183
184 float skyboxvertex3f[6*4*3] =
185 {
186         // skyside[0]
187          16, -16,  16,
188          16, -16, -16,
189          16,  16, -16,
190          16,  16,  16,
191         // skyside[1]
192         -16, -16,  16,
193         -16, -16, -16,
194          16, -16, -16,
195          16, -16,  16,
196         // skyside[2]
197         -16,  16,  16,
198         -16,  16, -16,
199         -16, -16, -16,
200         -16, -16,  16,
201         // skyside[3]
202          16,  16,  16,
203          16,  16, -16,
204         -16,  16, -16,
205         -16,  16,  16,
206         // skyside[4]
207         -16, -16,  16,
208          16, -16,  16,
209          16,  16,  16,
210         -16,  16,  16,
211         // skyside[5]
212          16, -16, -16,
213         -16, -16, -16,
214         -16,  16, -16,
215          16,  16, -16
216 };
217
218 float skyboxtexcoord2f[6*4*2] =
219 {
220         // skyside[0]
221         0, 0,
222         0, 1,
223         1, 1,
224         1, 0,
225         // skyside[1]
226         0, 0,
227         0, 1,
228         1, 1,
229         1, 0,
230         // skyside[2]
231         0, 0,
232         0, 1,
233         1, 1,
234         1, 0,
235         // skyside[3]
236         0, 0,
237         0, 1,
238         1, 1,
239         1, 0,
240         // skyside[4]
241         0, 0,
242         0, 1,
243         1, 1,
244         1, 0,
245         // skyside[5]
246         0, 0,
247         0, 1,
248         1, 1,
249         1, 0
250 };
251
252 int skyboxelements[6*2*3] =
253 {
254         // skyside[3]
255          0,  1,  2,
256          0,  2,  3,
257         // skyside[1]
258          4,  5,  6,
259          4,  6,  7,
260         // skyside[0]
261          8,  9, 10,
262          8, 10, 11,
263         // skyside[2]
264         12, 13, 14,
265         12, 14, 15,
266         // skyside[4]
267         16, 17, 18,
268         16, 18, 19,
269         // skyside[5]
270         20, 21, 22,
271         20, 22, 23
272 };
273
274 static void R_SkyBox(void)
275 {
276         int i;
277         rmeshstate_t m;
278         GL_Color(1, 1, 1, 1);
279         memset(&m, 0, sizeof(m));
280         GL_BlendFunc(GL_ONE, GL_ZERO);
281         GL_DepthMask(true);
282         GL_DepthTest(false); // don't modify or read zbuffer
283         m.pointer_vertex = skyboxvertex3f;
284         m.pointer_texcoord[0] = skyboxtexcoord2f;
285         GL_LockArrays(0, 6*4);
286         for (i = 0;i < 6;i++)
287         {
288                 m.tex[0] = R_GetTexture(skyboxside[i]);
289                 R_Mesh_State(&m);
290                 R_Mesh_Draw(6*4, 2, skyboxelements + i * 6);
291         }
292         GL_LockArrays(0, 0);
293 }
294
295 #define skygridx 32
296 #define skygridx1 (skygridx + 1)
297 #define skygridxrecip (1.0f / (skygridx))
298 #define skygridy 32
299 #define skygridy1 (skygridy + 1)
300 #define skygridyrecip (1.0f / (skygridy))
301 #define skysphere_numverts (skygridx1 * skygridy1)
302 #define skysphere_numtriangles (skygridx * skygridy * 2)
303 static float skysphere_vertex3f[skysphere_numverts * 3];
304 static float skysphere_texcoord2f[skysphere_numverts * 2];
305 static int skysphere_element3i[skysphere_numtriangles * 3];
306
307 static void skyspherecalc(void)
308 {
309         int i, j, *e;
310         float a, b, x, ax, ay, v[3], length, *vertex3f, *texcoord2f;
311         float dx, dy, dz;
312         dx = 16;
313         dy = 16;
314         dz = 16 / 3;
315         vertex3f = skysphere_vertex3f;
316         texcoord2f = skysphere_texcoord2f;
317         for (j = 0;j <= skygridy;j++)
318         {
319                 a = j * skygridyrecip;
320                 ax = cos(a * M_PI * 2);
321                 ay = -sin(a * M_PI * 2);
322                 for (i = 0;i <= skygridx;i++)
323                 {
324                         b = i * skygridxrecip;
325                         x = cos((b + 0.5) * M_PI);
326                         v[0] = ax*x * dx;
327                         v[1] = ay*x * dy;
328                         v[2] = -sin((b + 0.5) * M_PI) * dz;
329                         length = 3.0f / sqrt(v[0]*v[0]+v[1]*v[1]+(v[2]*v[2]*9));
330                         *texcoord2f++ = v[0] * length;
331                         *texcoord2f++ = v[1] * length;
332                         *vertex3f++ = v[0];
333                         *vertex3f++ = v[1];
334                         *vertex3f++ = v[2];
335                 }
336         }
337         e = skysphere_element3i;
338         for (j = 0;j < skygridy;j++)
339         {
340                 for (i = 0;i < skygridx;i++)
341                 {
342                         *e++ =  j      * skygridx1 + i;
343                         *e++ =  j      * skygridx1 + i + 1;
344                         *e++ = (j + 1) * skygridx1 + i;
345
346                         *e++ =  j      * skygridx1 + i + 1;
347                         *e++ = (j + 1) * skygridx1 + i + 1;
348                         *e++ = (j + 1) * skygridx1 + i;
349                 }
350         }
351 }
352
353 static void R_SkySphere(void)
354 {
355         float speedscale;
356         static qboolean skysphereinitialized = false;
357         rmeshstate_t m;
358         matrix4x4_t scroll1matrix, scroll2matrix;
359         if (!skysphereinitialized)
360         {
361                 skysphereinitialized = true;
362                 skyspherecalc();
363         }
364
365         // scroll speed for upper layer
366         speedscale = cl.time*8.0/128.0;
367         // wrap the scroll just to be extra kind to float accuracy
368         speedscale -= (int)speedscale;
369
370         // scroll the lower cloud layer twice as fast (just like quake did)
371         Matrix4x4_CreateTranslate(&scroll1matrix, speedscale, speedscale, 0);
372         Matrix4x4_CreateTranslate(&scroll2matrix, speedscale * 2, speedscale * 2, 0);
373
374         GL_Color(1, 1, 1, 1);
375         GL_BlendFunc(GL_ONE, GL_ZERO);
376         GL_DepthMask(true);
377         GL_DepthTest(false); // don't modify or read zbuffer
378         memset(&m, 0, sizeof(m));
379         m.pointer_vertex = skysphere_vertex3f;
380         m.tex[0] = R_GetTexture(solidskytexture);
381         m.pointer_texcoord[0] = skysphere_texcoord2f;
382         m.texmatrix[0] = scroll1matrix;
383         if (r_textureunits.integer >= 2)
384         {
385                 // one pass using GL_DECAL or GL_INTERPOLATE_ARB for alpha layer
386                 m.tex[1] = R_GetTexture(alphaskytexture);
387                 m.texcombinergb[1] = gl_combine.integer ? GL_INTERPOLATE_ARB : GL_DECAL;
388                 m.pointer_texcoord[1] = skysphere_texcoord2f;
389                 m.texmatrix[1] = scroll2matrix;
390                 R_Mesh_State(&m);
391                 GL_LockArrays(0, skysphere_numverts);
392                 R_Mesh_Draw(skysphere_numverts, skysphere_numtriangles, skysphere_element3i);
393                 GL_LockArrays(0, 0);
394         }
395         else
396         {
397                 // two pass
398                 R_Mesh_State(&m);
399                 GL_LockArrays(0, skysphere_numverts);
400                 R_Mesh_Draw(skysphere_numverts, skysphere_numtriangles, skysphere_element3i);
401                 GL_LockArrays(0, 0);
402
403                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
404                 m.tex[0] = R_GetTexture(alphaskytexture);
405                 m.texmatrix[0] = scroll2matrix;
406                 R_Mesh_State(&m);
407                 GL_LockArrays(0, skysphere_numverts);
408                 R_Mesh_Draw(skysphere_numverts, skysphere_numtriangles, skysphere_element3i);
409                 GL_LockArrays(0, 0);
410         }
411 }
412
413 void R_Sky(void)
414 {
415         matrix4x4_t skymatrix;
416         if (skyrendermasked)
417         {
418                 Matrix4x4_CreateTranslate(&skymatrix, r_vieworigin[0], r_vieworigin[1], r_vieworigin[2]);
419                 R_Mesh_Matrix(&skymatrix);
420                 if (skyrendersphere)
421                 {
422                         // this does not modify depth buffer
423                         R_SkySphere();
424                 }
425                 else if (skyrenderbox)
426                 {
427                         // this does not modify depth buffer
428                         R_SkyBox();
429                 }
430                 /* this will be skyroom someday
431                 else
432                 {
433                         // this modifies the depth buffer so we have to clear it afterward
434                         //R_SkyRoom();
435                         // clear the depthbuffer that was used while rendering the skyroom
436                         //GL_Clear(GL_DEPTH_BUFFER_BIT);
437                 }
438                 */
439         }
440 }
441
442 //===============================================================
443
444 /*
445 =============
446 R_InitSky
447
448 A sky texture is 256*128, with the right side being a masked overlay
449 ==============
450 */
451 void R_InitSky (qbyte *src, int bytesperpixel)
452 {
453         int i, j;
454         unsigned solidpixels[128*128], alphapixels[128*128];
455
456         skyavailable_quake = true;
457
458         // flush skytexturepool so we won't build up a leak from uploading textures multiple times
459         R_FreeTexturePool(&skytexturepool);
460         skytexturepool = R_AllocTexturePool();
461         solidskytexture = NULL;
462         alphaskytexture = NULL;
463
464         if (bytesperpixel == 4)
465         {
466                 for (i = 0;i < 128;i++)
467                 {
468                         for (j = 0;j < 128;j++)
469                         {
470                                 solidpixels[(i*128) + j] = ((unsigned *)src)[i*256+j+128];
471                                 alphapixels[(i*128) + j] = ((unsigned *)src)[i*256+j];
472                         }
473                 }
474         }
475         else
476         {
477                 // make an average value for the back to avoid
478                 // a fringe on the top level
479                 int p, r, g, b;
480                 union
481                 {
482                         unsigned int i;
483                         unsigned char b[4];
484                 }
485                 rgba;
486                 r = g = b = 0;
487                 for (i = 0;i < 128;i++)
488                 {
489                         for (j = 0;j < 128;j++)
490                         {
491                                 rgba.i = palette_complete[src[i*256 + j + 128]];
492                                 r += rgba.b[0];
493                                 g += rgba.b[1];
494                                 b += rgba.b[2];
495                         }
496                 }
497                 rgba.b[0] = r/(128*128);
498                 rgba.b[1] = g/(128*128);
499                 rgba.b[2] = b/(128*128);
500                 rgba.b[3] = 0;
501                 for (i = 0;i < 128;i++)
502                 {
503                         for (j = 0;j < 128;j++)
504                         {
505                                 solidpixels[(i*128) + j] = palette_complete[src[i*256 + j + 128]];
506                                 alphapixels[(i*128) + j] = (p = src[i*256 + j]) ? palette_complete[p] : rgba.i;
507                         }
508                 }
509         }
510
511         solidskytexture = R_LoadTexture2D(skytexturepool, "sky_solidtexture", 128, 128, (qbyte *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
512         alphaskytexture = R_LoadTexture2D(skytexturepool, "sky_alphatexture", 128, 128, (qbyte *) alphapixels, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
513 }
514
515 void R_ResetQuakeSky(void)
516 {
517         skyavailable_quake = false;
518 }
519
520 void R_ResetSkyBox(void)
521 {
522         skyboxside[0] = skyboxside[1] = skyboxside[2] = skyboxside[3] = skyboxside[4] = skyboxside[5] = NULL;
523         skyname[0] = 0;
524         skyavailable_box = false;
525 }
526
527 static void r_sky_start(void)
528 {
529         skytexturepool = R_AllocTexturePool();
530         solidskytexture = NULL;
531         alphaskytexture = NULL;
532         R_LoadSkyBox();
533 }
534
535 static void r_sky_shutdown(void)
536 {
537         R_UnloadSkyBox();
538         R_FreeTexturePool(&skytexturepool);
539         solidskytexture = NULL;
540         alphaskytexture = NULL;
541 }
542
543 static void r_sky_newmap(void)
544 {
545 }
546
547 void R_Sky_Init(void)
548 {
549         Cmd_AddCommand ("loadsky", &LoadSky_f);
550         Cvar_RegisterVariable (&r_sky);
551         R_ResetSkyBox();
552         R_ResetQuakeSky();
553         R_RegisterModule("R_Sky", r_sky_start, r_sky_shutdown, r_sky_newmap);
554 }
555