added cl_screen.c/h (eventually most 2D stuff should be moved here)
[divverent/darkplaces.git] / r_sky.c
1 #include "quakedef.h"
2
3 void LoadSky_f(void);
4
5 cvar_t r_skyquality = {CVAR_SAVE, "r_skyquality", "2"};
6 cvar_t r_mergesky = {CVAR_SAVE, "r_mergesky", "0"};
7 cvar_t r_skyflush = {0, "r_skyflush", "0"};
8
9 static char skyworldname[1024];
10 rtexture_t *mergeskytexture;
11 rtexture_t *solidskytexture;
12 rtexture_t *alphaskytexture;
13 static qboolean skyavailable_quake;
14 static qboolean skyavailable_box;
15 static rtexturepool_t *skytexturepool;
16
17 int skyrendernow;
18 int skyrendermasked;
19 int skyrenderglquake;
20
21 static void R_BuildSky (int scrollupper, int scrolllower);
22
23 static void r_sky_start(void)
24 {
25         skytexturepool = R_AllocTexturePool();
26         mergeskytexture = NULL;
27         solidskytexture = NULL;
28         alphaskytexture = NULL;
29 }
30
31 static void r_sky_shutdown(void)
32 {
33         R_FreeTexturePool(&skytexturepool);
34         mergeskytexture = NULL;
35         solidskytexture = NULL;
36         alphaskytexture = NULL;
37 }
38
39 int R_SetSkyBox(char *sky);
40
41 static void r_sky_newmap(void)
42 {
43         skyavailable_quake = false;
44         if (!strcmp(skyworldname, cl.worldmodel->name))
45                 skyavailable_quake = true;
46 }
47
48 void R_Sky_Init(void)
49 {
50         Cmd_AddCommand ("loadsky", &LoadSky_f);
51         Cvar_RegisterVariable (&r_skyquality);
52         Cvar_RegisterVariable (&r_mergesky);
53         Cvar_RegisterVariable (&r_skyflush);
54         R_RegisterModule("R_Sky", r_sky_start, r_sky_shutdown, r_sky_newmap);
55 }
56
57 static int skyrendersphere;
58 static int skyrenderbox;
59
60 void R_SkyStartFrame(void)
61 {
62         skyrendernow = false;
63         skyrendersphere = false;
64         skyrenderbox = false;
65         skyrenderglquake = false;
66         skyrendermasked = false;
67         if (r_skyquality.integer >= 1 && !fogenabled)
68         {
69                 if (skyavailable_box)
70                         skyrenderbox = true;
71                 else if (skyavailable_quake)
72                 {
73                         switch(r_skyquality.integer)
74                         {
75                         case 1:
76                                 skyrenderglquake = true;
77                                 break;
78                         default:
79                         case 2:
80                                 skyrendersphere = true;
81                                 break;
82                         }
83                 }
84                 if (r_mergesky.integer && (skyrenderglquake || skyrendersphere))
85                 {
86         //              R_BuildSky((int) (cl.time * 8.0), (int) (cl.time * 16.0));
87         //              R_BuildSky((int) (cl.time * -8.0), 0);
88                         R_BuildSky(0, (int) (cl.time * 8.0));
89                 }
90                 if (skyrenderbox || skyrendersphere)
91                 {
92                         // for depth-masked sky, render the sky on the first sky surface encountered
93                         skyrendernow = true;
94                         skyrendermasked = true;
95                 }
96         }
97 }
98
99 static char skyname[256];
100
101 /*
102 ==================
103 R_SetSkyBox
104 ==================
105 */
106 static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
107 static rtexture_t *skyboxside[6];
108 int R_SetSkyBox(char *sky)
109 {
110         int             i;
111         char    name[1024];
112         byte*   image_rgba;
113
114         if (strcmp(sky, skyname) == 0) // no change
115                 return true;
116
117         skyboxside[0] = skyboxside[1] = skyboxside[2] = skyboxside[3] = skyboxside[4] = skyboxside[5] = NULL;
118         skyavailable_box = false;
119         skyname[0] = 0;
120
121         if (!sky[0])
122                 return true;
123
124         if (strlen(sky) > 1000)
125         {
126                 Con_Printf ("sky name too long (%i, max is 1000)\n", strlen(sky));
127                 return false;
128         }
129
130         for (i = 0;i < 6;i++)
131         {
132                 sprintf (name, "env/%s%s", sky, suf[i]);
133                 if (!(image_rgba = loadimagepixels(name, false, 0, 0)))
134                 {
135                         sprintf (name, "gfx/env/%s%s", sky, suf[i]);
136                         if (!(image_rgba = loadimagepixels(name, false, 0, 0)))
137                         {
138                                 Con_Printf ("Couldn't load env/%s%s or gfx/env/%s%s\n", sky, suf[i], sky, suf[i]);
139                                 continue;
140                         }
141                 }
142                 skyboxside[i] = R_LoadTexture(skytexturepool, va("skyboxside%d", i), image_width, image_height, image_rgba, TEXTYPE_RGBA, TEXF_PRECACHE);
143                 Mem_Free(image_rgba);
144         }
145
146         if (skyboxside[0] || skyboxside[1] || skyboxside[2] || skyboxside[3] || skyboxside[4] || skyboxside[5])
147         {
148                 skyavailable_box = true;
149                 strcpy(skyname, sky);
150                 return true;
151         }
152         return false;
153 }
154
155 // LordHavoc: added LoadSky console command
156 void LoadSky_f (void)
157 {
158         switch (Cmd_Argc())
159         {
160         case 1:
161                 if (skyname[0])
162                         Con_Printf("current sky: %s\n", skyname);
163                 else
164                         Con_Printf("no skybox has been set\n");
165                 break;
166         case 2:
167                 if (R_SetSkyBox(Cmd_Argv(1)))
168                 {
169                         if (skyname[0])
170                                 Con_Printf("skybox set to %s\n", skyname);
171                         else
172                                 Con_Printf("skybox disabled\n");
173                 }
174                 else
175                         Con_Printf("failed to load skybox %s\n", Cmd_Argv(1));
176                 break;
177         default:
178                 Con_Printf("usage: loadsky skyname\n");
179                 break;
180         }
181 }
182
183 #define R_SkyBoxPolyVec(i,s,t,x,y,z) \
184         vert[i][0] = (x) * 1024.0f + r_origin[0];\
185         vert[i][1] = (y) * 1024.0f + r_origin[1];\
186         vert[i][2] = (z) * 1024.0f + r_origin[2];\
187         st[i][0] = (s) * (254.0f/256.0f) + (1.0f/256.0f);\
188         st[i][1] = (t) * (254.0f/256.0f) + (1.0f/256.0f);
189
190 int skyboxindex[6] = {0, 1, 2, 0, 2, 3};
191
192 static void R_SkyBox(void)
193 {
194         float vert[4][4], st[4][2];
195         rmeshinfo_t m;
196         memset(&m, 0, sizeof(m));
197         m.transparent = false;
198         m.blendfunc1 = GL_ONE;
199         m.blendfunc2 = GL_ZERO;
200         m.numtriangles = 2;
201         m.numverts = 4;
202         m.index = skyboxindex;
203         m.vertex = &vert[0][0];
204         m.vertexstep = sizeof(float[4]);
205         m.cr = 1;
206         m.cg = 1;
207         m.cb = 1;
208         m.ca = 1;
209         m.texcoords[0] = &st[0][0];
210         m.texcoordstep[0] = sizeof(float[2]);
211         m.tex[0] = R_GetTexture(skyboxside[3]); // front
212         R_SkyBoxPolyVec(0, 1, 0,  1, -1,  1);
213         R_SkyBoxPolyVec(1, 1, 1,  1, -1, -1);
214         R_SkyBoxPolyVec(2, 0, 1,  1,  1, -1);
215         R_SkyBoxPolyVec(3, 0, 0,  1,  1,  1);
216         R_Mesh_Draw(&m);
217         m.tex[0] = R_GetTexture(skyboxside[1]); // back
218         R_SkyBoxPolyVec(0, 1, 0, -1,  1,  1);
219         R_SkyBoxPolyVec(1, 1, 1, -1,  1, -1);
220         R_SkyBoxPolyVec(2, 0, 1, -1, -1, -1);
221         R_SkyBoxPolyVec(3, 0, 0, -1, -1,  1);
222         R_Mesh_Draw(&m);
223         m.tex[0] = R_GetTexture(skyboxside[0]); // right
224         R_SkyBoxPolyVec(0, 1, 0,  1,  1,  1);
225         R_SkyBoxPolyVec(1, 1, 1,  1,  1, -1);
226         R_SkyBoxPolyVec(2, 0, 1, -1,  1, -1);
227         R_SkyBoxPolyVec(3, 0, 0, -1,  1,  1);
228         R_Mesh_Draw(&m);
229         m.tex[0] = R_GetTexture(skyboxside[2]); // left
230         R_SkyBoxPolyVec(0, 1, 0, -1, -1,  1);
231         R_SkyBoxPolyVec(1, 1, 1, -1, -1, -1);
232         R_SkyBoxPolyVec(2, 0, 1,  1, -1, -1);
233         R_SkyBoxPolyVec(3, 0, 0,  1, -1,  1);
234         R_Mesh_Draw(&m);
235         m.tex[0] = R_GetTexture(skyboxside[4]); // up
236         R_SkyBoxPolyVec(0, 1, 0,  1, -1,  1);
237         R_SkyBoxPolyVec(1, 1, 1,  1,  1,  1);
238         R_SkyBoxPolyVec(2, 0, 1, -1,  1,  1);
239         R_SkyBoxPolyVec(3, 0, 0, -1, -1,  1);
240         R_Mesh_Draw(&m);
241         m.tex[0] = R_GetTexture(skyboxside[5]); // down
242         R_SkyBoxPolyVec(0, 1, 0,  1,  1, -1);
243         R_SkyBoxPolyVec(1, 1, 1,  1, -1, -1);
244         R_SkyBoxPolyVec(2, 0, 1, -1, -1, -1);
245         R_SkyBoxPolyVec(3, 0, 0, -1,  1, -1);
246         R_Mesh_Draw(&m);
247         R_Mesh_Render();
248         if (r_skyflush.integer)
249                 glFlush();
250         // clear the zbuffer that was used while rendering the sky
251         glClear(GL_DEPTH_BUFFER_BIT);
252         if (r_skyflush.integer)
253                 glFlush();
254 }
255
256 static float skysphere[33*33*5];
257 static int skysphereindices[32*32*6];
258 static void skyspherecalc(float *sphere, float dx, float dy, float dz)
259 {
260         float a, b, x, ax, ay, v[3], length;
261         int i, j, *index;
262         for (a = 0;a <= 1;a += (1.0 / 32.0))
263         {
264                 ax = cos(a * M_PI * 2);
265                 ay = -sin(a * M_PI * 2);
266                 for (b = 0;b <= 1;b += (1.0 / 32.0))
267                 {
268                         x = cos(b * M_PI * 2);
269                         v[0] = ax*x * dx;
270                         v[1] = ay*x * dy;
271                         v[2] = -sin(b * M_PI * 2) * dz;
272                         length = 3.0f / sqrt(v[0]*v[0]+v[1]*v[1]+(v[2]*v[2]*9));
273                         *sphere++ = v[0] * length;
274                         *sphere++ = v[1] * length;
275                         *sphere++ = v[0];
276                         *sphere++ = v[1];
277                         *sphere++ = v[2];
278                 }
279         }
280         index = skysphereindices;
281         for (j = 0;j < 32;j++)
282         {
283                 for (i = 0;i < 32;i++)
284                 {
285                         *index++ =  j      * 33 + i;
286                         *index++ =  j      * 33 + i + 1;
287                         *index++ = (j + 1) * 33 + i;
288
289                         *index++ =  j      * 33 + i + 1;
290                         *index++ = (j + 1) * 33 + i + 1;
291                         *index++ = (j + 1) * 33 + i;
292                 }
293                 i++;
294         }
295 }
296
297 static void skyspherearrays(float *vert, float *tex, float *tex2, float *source, float s, float s2)
298 {
299         float *v, *t, *t2, radius;
300         int i;
301         v = vert;
302         t = tex;
303         t2 = tex2;
304         radius = r_farclip - 8;
305         for (i = 0;i < (33*33);i++)
306         {
307                 *t++ = source[0] + s;
308                 *t++ = source[1] + s;
309                 *t2++ = source[0] + s2;
310                 *t2++ = source[1] + s2;
311                 *v++ = source[2] + r_origin[0];
312                 *v++ = source[3] + r_origin[1];
313                 *v++ = source[4] + r_origin[2];
314                 *v++ = 0;
315                 source += 5;
316         }
317 }
318
319 static void R_SkySphere(void)
320 {
321         float speedscale, speedscale2;
322         float vert[33*33*4], tex[33*33*2], tex2[33*33*2];
323         static qboolean skysphereinitialized = false;
324         rmeshinfo_t m;
325         if (!skysphereinitialized)
326         {
327                 skysphereinitialized = true;
328                 skyspherecalc(skysphere, 1024, 1024, 1024 / 3);
329         }
330         memset(&m, 0, sizeof(m));
331         m.transparent = false;
332         m.blendfunc1 = GL_ONE;
333         m.blendfunc2 = GL_ZERO;
334         m.numtriangles = 32*32*2;
335         m.numverts = 33*33;
336         m.index = skysphereindices;
337         m.vertex = vert;
338         m.vertexstep = sizeof(float[4]);
339         m.cr = 1;
340         m.cg = 1;
341         m.cb = 1;
342         m.ca = 1;
343         m.texcoords[0] = tex;
344         m.texcoordstep[0] = sizeof(float[2]);
345         speedscale = cl.time*8.0/128.0;
346         speedscale -= (int)speedscale;
347         speedscale2 = cl.time*16.0/128.0;
348         speedscale2 -= (int)speedscale2;
349         skyspherearrays(vert, tex, tex2, skysphere, speedscale, speedscale2);
350         // do not lock the texcoord array, because it will be switched
351         if (r_mergesky.integer)
352         {
353                 m.tex[0] = R_GetTexture(mergeskytexture);
354                 R_Mesh_Draw(&m);
355         }
356         else
357         {
358                 m.tex[0] = R_GetTexture(solidskytexture);
359                 R_Mesh_Draw(&m);
360
361                 m.blendfunc1 = GL_SRC_ALPHA;
362                 m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
363                 m.tex[0] = R_GetTexture(alphaskytexture);
364                 m.texcoords[0] = tex2;
365                 R_Mesh_Draw(&m);
366         }
367         R_Mesh_Render();
368         if (r_skyflush.integer)
369                 glFlush();
370         // clear the zbuffer that was used while rendering the sky
371         glClear(GL_DEPTH_BUFFER_BIT);
372         if (r_skyflush.integer)
373                 glFlush();
374 }
375
376 void R_Sky(void)
377 {
378         if (skyrendersphere)
379                 R_SkySphere();
380         else if (skyrenderbox)
381                 R_SkyBox();
382 }
383
384 //===============================================================
385
386 static byte skyupperlayerpixels[128*128*4];
387 static byte skylowerlayerpixels[128*128*4];
388 static byte skymergedpixels[128*128*4];
389
390 static void R_BuildSky (int scrollupper, int scrolllower)
391 {
392         int x, y, ux, uy, lx, ly;
393         byte *m, *u, *l;
394         m = skymergedpixels;
395         for (y = 0;y < 128;y++)
396         {
397                 uy = (y + scrollupper) & 127;
398                 ly = (y + scrolllower) & 127;
399                 for (x = 0;x < 128;x++)
400                 {
401                         ux = (x + scrollupper) & 127;
402                         lx = (x + scrolllower) & 127;
403                         u = &skyupperlayerpixels[(uy * 128 + ux) * 4];
404                         l = &skylowerlayerpixels[(ly * 128 + lx) * 4];
405                         if (l[3])
406                         {
407                                 if (l[3] == 255)
408                                         *((int *)m) = *((int *)l);
409                                 else
410                                 {
411                                         m[0] = ((((int) l[0] - (int) u[0]) * (int) l[3]) >> 8) + (int) u[0];
412                                         m[1] = ((((int) l[1] - (int) u[1]) * (int) l[3]) >> 8) + (int) u[1];
413                                         m[2] = ((((int) l[2] - (int) u[2]) * (int) l[3]) >> 8) + (int) u[2];
414                                         m[3] = 255;
415                                 }
416                         }
417                         else
418                                 *((int *)m) = *((int *)u);
419                         m += 4;
420                 }
421         }
422         if (mergeskytexture)
423                 R_UpdateTexture(mergeskytexture, skymergedpixels);
424         else
425                 mergeskytexture = R_LoadTexture(skytexturepool, "mergedskytexture", 128, 128, skymergedpixels, TEXTYPE_RGBA, TEXF_ALWAYSPRECACHE);
426 }
427
428 /*
429 =============
430 R_InitSky
431
432 A sky texture is 256*128, with the right side being a masked overlay
433 ==============
434 */
435 void R_InitSky (byte *src, int bytesperpixel)
436 {
437         int                     i, j, p;
438         unsigned        trans[128*128];
439         unsigned        transpix;
440         int                     r, g, b;
441         unsigned        *rgba;
442
443         strcpy(skyworldname, loadmodel->name);
444
445         // flush skytexturepool so we won't build up a leak from uploading textures multiple times
446         R_FreeTexturePool(&skytexturepool);
447         skytexturepool = R_AllocTexturePool();
448         mergeskytexture = NULL;
449         solidskytexture = NULL;
450         alphaskytexture = NULL;
451
452         if (bytesperpixel == 4)
453         {
454                 for (i = 0;i < 128;i++)
455                         for (j = 0;j < 128;j++)
456                                 trans[(i*128) + j] = src[i*256+j+128];
457         }
458         else
459         {
460                 // make an average value for the back to avoid
461                 // a fringe on the top level
462                 r = g = b = 0;
463                 for (i=0 ; i<128 ; i++)
464                 {
465                         for (j=0 ; j<128 ; j++)
466                         {
467                                 p = src[i*256 + j + 128];
468                                 rgba = &d_8to24table[p];
469                                 trans[(i*128) + j] = *rgba;
470                                 r += ((byte *)rgba)[0];
471                                 g += ((byte *)rgba)[1];
472                                 b += ((byte *)rgba)[2];
473                         }
474                 }
475
476                 ((byte *)&transpix)[0] = r/(128*128);
477                 ((byte *)&transpix)[1] = g/(128*128);
478                 ((byte *)&transpix)[2] = b/(128*128);
479                 ((byte *)&transpix)[3] = 0;
480         }
481
482         memcpy(skyupperlayerpixels, trans, 128*128*4);
483
484         solidskytexture = R_LoadTexture (skytexturepool, "sky_solidtexture", 128, 128, (byte *) trans, TEXTYPE_RGBA, TEXF_PRECACHE);
485         /*
486         for (i = 0;i < 128*128;i++)
487         {
488                 ((byte *)&trans[i])[0] >>= 1;
489                 ((byte *)&trans[i])[1] >>= 1;
490                 ((byte *)&trans[i])[2] >>= 1;
491         }
492         solidskytexture_half = R_LoadTexture (skytexturepool, "sky_solidtexture_half", 128, 128, (byte *) trans, TEXTYPE_RGBA, TEXF_PRECACHE);
493         */
494
495         if (bytesperpixel == 4)
496         {
497                 for (i = 0;i < 128;i++)
498                         for (j = 0;j < 128;j++)
499                                 trans[(i*128) + j] = src[i*256+j];
500         }
501         else
502         {
503                 for (i=0 ; i<128 ; i++)
504                 {
505                         for (j=0 ; j<128 ; j++)
506                         {
507                                 p = src[i*256 + j];
508                                 if (p == 0)
509                                         trans[(i*128) + j] = transpix;
510                                 else
511                                         trans[(i*128) + j] = d_8to24table[p];
512                         }
513                 }
514         }
515
516         memcpy(skylowerlayerpixels, trans, 128*128*4);
517
518         alphaskytexture = R_LoadTexture (skytexturepool, "sky_alphatexture", 128, 128, (byte *) trans, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
519         /*
520         for (i = 0;i < 128*128;i++)
521         {
522                 ((byte *)&trans[i])[0] >>= 1;
523                 ((byte *)&trans[i])[1] >>= 1;
524                 ((byte *)&trans[i])[2] >>= 1;
525         }
526         alphaskytexture_half = R_LoadTexture (skytexturepool, "sky_alphatexture_half", 128, 128, (byte *) trans, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
527         */
528 }