613 lines
21 KiB
C++
613 lines
21 KiB
C++
#include <shared/command.hh>
|
|
#include <shared/glemu.hh>
|
|
|
|
#include "console.hh" /* conoutf */
|
|
#include "light.hh"
|
|
#include "main.hh" // timings
|
|
#include "octaedit.hh" // editmode
|
|
#include "octarender.hh"
|
|
#include "rendergl.hh"
|
|
#include "renderlights.hh"
|
|
#include "renderva.hh"
|
|
#include "texture.hh"
|
|
|
|
static Texture *sky[6] = { 0, 0, 0, 0, 0, 0 }, *clouds[6] = { 0, 0, 0, 0, 0, 0 };
|
|
|
|
static void loadsky(const char *basename, Texture *texs[6])
|
|
{
|
|
const char *wildcard = strchr(basename, '*');
|
|
loopi(6)
|
|
{
|
|
const char *side = cubemapsides[i].name;
|
|
string name;
|
|
copystring(name, makerelpath("media/sky", basename));
|
|
if(wildcard)
|
|
{
|
|
char *chop = strchr(name, '*');
|
|
if(chop) { *chop = '\0'; concatstring(name, side); concatstring(name, wildcard+1); }
|
|
texs[i] = textureload(name, 3, true, false);
|
|
}
|
|
else
|
|
{
|
|
defformatstring(ext, "_%s.jpg", side);
|
|
concatstring(name, ext);
|
|
if((texs[i] = textureload(name, 3, true, false))==notexture)
|
|
{
|
|
strcpy(name+strlen(name)-3, "png");
|
|
texs[i] = textureload(name, 3, true, false);
|
|
}
|
|
}
|
|
if(texs[i]==notexture) conoutf(CON_ERROR, "could not load side %s of sky texture %s", side, basename);
|
|
}
|
|
}
|
|
|
|
static Texture *cloudoverlay = nullptr;
|
|
|
|
static Texture *loadskyoverlay(const char *basename)
|
|
{
|
|
const char *ext = strrchr(basename, '.');
|
|
string name;
|
|
copystring(name, makerelpath("media/sky", basename));
|
|
Texture *t = notexture;
|
|
if(ext) t = textureload(name, 0, true, false);
|
|
else
|
|
{
|
|
concatstring(name, ".jpg");
|
|
if((t = textureload(name, 0, true, false)) == notexture)
|
|
{
|
|
strcpy(name+strlen(name)-3, "png");
|
|
t = textureload(name, 0, true, false);
|
|
}
|
|
}
|
|
if(t==notexture) conoutf(CON_ERROR, "could not load sky overlay texture %s", basename);
|
|
return t;
|
|
}
|
|
|
|
SVARFR(skybox, "", { if(skybox[0]) loadsky(skybox, sky); });
|
|
CVARR(skyboxcolour, 0xFFFFFF);
|
|
FVARR(skyboxoverbright, 1, 2, 16);
|
|
FVARR(skyboxoverbrightmin, 0, 1, 16);
|
|
FVARR(skyboxoverbrightthreshold, 0, 0.7f, 1);
|
|
FVARR(spinsky, -720, 0, 720);
|
|
VARR(yawsky, 0, 0, 360);
|
|
SVARFR(cloudbox, "", { if(cloudbox[0]) loadsky(cloudbox, clouds); });
|
|
CVARR(cloudboxcolour, 0xFFFFFF);
|
|
FVARR(cloudboxalpha, 0, 1, 1);
|
|
FVARR(spinclouds, -720, 0, 720);
|
|
VARR(yawclouds, 0, 0, 360);
|
|
FVARR(cloudclip, 0, 0.5f, 1);
|
|
SVARFR(cloudlayer, "", { if(cloudlayer[0]) cloudoverlay = loadskyoverlay(cloudlayer); });
|
|
FVARR(cloudoffsetx, 0, 0, 1);
|
|
FVARR(cloudoffsety, 0, 0, 1);
|
|
FVARR(cloudscrollx, -16, 0, 16);
|
|
FVARR(cloudscrolly, -16, 0, 16);
|
|
FVARR(cloudscale, 0.001, 1, 64);
|
|
FVARR(spincloudlayer, -720, 0, 720);
|
|
VARR(yawcloudlayer, 0, 0, 360);
|
|
FVARR(cloudheight, -1, 0.2f, 1);
|
|
FVARR(cloudfade, 0, 0.2f, 1);
|
|
FVARR(cloudalpha, 0, 1, 1);
|
|
VARR(cloudsubdiv, 4, 16, 64);
|
|
CVARR(cloudcolour, 0xFFFFFF);
|
|
|
|
static void drawenvboxface(float s0, float t0, int x0, int y0, int z0,
|
|
float s1, float t1, int x1, int y1, int z1,
|
|
float s2, float t2, int x2, int y2, int z2,
|
|
float s3, float t3, int x3, int y3, int z3,
|
|
Texture *tex)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, (tex ? tex : notexture)->id);
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
gle::attribf(x3, y3, z3); gle::attribf(s3, t3);
|
|
gle::attribf(x2, y2, z2); gle::attribf(s2, t2);
|
|
gle::attribf(x0, y0, z0); gle::attribf(s0, t0);
|
|
gle::attribf(x1, y1, z1); gle::attribf(s1, t1);
|
|
xtraverts += gle::end();
|
|
}
|
|
|
|
static void drawenvbox(Texture **sky = nullptr, float z1clip = 0.0f, float z2clip = 1.0f, int faces = 0x3F)
|
|
{
|
|
if(z1clip >= z2clip) return;
|
|
|
|
float v1 = 1-z1clip, v2 = 1-z2clip;
|
|
int w = farplane/2, z1 = int(ceil(2*w*(z1clip-0.5f))), z2 = int(ceil(2*w*(z2clip-0.5f)));
|
|
|
|
gle::defvertex();
|
|
gle::deftexcoord0();
|
|
|
|
if(faces&0x01)
|
|
drawenvboxface(1.0f, v2, -w, -w, z2,
|
|
0.0f, v2, -w, w, z2,
|
|
0.0f, v1, -w, w, z1,
|
|
1.0f, v1, -w, -w, z1, sky[0]);
|
|
|
|
if(faces&0x02)
|
|
drawenvboxface(0.0f, v1, w, -w, z1,
|
|
1.0f, v1, w, w, z1,
|
|
1.0f, v2, w, w, z2,
|
|
0.0f, v2, w, -w, z2, sky[1]);
|
|
|
|
if(faces&0x04)
|
|
drawenvboxface(0.0f, v1, -w, -w, z1,
|
|
1.0f, v1, w, -w, z1,
|
|
1.0f, v2, w, -w, z2,
|
|
0.0f, v2, -w, -w, z2, sky[2]);
|
|
|
|
if(faces&0x08)
|
|
drawenvboxface(0.0f, v1, w, w, z1,
|
|
1.0f, v1, -w, w, z1,
|
|
1.0f, v2, -w, w, z2,
|
|
0.0f, v2, w, w, z2, sky[3]);
|
|
|
|
if(z1clip <= 0 && faces&0x10)
|
|
drawenvboxface(1.0f, 1.0f, -w, w, -w,
|
|
1.0f, 0.0f, w, w, -w,
|
|
0.0f, 0.0f, w, -w, -w,
|
|
0.0f, 1.0f, -w, -w, -w, sky[4]);
|
|
|
|
if(z2clip >= 1 && faces&0x20)
|
|
drawenvboxface(1.0f, 1.0f, w, w, w,
|
|
1.0f, 0.0f, -w, w, w,
|
|
0.0f, 0.0f, -w, -w, w,
|
|
0.0f, 1.0f, w, -w, w, sky[5]);
|
|
}
|
|
|
|
static void drawenvoverlay(Texture *overlay = nullptr, float tx = 0, float ty = 0)
|
|
{
|
|
int w = farplane/2;
|
|
float z = w*cloudheight, tsz = 0.5f*(1-cloudfade)/cloudscale, psz = w*(1-cloudfade);
|
|
glBindTexture(GL_TEXTURE_2D, (overlay ? overlay : notexture)->id);
|
|
vec color = cloudcolour.tocolor();
|
|
gle::color(color, cloudalpha);
|
|
gle::defvertex();
|
|
gle::deftexcoord0();
|
|
gle::begin(GL_TRIANGLE_FAN);
|
|
loopi(cloudsubdiv+1)
|
|
{
|
|
vec p(1, 1, 0);
|
|
p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
|
|
gle::attribf(p.x*psz, p.y*psz, z);
|
|
gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
|
|
}
|
|
xtraverts += gle::end();
|
|
float tsz2 = 0.5f/cloudscale;
|
|
gle::defvertex();
|
|
gle::deftexcoord0();
|
|
gle::defcolor(4);
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
loopi(cloudsubdiv+1)
|
|
{
|
|
vec p(1, 1, 0);
|
|
p.rotate_around_z((-2.0f*M_PI*i)/cloudsubdiv);
|
|
gle::attribf(p.x*psz, p.y*psz, z);
|
|
gle::attribf(tx - p.x*tsz, ty + p.y*tsz);
|
|
gle::attrib(color, cloudalpha);
|
|
gle::attribf(p.x*w, p.y*w, z);
|
|
gle::attribf(tx - p.x*tsz2, ty + p.y*tsz2);
|
|
gle::attrib(color, 0.0f);
|
|
}
|
|
xtraverts += gle::end();
|
|
}
|
|
|
|
FVARR(fogdomeheight, -1, -0.5f, 1);
|
|
FVARR(fogdomemin, 0, 0, 1);
|
|
FVARR(fogdomemax, 0, 0, 1);
|
|
VARR(fogdomecap, 0, 1, 1);
|
|
FVARR(fogdomeclip, 0, 1, 1);
|
|
CVARR(fogdomecolour, 0);
|
|
VARR(fogdomeclouds, 0, 1, 1);
|
|
|
|
namespace fogdome
|
|
{
|
|
static struct vert
|
|
{
|
|
vec pos;
|
|
bvec4 color;
|
|
|
|
vert() {}
|
|
vert(const vec &pos, const bvec &fcolor, float alpha) : pos(pos), color(fcolor, uchar(alpha*255))
|
|
{
|
|
}
|
|
vert(const vert &v0, const vert &v1) : pos(vec(v0.pos).add(v1.pos).normalize()), color(v0.color)
|
|
{
|
|
if(v0.pos.z != v1.pos.z) color.a += uchar((v1.color.a - v0.color.a) * (pos.z - v0.pos.z) / (v1.pos.z - v0.pos.z));
|
|
}
|
|
} *verts = nullptr;
|
|
static GLushort *indices = nullptr;
|
|
static int numverts = 0, numindices = 0, capindices = 0;
|
|
static GLuint vbuf = 0, ebuf = 0;
|
|
static bvec lastcolor(0, 0, 0);
|
|
static float lastminalpha = 0, lastmaxalpha = 0, lastcapsize = -1, lastclipz = 1;
|
|
|
|
static void subdivide(int depth, int face);
|
|
|
|
static void genface(int depth, int i1, int i2, int i3)
|
|
{
|
|
int face = numindices; numindices += 3;
|
|
indices[face] = i3;
|
|
indices[face+1] = i2;
|
|
indices[face+2] = i1;
|
|
subdivide(depth, face);
|
|
}
|
|
|
|
static void subdivide(int depth, int face)
|
|
{
|
|
if(depth-- <= 0) return;
|
|
int idx[6];
|
|
loopi(3) idx[i] = indices[face+2-i];
|
|
loopi(3)
|
|
{
|
|
int curvert = numverts++;
|
|
verts[curvert] = vert(verts[idx[i]], verts[idx[(i+1)%3]]); //push on to unit sphere
|
|
idx[3+i] = curvert;
|
|
indices[face+2-i] = curvert;
|
|
}
|
|
subdivide(depth, face);
|
|
loopi(3) genface(depth, idx[i], idx[3+i], idx[3+(i+2)%3]);
|
|
}
|
|
|
|
static inline int sortcap(GLushort x, GLushort y)
|
|
{
|
|
const vec &xv = verts[x].pos, &yv = verts[y].pos;
|
|
return xv.y < 0 ? yv.y >= 0 || xv.x < yv.x : yv.y >= 0 && xv.x > yv.x;
|
|
}
|
|
|
|
static void init(const bvec &color, float minalpha = 0.0f, float maxalpha = 1.0f, float capsize = -1, float clipz = 1, int hres = 16, int depth = 2)
|
|
{
|
|
const int tris = hres << (2*depth);
|
|
numverts = numindices = capindices = 0;
|
|
verts = new vert[tris+1 + (capsize >= 0 ? 1 : 0)];
|
|
indices = new GLushort[(tris + (capsize >= 0 ? hres<<depth : 0))*3];
|
|
if(clipz >= 1)
|
|
{
|
|
verts[numverts++] = vert(vec(0.0f, 0.0f, 1.0f), color, minalpha); //build initial 'hres' sided pyramid
|
|
loopi(hres) verts[numverts++] = vert(vec(sincos360[(360*i)/hres], 0.0f), color, maxalpha);
|
|
loopi(hres) genface(depth, 0, i+1, 1+(i+1)%hres);
|
|
}
|
|
else if(clipz <= 0)
|
|
{
|
|
loopi(hres<<depth) verts[numverts++] = vert(vec(sincos360[(360*i)/(hres<<depth)], 0.0f), color, maxalpha);
|
|
}
|
|
else
|
|
{
|
|
float clipxy = sqrtf(1 - clipz*clipz);
|
|
const vec2 &scm = sincos360[180/hres];
|
|
loopi(hres)
|
|
{
|
|
const vec2 &sc = sincos360[(360*i)/hres];
|
|
verts[numverts++] = vert(vec(sc.x*clipxy, sc.y*clipxy, clipz), color, minalpha);
|
|
verts[numverts++] = vert(vec(sc.x, sc.y, 0.0f), color, maxalpha);
|
|
verts[numverts++] = vert(vec(sc.x*scm.x - sc.y*scm.y, sc.y*scm.x + sc.x*scm.y, 0.0f), color, maxalpha);
|
|
}
|
|
loopi(hres)
|
|
{
|
|
genface(depth-1, 3*i, 3*i+1, 3*i+2);
|
|
genface(depth-1, 3*i, 3*i+2, 3*((i+1)%hres));
|
|
genface(depth-1, 3*i+2, 3*((i+1)%hres)+1, 3*((i+1)%hres));
|
|
}
|
|
}
|
|
|
|
if(capsize >= 0)
|
|
{
|
|
GLushort *cap = &indices[numindices];
|
|
int capverts = 0;
|
|
loopi(numverts) if(!verts[i].pos.z) cap[capverts++] = i;
|
|
verts[numverts++] = vert(vec(0.0f, 0.0f, -capsize), color, maxalpha);
|
|
quicksort(cap, capverts, sortcap);
|
|
loopi(capverts)
|
|
{
|
|
int n = capverts-1-i;
|
|
cap[n*3] = cap[n];
|
|
cap[n*3+1] = cap[(n+1)%capverts];
|
|
cap[n*3+2] = numverts-1;
|
|
capindices += 3;
|
|
}
|
|
}
|
|
|
|
if(!vbuf) glGenBuffers_(1, &vbuf);
|
|
gle::bindvbo(vbuf);
|
|
glBufferData_(GL_ARRAY_BUFFER, numverts*sizeof(vert), verts, GL_STATIC_DRAW);
|
|
DELETEA(verts);
|
|
|
|
if(!ebuf) glGenBuffers_(1, &ebuf);
|
|
gle::bindebo(ebuf);
|
|
glBufferData_(GL_ELEMENT_ARRAY_BUFFER, (numindices + capindices)*sizeof(GLushort), indices, GL_STATIC_DRAW);
|
|
DELETEA(indices);
|
|
}
|
|
|
|
static void cleanup()
|
|
{
|
|
numverts = numindices = 0;
|
|
if(vbuf) { glDeleteBuffers_(1, &vbuf); vbuf = 0; }
|
|
if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
|
|
}
|
|
|
|
static void draw()
|
|
{
|
|
float capsize = fogdomecap && fogdomeheight < 1 ? (1 + fogdomeheight) / (1 - fogdomeheight) : -1;
|
|
bvec color = !fogdomecolour.iszero() ? fogdomecolour : fogcolour;
|
|
if(!numverts || lastcolor != color || lastminalpha != fogdomemin || lastmaxalpha != fogdomemax || lastcapsize != capsize || lastclipz != fogdomeclip)
|
|
{
|
|
init(color, min(fogdomemin, fogdomemax), fogdomemax, capsize, fogdomeclip);
|
|
lastcolor = color;
|
|
lastminalpha = fogdomemin;
|
|
lastmaxalpha = fogdomemax;
|
|
lastcapsize = capsize;
|
|
lastclipz = fogdomeclip;
|
|
}
|
|
|
|
gle::bindvbo(vbuf);
|
|
gle::bindebo(ebuf);
|
|
|
|
gle::vertexpointer(sizeof(vert), &verts->pos);
|
|
gle::colorpointer(sizeof(vert), &verts->color);
|
|
gle::enablevertex();
|
|
gle::enablecolor();
|
|
|
|
glDrawRangeElements_(GL_TRIANGLES, 0, numverts-1, numindices + fogdomecap*capindices, GL_UNSIGNED_SHORT, indices);
|
|
xtraverts += numverts;
|
|
glde++;
|
|
|
|
gle::disablevertex();
|
|
gle::disablecolor();
|
|
|
|
gle::clearvbo();
|
|
gle::clearebo();
|
|
}
|
|
}
|
|
|
|
static void drawfogdome()
|
|
{
|
|
SETSHADER(skyfog);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
matrix4 skymatrix = cammatrix, skyprojmatrix;
|
|
skymatrix.settranslation(vec(cammatrix.c).mul(farplane*fogdomeheight*0.5f));
|
|
skymatrix.scale(farplane/2, farplane/2, farplane*(0.5f - fogdomeheight*0.5f));
|
|
skyprojmatrix.mul(projmatrix, skymatrix);
|
|
LOCALPARAM(skymatrix, skyprojmatrix);
|
|
|
|
fogdome::draw();
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
void cleanupsky()
|
|
{
|
|
fogdome::cleanup();
|
|
}
|
|
|
|
VARR(atmo, 0, 0, 1);
|
|
FVARR(atmoplanetsize, 1e-3f, 1, 1e3f);
|
|
FVARR(atmoheight, 1e-3f, 1, 1e3f);
|
|
FVARR(atmobright, 0, 1, 16);
|
|
CVAR1R(atmosunlight, 0);
|
|
FVARR(atmosunlightscale, 0, 1, 16);
|
|
CVAR1R(atmosundisk, 0);
|
|
FVARR(atmosundisksize, 0, 12, 90);
|
|
FVARR(atmosundiskcorona, 0, 0.4f, 1);
|
|
FVARR(atmosundiskbright, 0, 1, 16);
|
|
FVARR(atmohaze, 0, 0.1f, 16);
|
|
FVARR(atmodensity, 0, 1, 16);
|
|
FVARR(atmoozone, 0, 1, 16);
|
|
FVARR(atmoalpha, 0, 1, 1);
|
|
|
|
static void drawatmosphere()
|
|
{
|
|
SETSHADER(atmosphere);
|
|
|
|
matrix4 sunmatrix = invcammatrix;
|
|
sunmatrix.settranslation(0, 0, 0);
|
|
sunmatrix.mul(invprojmatrix);
|
|
LOCALPARAM(sunmatrix, sunmatrix);
|
|
|
|
// optical depth scales for 3 different shells of atmosphere - air, haze, ozone
|
|
const float earthradius = 6371e3f, earthairheight = 8.4e3f, earthhazeheight = 1.25e3f, earthozoneheight = 50e3f;
|
|
float planetradius = earthradius*atmoplanetsize;
|
|
vec atmoshells = vec(earthairheight, earthhazeheight, earthozoneheight).mul(atmoheight).add(planetradius).square().sub(planetradius*planetradius);
|
|
LOCALPARAM(opticaldepthparams, vec4(atmoshells, planetradius));
|
|
|
|
// Henyey-Greenstein approximation, 1/(4pi) * (1 - g^2)/(1 + g^2 - 2gcos)]^1.5
|
|
// Hoffman-Preetham variation uses (1-g)^2 instead of 1-g^2 which avoids excessive glare
|
|
// clamp values near 0 angle to avoid spotlight artifact inside sundisk
|
|
float gm = max(0.95f - 0.2f*atmohaze, 0.65f), miescale = pow((1-gm)*(1-gm)/(4*M_PI), -2.0f/3.0f);
|
|
LOCALPARAMF(mieparams, miescale*(1 + gm*gm), miescale*-2*gm, 1 - (1 - cosf(0.5f*atmosundisksize*(1 - atmosundiskcorona)*RAD)));
|
|
|
|
static const vec lambda(680e-9f, 550e-9f, 450e-9f),
|
|
k(0.686f, 0.678f, 0.666f),
|
|
ozone(3.426f, 8.298f, 0.356f);
|
|
vec betar = vec(lambda).square().square().recip().mul(1.241e-30f/M_LN2 * atmodensity),
|
|
betam = vec(lambda).recip().square().mul(k).mul(9.072e-17f/M_LN2 * atmohaze),
|
|
betao = vec(ozone).mul(1.5e-7f/M_LN2 * atmoozone);
|
|
LOCALPARAM(betarayleigh, betar);
|
|
LOCALPARAM(betamie, betam);
|
|
LOCALPARAM(betaozone, betao);
|
|
|
|
// extinction in direction of sun
|
|
float sunoffset = sunlightdir.z*planetradius;
|
|
vec sundepth = vec(atmoshells).add(sunoffset*sunoffset).sqrt().sub(sunoffset);
|
|
vec sunweight = vec(betar).mul(sundepth.x).madd(betam, sundepth.y).madd(betao, sundepth.z - sundepth.x);
|
|
vec sunextinction = vec(sunweight).neg().exp2();
|
|
vec suncolor = !atmosunlight.iszero() ? atmosunlight.tocolor().mul(atmosunlightscale) : sunlight.tocolor().mul(sunlightscale);
|
|
// assume sunlight color is gamma encoded, so decode to linear light, then apply extinction
|
|
extern float hdrgamma;
|
|
vec sunscale = vec(suncolor).mul(ldrscale).pow(hdrgamma).mul(atmobright * 16).mul(sunextinction);
|
|
float maxsunweight = max(max(sunweight.x, sunweight.y), sunweight.z);
|
|
if(maxsunweight > 127) sunweight.mul(127/maxsunweight);
|
|
sunweight.add(1e-4f);
|
|
LOCALPARAM(sunweight, sunweight);
|
|
LOCALPARAM(sunlight, vec4(sunscale, atmoalpha));
|
|
LOCALPARAM(sundir, sunlightdir);
|
|
|
|
// invert extinction at zenith to get an approximation of how bright the sun disk should be
|
|
vec zenithdepth = vec(atmoshells).add(planetradius*planetradius).sqrt().sub(planetradius);
|
|
vec zenithweight = vec(betar).mul(zenithdepth.x).madd(betam, zenithdepth.y).madd(betao, zenithdepth.z - zenithdepth.x);
|
|
vec zenithextinction = vec(zenithweight).sub(sunweight).exp2();
|
|
vec diskcolor = (!atmosundisk.iszero() ? atmosundisk.tocolor() : suncolor).mul(ldrscale).pow(hdrgamma).mul(zenithextinction).mul(atmosundiskbright * 4);
|
|
LOCALPARAM(sundiskcolor, diskcolor);
|
|
|
|
// convert from view cosine into mu^2 for limb darkening, where mu = sqrt(1 - sin^2) and sin^2 = 1 - cos^2, thus mu^2 = 1 - (1 - cos^2*scale)
|
|
// convert corona offset into scale for mu^2, where sin = (1-corona) and thus mu^2 = 1 - (1-corona^2)
|
|
float sundiskscale = sinf(0.5f*atmosundisksize*RAD);
|
|
float coronamu = 1 - (1-atmosundiskcorona)*(1-atmosundiskcorona);
|
|
if(sundiskscale > 0) LOCALPARAMF(sundiskparams, 1.0f/(sundiskscale*sundiskscale), 1.0f/max(coronamu, 1e-3f));
|
|
else LOCALPARAMF(sundiskparams, 0, 0);
|
|
|
|
gle::defvertex();
|
|
gle::begin(GL_TRIANGLE_STRIP);
|
|
gle::attribf(-1, 1, 1);
|
|
gle::attribf(1, 1, 1);
|
|
gle::attribf(-1, -1, 1);
|
|
gle::attribf(1, -1, 1);
|
|
xtraverts += gle::end();
|
|
}
|
|
|
|
VAR(showsky, 0, 1, 1);
|
|
VAR(clampsky, 0, 1, 1);
|
|
VARNR(skytexture, useskytexture, 0, 0, 1);
|
|
VARFR(skyshadow, 0, 0, 1, clearshadowcache());
|
|
|
|
int explicitsky = 0;
|
|
|
|
bool limitsky()
|
|
{
|
|
return explicitsky && (useskytexture || editmode);
|
|
}
|
|
|
|
void drawskybox(bool clear)
|
|
{
|
|
bool limited = false;
|
|
if(limitsky()) for(vtxarray *va = visibleva; va; va = va->next)
|
|
{
|
|
if(va->sky && va->occluded < OCCLUDE_BB &&
|
|
((va->skymax.x >= 0 && isvisiblebb(va->skymin, ivec(va->skymax).sub(va->skymin)) != VFC_NOT_VISIBLE) ||
|
|
!insideworld(camera1->o)))
|
|
{
|
|
limited = true;
|
|
break;
|
|
}
|
|
}
|
|
if(limited)
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
glDepthFunc(GL_LEQUAL);
|
|
glDepthMask(GL_FALSE);
|
|
}
|
|
|
|
if(clampsky) glDepthRange(1, 1);
|
|
|
|
if(clear || (!skybox[0] && (!atmo || atmoalpha < 1)))
|
|
{
|
|
vec skyboxcolor = skyboxcolour.tocolor().mul(ldrscale);
|
|
glClearColor(skyboxcolor.x, skyboxcolor.y, skyboxcolor.z, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
if(skybox[0])
|
|
{
|
|
if(ldrscale < 1 && (skyboxoverbrightmin != 1 || (skyboxoverbright > 1 && skyboxoverbrightthreshold < 1)))
|
|
{
|
|
SETSHADER(skyboxoverbright);
|
|
LOCALPARAMF(overbrightparams, skyboxoverbrightmin, max(skyboxoverbright, skyboxoverbrightmin), skyboxoverbrightthreshold);
|
|
}
|
|
else SETSHADER(skybox);
|
|
|
|
gle::color(skyboxcolour);
|
|
|
|
matrix4 skymatrix = cammatrix, skyprojmatrix;
|
|
skymatrix.settranslation(0, 0, 0);
|
|
skymatrix.rotate_around_z((spinsky*lastmillis/1000.0f+yawsky)*-RAD);
|
|
skyprojmatrix.mul(projmatrix, skymatrix);
|
|
LOCALPARAM(skymatrix, skyprojmatrix);
|
|
|
|
drawenvbox(sky);
|
|
}
|
|
|
|
if(atmo && (!skybox[0] || atmoalpha < 1))
|
|
{
|
|
if(atmoalpha < 1)
|
|
{
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
drawatmosphere();
|
|
|
|
if(atmoalpha < 1) glDisable(GL_BLEND);
|
|
}
|
|
|
|
if(fogdomemax && !fogdomeclouds)
|
|
{
|
|
drawfogdome();
|
|
}
|
|
|
|
if(cloudbox[0])
|
|
{
|
|
SETSHADER(skybox);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
gle::color(cloudboxcolour.tocolor(), cloudboxalpha);
|
|
|
|
matrix4 skymatrix = cammatrix, skyprojmatrix;
|
|
skymatrix.settranslation(0, 0, 0);
|
|
skymatrix.rotate_around_z((spinclouds*lastmillis/1000.0f+yawclouds)*-RAD);
|
|
skyprojmatrix.mul(projmatrix, skymatrix);
|
|
LOCALPARAM(skymatrix, skyprojmatrix);
|
|
|
|
drawenvbox(clouds, cloudclip);
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
if(cloudlayer[0] && cloudheight)
|
|
{
|
|
SETSHADER(skybox);
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
matrix4 skymatrix = cammatrix, skyprojmatrix;
|
|
skymatrix.settranslation(0, 0, 0);
|
|
skymatrix.rotate_around_z((spincloudlayer*lastmillis/1000.0f+yawcloudlayer)*-RAD);
|
|
skyprojmatrix.mul(projmatrix, skymatrix);
|
|
LOCALPARAM(skymatrix, skyprojmatrix);
|
|
|
|
drawenvoverlay(cloudoverlay, cloudoffsetx + cloudscrollx * lastmillis/1000.0f, cloudoffsety + cloudscrolly * lastmillis/1000.0f);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
|
|
if(fogdomemax && fogdomeclouds)
|
|
{
|
|
drawfogdome();
|
|
}
|
|
|
|
if(clampsky) glDepthRange(0, 1);
|
|
|
|
if(limited)
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
glDepthMask(GL_TRUE);
|
|
glDepthFunc(GL_LESS);
|
|
}
|
|
}
|
|
|
|
bool hasskybox()
|
|
{
|
|
return skybox[0] || atmo || fogdomemax || cloudbox[0] || cloudlayer[0];
|
|
}
|
|
|