Line data Source code
1 : /* rendersky.cpp: skybox and sky environment rendering
2 : *
3 : * Libprimis supports standard static cubemap skyboxes as well as cloud layers,
4 : * which are both static (though capable of translation/rotation)
5 : *
6 : * as well as a parameterized sky generation system, referred to the code as the
7 : * atmo" functionality
8 : *
9 : * as expected for distant scenery, like the sky and clouds are, there is no support
10 : * for parallax (cloud layers do not move relative to background sky as the player
11 : * moves)
12 : */
13 : #include "../libprimis-headers/cube.h"
14 : #include "../../shared/geomexts.h"
15 : #include "../../shared/glemu.h"
16 : #include "../../shared/glexts.h"
17 : #include "../../shared/stream.h"
18 :
19 : #include "octarender.h"
20 : #include "rendergl.h"
21 : #include "renderlights.h"
22 : #include "rendersky.h"
23 : #include "renderva.h"
24 : #include "shader.h"
25 : #include "shaderparam.h"
26 : #include "texture.h"
27 :
28 : #include "interface/console.h"
29 : #include "interface/control.h"
30 :
31 : #include "world/light.h"
32 : #include "world/octaedit.h"
33 : #include "world/octaworld.h"
34 : #include "world/raycube.h"
35 :
36 0 : VARFR(skyshadow, 0, 0, 1, clearshadowcache()); //toggles rsm features in renderva.cpp
37 :
38 : bool explicitsky = false;
39 :
40 : // internally relevant functionality
41 : namespace
42 : {
43 : VARNR(skytexture, useskytexture, 0, 0, 1); //toggles rendering sky texture instead of nothing on skytex'd geometry
44 :
45 : std::array<const Texture *, 6> sky = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
46 :
47 0 : void loadsky(const char *basename, std::array<const Texture *, 6> &texs)
48 : {
49 :
50 : struct cubemapside
51 : {
52 : GLenum target;
53 : const char *name;
54 : bool flipx, flipy, swapxy;
55 : };
56 : static const cubemapside cubemapsides[6] =
57 : {
58 : { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, "lf", false, true, true },
59 : { GL_TEXTURE_CUBE_MAP_POSITIVE_X, "rt", true, false, true },
60 : { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, "bk", false, false, false },
61 : { GL_TEXTURE_CUBE_MAP_POSITIVE_Y, "ft", true, true, false },
62 : { GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, "dn", true, false, true },
63 : { GL_TEXTURE_CUBE_MAP_POSITIVE_Z, "up", true, false, true },
64 : };
65 :
66 0 : const char *wildcard = std::strchr(basename, '*');
67 0 : for(int i = 0; i < 6; ++i) //six sides for a cubemap
68 : {
69 0 : const char *side = cubemapsides[i].name;
70 : string name;
71 0 : copystring(name, makerelpath("media/sky", basename));
72 0 : if(wildcard)
73 : {
74 0 : char *chop = std::strchr(name, '*');
75 0 : if(chop)
76 : {
77 0 : *chop = '\0';
78 0 : concatstring(name, side);
79 0 : concatstring(name, wildcard+1);
80 : }
81 0 : texs[i] = textureload(name, 3, true, false);
82 : }
83 : else
84 : {
85 0 : DEF_FORMAT_STRING(ext, "_%s.jpg", side);
86 0 : concatstring(name, ext);
87 0 : if((texs[i] = textureload(name, 3, true, false))==notexture)
88 : {
89 0 : std::strcpy(name+std::strlen(name)-3, "png");
90 0 : texs[i] = textureload(name, 3, true, false);
91 : }
92 : }
93 0 : if(texs[i]==notexture)
94 : {
95 0 : conoutf(Console_Error, "could not load side %s of sky texture %s", side, basename);
96 : }
97 : }
98 0 : }
99 :
100 : Texture *cloudoverlay = nullptr;
101 :
102 0 : Texture *loadskyoverlay(const char *basename)
103 : {
104 0 : const char *ext = std::strrchr(basename, '.');
105 : string name;
106 0 : copystring(name, makerelpath("media/sky", basename));
107 0 : Texture *t = notexture;
108 0 : if(ext)
109 : {
110 0 : t = textureload(name, 0, true, false);
111 : }
112 : else
113 : {
114 0 : concatstring(name, ".jpg");
115 0 : if((t = textureload(name, 0, true, false)) == notexture)
116 : {
117 0 : std::strcpy(name+std::strlen(name)-3, "png");
118 0 : t = textureload(name, 0, true, false);
119 : }
120 : }
121 0 : if(t==notexture)
122 : {
123 0 : conoutf(Console_Error, "could not load sky overlay texture %s", basename);
124 : }
125 0 : return t;
126 : }
127 :
128 0 : SVARFR(skybox, "",
129 : {
130 : if(skybox[0])
131 : {
132 : loadsky(skybox, sky);
133 : }
134 : }); //path to skybox
135 0 : CVARR(skyboxcolor, 0xFFFFFF); //color to multiply skybox texture by
136 : FVARR(skyboxoverbright, 1, 2, 16); //amount by which skybox can exceed 0xFFFFFF
137 : FVARR(skyboxoverbrightmin, 0, 1, 16);
138 : FVARR(skyboxoverbrightthreshold, 0, 0.7f, 1);
139 : FVARR(skyboxspin, -720, 0, 720); //skybox spin rate in degrees per second
140 : VARR (skyboxyaw, 0, 0, 360); //skybox rotation offset in degrees
141 :
142 : //cloud layer variables
143 : FVARR(cloudclip, 0, 0.5f, 1);
144 0 : SVARFR(cloudlayer, "",
145 : {
146 : if(cloudlayer[0])
147 : {
148 : cloudoverlay = loadskyoverlay(cloudlayer);
149 : }
150 : });
151 : FVARR(cloudoffsetx, 0, 0, 1); //offset of cloud texture: 1 is equal to 1 tex-width x
152 : FVARR(cloudoffsety, 0, 0, 1); //offset of cloud texture: 1 is equal to 1 tex-width y
153 : FVARR(cloudscrollx, -16, 0, 16);
154 : FVARR(cloudscrolly, -16, 0, 16);
155 : FVARR(cloudscale, 0.001, 1, 64);
156 : FVARR(cloudspin, -720, 0, 720);
157 : VARR (cloudyaw, 0, 0, 360);
158 : FVARR(cloudheight, -1, 0.2f, 1);
159 : FVARR(cloudfade, 0, 0.2f, 1);
160 : FVARR(cloudalpha, 0, 1, 1);
161 : VARR (cloudsubdiv, 4, 16, 64);
162 0 : CVARR(cloudcolor, 0xFFFFFF);
163 :
164 0 : void drawenvboxface(float s0, float t0, int x0, int y0, int z0,
165 : float s1, float t1, int x1, int y1, int z1,
166 : float s2, float t2, int x2, int y2, int z2,
167 : float s3, float t3, int x3, int y3, int z3,
168 : const Texture *tex)
169 : {
170 0 : glBindTexture(GL_TEXTURE_2D, (tex ? tex : notexture)->id);
171 0 : gle::begin(GL_TRIANGLE_STRIP);
172 0 : gle::attribf(x3, y3, z3); gle::attribf(s3, t3);
173 0 : gle::attribf(x2, y2, z2); gle::attribf(s2, t2);
174 0 : gle::attribf(x0, y0, z0); gle::attribf(s0, t0);
175 0 : gle::attribf(x1, y1, z1); gle::attribf(s1, t1);
176 0 : xtraverts += gle::end();
177 0 : }
178 :
179 0 : void drawenvbox(const std::array<const Texture *, 6> &sky, float z1clip = 0.0f, float z2clip = 1.0f, int faces = 0x3F)
180 : {
181 0 : if(z1clip >= z2clip)
182 : {
183 0 : return;
184 : }
185 0 : float v1 = 1-z1clip,
186 0 : v2 = 1-z2clip;
187 0 : int w = farplane/2,
188 0 : z1 = static_cast<int>(std::ceil(2*w*(z1clip-0.5f))),
189 0 : z2 = static_cast<int>(std::ceil(2*w*(z2clip-0.5f)));
190 :
191 0 : gle::defvertex();
192 0 : gle::deftexcoord0();
193 :
194 : //daw the six faces of the skybox's cubemap
195 0 : if(faces&0x01)
196 : {
197 0 : drawenvboxface(1.0f, v2, -w, -w, z2,
198 : 0.0f, v2, -w, w, z2,
199 : 0.0f, v1, -w, w, z1,
200 0 : 1.0f, v1, -w, -w, z1, sky[0]);
201 : }
202 0 : if(faces&0x02)
203 : {
204 0 : drawenvboxface(0.0f, v1, w, -w, z1,
205 : 1.0f, v1, w, w, z1,
206 : 1.0f, v2, w, w, z2,
207 0 : 0.0f, v2, w, -w, z2, sky[1]);
208 : }
209 0 : if(faces&0x04)
210 : {
211 0 : drawenvboxface(0.0f, v1, -w, -w, z1,
212 : 1.0f, v1, w, -w, z1,
213 : 1.0f, v2, w, -w, z2,
214 0 : 0.0f, v2, -w, -w, z2, sky[2]);
215 : }
216 0 : if(faces&0x08)
217 : {
218 0 : drawenvboxface(0.0f, v1, w, w, z1,
219 : 1.0f, v1, -w, w, z1,
220 : 1.0f, v2, -w, w, z2,
221 0 : 0.0f, v2, w, w, z2, sky[3]);
222 : }
223 0 : if(z1clip <= 0 && faces&0x10)
224 : {
225 0 : drawenvboxface(1.0f, 1.0f, -w, w, -w,
226 : 1.0f, 0.0f, w, w, -w,
227 : 0.0f, 0.0f, w, -w, -w,
228 0 : 0.0f, 1.0f, -w, -w, -w, sky[4]);
229 : }
230 0 : if(z2clip >= 1 && faces&0x20)
231 : {
232 0 : drawenvboxface(1.0f, 1.0f, w, w, w,
233 : 1.0f, 0.0f, -w, w, w,
234 : 0.0f, 0.0f, -w, -w, w,
235 0 : 0.0f, 1.0f, w, -w, w, sky[5]);
236 : }
237 : }
238 :
239 0 : void drawenvoverlay(const Texture *overlay = nullptr, float tx = 0, float ty = 0)
240 : {
241 0 : int w = farplane/2;
242 0 : float z = w*cloudheight,
243 0 : tsz = 0.5f*(1-cloudfade)/cloudscale,
244 0 : psz = w*(1-cloudfade);
245 0 : glBindTexture(GL_TEXTURE_2D, (overlay ? overlay : notexture)->id);
246 0 : vec color = cloudcolor.tocolor();
247 0 : gle::color(color, cloudalpha);
248 0 : gle::defvertex();
249 0 : gle::deftexcoord0();
250 0 : gle::begin(GL_TRIANGLE_FAN);
251 0 : for(int i = 0; i < cloudsubdiv+1; ++i)
252 : {
253 0 : vec p(1, 1, 0);
254 0 : p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
255 0 : gle::attribf(p.x*psz, p.y*psz, z);
256 0 : gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
257 : }
258 0 : xtraverts += gle::end();
259 0 : float tsz2 = 0.5f/cloudscale;
260 0 : gle::defvertex();
261 0 : gle::deftexcoord0();
262 0 : gle::defcolor(4);
263 0 : gle::begin(GL_TRIANGLE_STRIP);
264 0 : for(int i = 0; i < cloudsubdiv+1; ++i)
265 : {
266 0 : vec p(1, 1, 0);
267 0 : p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
268 0 : gle::attribf(p.x*psz, p.y*psz, z);
269 0 : gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
270 0 : gle::attrib(color, cloudalpha);
271 0 : gle::attribf(p.x*w, p.y*w, z);
272 0 : gle::attribf(tx - p.x*tsz2, ty + p.y*tsz2);
273 0 : gle::attrib(color, 0.0f);
274 : }
275 0 : xtraverts += gle::end();
276 0 : }
277 :
278 : /* === "atmo" parameterized, procedurally generated sky === */
279 : VARR(atmo, 0, 0, 1);
280 : FVARR(atmoplanetsize, 1e-3f, 8, 1e3f);
281 : FVARR(atmoheight, 1e-3f, 1, 1e3f);
282 : FVARR(atmobright, 0, 4, 16);
283 0 : CVAR1R(atmosunlight, 0);
284 : FVARR(atmosunlightscale, 0, 1, 16);
285 : FVARR(atmosundisksize, 0, 1, 10);
286 : FVARR(atmosundiskbright, 0, 1, 16);
287 : FVARR(atmohaze, 0, 0.03f, 1);
288 0 : CVAR0R(atmohazefade, 0xAEACA9);
289 : FVARR(atmohazefadescale, 0, 1, 1);
290 : FVARR(atmoclarity, 0, 0.2f, 10);
291 : FVARR(atmodensity, 1e-3f, 0.99f, 10);
292 : FVARR(atmoalpha, 0, 1, 1);
293 :
294 0 : void drawatmosphere()
295 : {
296 0 : SETSHADER(atmosphere);
297 :
298 0 : matrix4 sunmatrix = cammatrix.inverse();
299 0 : sunmatrix.settranslation(0, 0, 0);
300 0 : sunmatrix.mul(projmatrix.inverse());
301 0 : LOCALPARAM(sunmatrix, sunmatrix);
302 :
303 0 : LOCALPARAM(sunlight, (!atmosunlight.iszero() ? atmosunlight.tocolor().mul(atmosunlightscale) : sunlight.tocolor().mul(sunlightscale)).mul(atmobright*ldrscale));
304 0 : LOCALPARAM(sundir, sunlightdir);
305 :
306 0 : vec sundiskparams;
307 0 : sundiskparams.y = -(1 - 0.0075f * atmosundisksize);
308 0 : sundiskparams.x = 1/(1 + sundiskparams.y);
309 0 : sundiskparams.y *= sundiskparams.x;
310 0 : sundiskparams.z = atmosundiskbright;
311 0 : LOCALPARAM(sundiskparams, sundiskparams);
312 :
313 0 : const float earthradius = 6.371e6f, //radius of earth in meters
314 0 : earthatmoheight = 100e3f; //atmospheric height (100km)
315 0 : float planetradius = earthradius*atmoplanetsize,
316 0 : atmoradius = planetradius + earthatmoheight*atmoheight;
317 0 : LOCALPARAMF(atmoradius, planetradius, atmoradius*atmoradius, atmoradius*atmoradius - planetradius*planetradius);
318 :
319 0 : float gm = (1 - atmohaze)*0.2f + 0.75f;
320 0 : LOCALPARAMF(gm, gm);
321 :
322 0 : vec lambda(680e-9f, 550e-9f, 450e-9f),
323 0 : betar = vec(lambda).square().square().recip().mul(1.86e-31f / atmodensity),
324 0 : betam = vec(lambda).recip().mul(2*M_PI).square().mul(atmohazefade.tocolor().mul(atmohazefadescale)).mul(1.36e-19f * std::max(atmohaze, 1e-3f)),
325 0 : betarm = vec(betar).div(1+atmoclarity).add(betam);
326 0 : betar.div(betarm).mul(3/(16*M_PI));
327 0 : betam.div(betarm).mul((1-gm)*(1-gm)/(4*M_PI));
328 0 : LOCALPARAM(betar, betar);
329 0 : LOCALPARAM(betam, betam);
330 0 : LOCALPARAM(betarm, betarm.div(M_LN2));
331 :
332 0 : LOCALPARAMF(atmoalpha, atmoalpha);
333 :
334 0 : gle::defvertex();
335 0 : gle::begin(GL_TRIANGLE_STRIP);
336 0 : gle::attribf(-1, 1, 1);
337 0 : gle::attribf(1, 1, 1);
338 0 : gle::attribf(-1, -1, 1);
339 0 : gle::attribf(1, -1, 1);
340 0 : xtraverts += gle::end();
341 0 : }
342 :
343 : /* === general sky rendering === */
344 : VAR(showsky, 0, 1, 1);
345 : VAR(clampsky, 0, 1, 1);
346 : }
347 :
348 : // externally relevant functionality
349 :
350 0 : bool limitsky()
351 : {
352 0 : return explicitsky && (useskytexture || editmode);
353 : }
354 :
355 0 : void drawskybox(bool clear)
356 : {
357 0 : bool limited = false;
358 0 : if(limitsky())
359 : {
360 0 : for(vtxarray *va = visibleva; va; va = va->next)
361 : {
362 0 : if(va->sky && va->occluded < Occlude_BB &&
363 0 : ((va->skymax.x >= 0 && view.isvisiblebb(va->skymin, ivec(va->skymax).sub(va->skymin)) != ViewFrustumCull_NotVisible) ||
364 0 : !insideworld(camera1->o)))
365 : {
366 0 : limited = true;
367 0 : break;
368 : }
369 : }
370 : }
371 0 : if(limited)
372 : {
373 0 : glDisable(GL_DEPTH_TEST);
374 : }
375 : else
376 : {
377 0 : glDepthFunc(GL_LEQUAL);
378 0 : glDepthMask(GL_FALSE);
379 : }
380 0 : if(clampsky)
381 : {
382 0 : glDepthRange(1, 1); //set gl depth range min and max to 1 (all far away)
383 : }
384 0 : if(clear || (!skybox[0] && (!atmo || atmoalpha < 1)))
385 : {
386 0 : vec skyboxcol = skyboxcolor.tocolor().mul(ldrscale); //local skyboxcol was skyboxcolor before skyboxcolour -> skyboxcolor uniformity change
387 0 : glClearColor(skyboxcol.x, skyboxcol.y, skyboxcol.z, 0);
388 0 : glClear(GL_COLOR_BUFFER_BIT);
389 : }
390 0 : if(skybox[0])
391 : {
392 0 : if(ldrscale < 1 && (skyboxoverbrightmin != 1 || (skyboxoverbright > 1 && skyboxoverbrightthreshold < 1)))
393 : {
394 0 : SETSHADER(skyboxoverbright);
395 0 : LOCALPARAMF(overbrightparams, skyboxoverbrightmin, std::max(skyboxoverbright, skyboxoverbrightmin), skyboxoverbrightthreshold);
396 0 : }
397 : else
398 : {
399 0 : SETSHADER(skybox);
400 : }
401 0 : gle::color(skyboxcolor);
402 :
403 0 : matrix4 skymatrix = cammatrix,
404 0 : skyprojmatrix;
405 0 : skymatrix.settranslation(0, 0, 0);
406 0 : skymatrix.rotate_around_z((skyboxspin*lastmillis/1000.0f+skyboxyaw)/RAD);
407 0 : skyprojmatrix.mul(projmatrix, skymatrix);
408 0 : LOCALPARAM(skymatrix, skyprojmatrix);
409 :
410 0 : drawenvbox(sky);
411 : }
412 0 : if(atmo && (!skybox[0] || atmoalpha < 1))
413 : {
414 0 : if(atmoalpha < 1)
415 : {
416 0 : glEnable(GL_BLEND);
417 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
418 : }
419 :
420 0 : drawatmosphere();
421 :
422 0 : if(atmoalpha < 1)
423 : {
424 0 : glDisable(GL_BLEND);
425 : }
426 : }
427 0 : if(cloudlayer[0] && cloudheight)
428 : {
429 0 : SETSHADER(skybox);
430 :
431 0 : glDisable(GL_CULL_FACE);
432 :
433 0 : glEnable(GL_BLEND);
434 0 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
435 :
436 0 : matrix4 skymatrix = cammatrix,
437 0 : skyprojmatrix;
438 0 : skymatrix.settranslation(0, 0, 0);
439 0 : skymatrix.rotate_around_z((cloudspin*lastmillis/1000.0f+cloudyaw)/RAD);
440 0 : skyprojmatrix.mul(projmatrix, skymatrix);
441 0 : LOCALPARAM(skymatrix, skyprojmatrix);
442 :
443 0 : drawenvoverlay(cloudoverlay, cloudoffsetx + cloudscrollx * lastmillis/1000.0f, cloudoffsety + cloudscrolly * lastmillis/1000.0f);
444 :
445 0 : glDisable(GL_BLEND);
446 :
447 0 : glEnable(GL_CULL_FACE);
448 : }
449 0 : if(clampsky)
450 : {
451 0 : glDepthRange(0, 1); //return depth range to normal
452 : }
453 0 : if(limited)
454 : {
455 0 : glEnable(GL_DEPTH_TEST);
456 : }
457 : else
458 : {
459 0 : glDepthMask(GL_TRUE);
460 0 : glDepthFunc(GL_LESS);
461 : }
462 0 : }
|