mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 16:21:04 +02:00
365 lines
10 KiB
C++
365 lines
10 KiB
C++
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tr_local.h"
|
|
|
|
static srfTriangles_t* g_skyDomeTri = NULL;
|
|
static const idMaterial* g_skyDomeMat = NULL;
|
|
|
|
static idCVar r_skyDome("r_skyDome", "1", CVAR_RENDERER | CVAR_BOOL, "render procedural skydome");
|
|
static idCVar r_skyDomeRadius("r_skyDomeRadius", "8192", CVAR_RENDERER | CVAR_FLOAT, "skydome radius (world units)");
|
|
static idCVar r_skyDomeRings("r_skyDomeRings", "24", CVAR_RENDERER | CVAR_INTEGER, "skydome vertical subdivisions");
|
|
static idCVar r_skyDomeSegments("r_skyDomeSegments", "48", CVAR_RENDERER | CVAR_INTEGER, "skydome horizontal subdivisions");
|
|
|
|
static idCVar r_skyClouds("r_skyClouds", "1", CVAR_RENDERER | CVAR_BOOL, "render automatic raymarched sky clouds");
|
|
static idCVar r_skyCloudZMin("r_skyCloudZMin", "2600", CVAR_RENDERER | CVAR_FLOAT, "sky cloud slab minimum world z");
|
|
static idCVar r_skyCloudZMax("r_skyCloudZMax", "7600", CVAR_RENDERER | CVAR_FLOAT, "sky cloud slab maximum world z");
|
|
static idCVar r_skyCloudScale("r_skyCloudScale", "0.00021", CVAR_RENDERER | CVAR_FLOAT, "sky cloud world noise scale");
|
|
static idCVar r_skyCloudCoverage("r_skyCloudCoverage", "0.30", CVAR_RENDERER | CVAR_FLOAT, "sky cloud coverage threshold, lower is denser");
|
|
static idCVar r_skyCloudDensity("r_skyCloudDensity", "0.5", CVAR_RENDERER | CVAR_FLOAT, "sky cloud density accumulation");
|
|
static idCVar r_skyCloudOpacity("r_skyCloudOpacity", "0.78", CVAR_RENDERER | CVAR_FLOAT, "sky cloud final opacity");
|
|
static idCVar r_skyCloudSeedOffset("r_skyCloudSeedOffset", "12000", CVAR_RENDERER | CVAR_FLOAT, "sky cloud world noise seed offset");
|
|
static idCVar r_skyCloudColor("r_skyCloudColor", "auto", CVAR_RENDERER, "sky cloud color override as 'r g b' in 0..1 or 0..255; auto samples the skydome");
|
|
static idCVar r_skyCloudWind("r_skyCloudWind", "55 -18", CVAR_RENDERER, "sky cloud scroll wind as world x y units per second");
|
|
static idCVar r_skyCloudVolume("r_skyCloudVolume", "0.65", CVAR_RENDERER | CVAR_FLOAT, "cheap cloud self-shadow and height shaping amount");
|
|
static idCVar r_skyFog("r_skyFog", "1", CVAR_RENDERER | CVAR_BOOL, "render sky horizon fog");
|
|
static idCVar r_skyFogOpacity("r_skyFogOpacity", "0.36", CVAR_RENDERER | CVAR_FLOAT, "sky horizon fog opacity");
|
|
static idCVar r_skyFogHeight("r_skyFogHeight", "0.30", CVAR_RENDERER | CVAR_FLOAT, "view ray z height where sky horizon fog fades out");
|
|
static idCVar r_skyFogPower("r_skyFogPower", "1.15", CVAR_RENDERER | CVAR_FLOAT, "sky horizon fog falloff power");
|
|
|
|
static void R_UploadSkyDomeMeshToVertexCache(srfTriangles_t* tri) {
|
|
if (!tri) {
|
|
return;
|
|
}
|
|
|
|
if (tri->ambientCache) {
|
|
vertexCache.Free(tri->ambientCache);
|
|
tri->ambientCache = 0;
|
|
}
|
|
|
|
if (tri->indexCache) {
|
|
vertexCache.Free(tri->indexCache);
|
|
tri->indexCache = 0;
|
|
}
|
|
|
|
vertexCache.Alloc(
|
|
tri->verts,
|
|
tri->numVerts * sizeof(tri->verts[0]),
|
|
&tri->ambientCache
|
|
);
|
|
|
|
vertexCache.Alloc(
|
|
tri->indexes,
|
|
tri->numIndexes * sizeof(tri->indexes[0]),
|
|
&tri->indexCache
|
|
);
|
|
}
|
|
|
|
// Build a hemisphere, top half. Inside-facing draw.
|
|
static srfTriangles_t* R_BuildSkyDomeMesh(int rings, int segs, float radius) {
|
|
rings = Max(rings, 4);
|
|
segs = Max(segs, 8);
|
|
|
|
const int vertsPerRing = segs + 1;
|
|
const int numVerts = (rings + 1) * vertsPerRing;
|
|
|
|
const int numQuads = rings * segs;
|
|
const int numTris = numQuads * 2;
|
|
const int numIndexes = numTris * 3;
|
|
|
|
srfTriangles_t* tri = R_AllocStaticTriSurf();
|
|
R_AllocStaticTriSurfVerts(tri, numVerts);
|
|
R_AllocStaticTriSurfIndexes(tri, numIndexes);
|
|
|
|
idDrawVert* v = tri->verts;
|
|
glIndex_t* idx = tri->indexes;
|
|
|
|
int vi = 0;
|
|
|
|
for (int r = 0; r <= rings; r++) {
|
|
const float t = (float)r / (float)rings;
|
|
const float phi = t * idMath::HALF_PI;
|
|
|
|
const float sinPhi = idMath::Sin(phi);
|
|
const float cosPhi = idMath::Cos(phi);
|
|
|
|
for (int s = 0; s <= segs; s++) {
|
|
const float u = (float)s / (float)segs;
|
|
const float theta = u * idMath::TWO_PI;
|
|
|
|
const float sinTheta = idMath::Sin(theta);
|
|
const float cosTheta = idMath::Cos(theta);
|
|
|
|
idVec3 dir;
|
|
dir.x = cosTheta * cosPhi;
|
|
dir.y = sinTheta * cosPhi;
|
|
dir.z = sinPhi;
|
|
|
|
v[vi].xyz = dir * radius;
|
|
|
|
float uu = idMath::ATan(dir.y, dir.x) / idMath::TWO_PI + 0.5f;
|
|
float vv = idMath::ASin(idMath::ClampFloat(-1.0f, 1.0f, dir.z)) / idMath::PI + 0.5f;
|
|
|
|
vv = 1.0f - vv;
|
|
|
|
v[vi].st.x = uu;
|
|
v[vi].st.y = vv;
|
|
|
|
v[vi].normal = dir;
|
|
v[vi].tangents[0].Set(-sinTheta, cosTheta, 0.0f);
|
|
v[vi].tangents[1].Set(0.0f, 0.0f, 1.0f);
|
|
|
|
v[vi].color[0] = 255;
|
|
v[vi].color[1] = 255;
|
|
v[vi].color[2] = 255;
|
|
v[vi].color[3] = 255;
|
|
|
|
vi++;
|
|
}
|
|
}
|
|
|
|
int ii = 0;
|
|
|
|
for (int r = 0; r < rings; r++) {
|
|
for (int s = 0; s < segs; s++) {
|
|
const int i0 = r * vertsPerRing + s;
|
|
const int i1 = i0 + 1;
|
|
const int i2 = (r + 1) * vertsPerRing + s;
|
|
const int i3 = i2 + 1;
|
|
|
|
idx[ii++] = i0;
|
|
idx[ii++] = i2;
|
|
idx[ii++] = i1;
|
|
|
|
idx[ii++] = i1;
|
|
idx[ii++] = i2;
|
|
idx[ii++] = i3;
|
|
}
|
|
}
|
|
|
|
tri->numVerts = numVerts;
|
|
tri->numIndexes = numIndexes;
|
|
|
|
R_BoundTriSurf(tri);
|
|
|
|
R_UploadSkyDomeMeshToVertexCache(tri);
|
|
|
|
return tri;
|
|
}
|
|
|
|
static idImage* R_GetSkyDomeImage() {
|
|
if (!g_skyDomeMat || g_skyDomeMat->GetNumStages() <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
const shaderStage_t* stage = g_skyDomeMat->GetStage(0);
|
|
|
|
if (!stage || !stage->texture.image) {
|
|
return NULL;
|
|
}
|
|
|
|
return stage->texture.image;
|
|
}
|
|
|
|
static void R_GetSkyAverageColor(float color[3]) {
|
|
color[0] = 0.16f;
|
|
color[1] = 0.28f;
|
|
color[2] = 0.48f;
|
|
|
|
idImage* skyImage = R_GetSkyDomeImage();
|
|
|
|
if (!skyImage) {
|
|
return;
|
|
}
|
|
|
|
skyImage->Bind();
|
|
|
|
const float* average = skyImage->GetAverageColor();
|
|
|
|
if (!average) {
|
|
return;
|
|
}
|
|
|
|
color[0] = idMath::ClampFloat(0.03f, 1.0f, average[0]);
|
|
color[1] = idMath::ClampFloat(0.03f, 1.0f, average[1]);
|
|
color[2] = idMath::ClampFloat(0.03f, 1.0f, average[2]);
|
|
}
|
|
|
|
static void R_GetSkyCloudColor(float color[3]) {
|
|
R_GetSkyAverageColor(color);
|
|
|
|
float overrideColor[3];
|
|
if (sscanf(r_skyCloudColor.GetString(), "%f %f %f", &overrideColor[0], &overrideColor[1], &overrideColor[2]) != 3) {
|
|
return;
|
|
}
|
|
|
|
const bool byteColor = overrideColor[0] > 1.0f || overrideColor[1] > 1.0f || overrideColor[2] > 1.0f;
|
|
const float scale = byteColor ? (1.0f / 255.0f) : 1.0f;
|
|
color[0] = idMath::ClampFloat(0.0f, 1.0f, overrideColor[0] * scale);
|
|
color[1] = idMath::ClampFloat(0.0f, 1.0f, overrideColor[1] * scale);
|
|
color[2] = idMath::ClampFloat(0.0f, 1.0f, overrideColor[2] * scale);
|
|
}
|
|
|
|
static void RB_DrawSkyCloudOverlay(const viewDef_t* viewDef) {
|
|
if (!viewDef) {
|
|
return;
|
|
}
|
|
|
|
const bool drawClouds = r_skyClouds.GetBool();
|
|
const bool drawFog = r_skyFog.GetBool() && r_skyFogOpacity.GetFloat() > 0.0f;
|
|
|
|
if (!drawClouds && !drawFog) {
|
|
return;
|
|
}
|
|
|
|
glSkyClouds_t clouds;
|
|
memset(&clouds, 0, sizeof(clouds));
|
|
|
|
R_GetSkyCloudColor(clouds.skyColor);
|
|
R_GetSkyAverageColor(clouds.fogColor);
|
|
clouds.opacity = drawClouds ? idMath::ClampFloat(0.0f, 1.0f, r_skyCloudOpacity.GetFloat()) : 0.0f;
|
|
clouds.fogOpacity = drawFog ? idMath::ClampFloat(0.0f, 1.0f, r_skyFogOpacity.GetFloat()) : 0.0f;
|
|
clouds.fogHeight = Max(r_skyFogHeight.GetFloat(), 0.01f);
|
|
clouds.fogPower = Max(r_skyFogPower.GetFloat(), 0.25f);
|
|
|
|
const idVec3& viewOrg = viewDef->renderView.vieworg;
|
|
clouds.viewOrigin[0] = viewOrg.x;
|
|
clouds.viewOrigin[1] = viewOrg.y;
|
|
clouds.viewOrigin[2] = viewOrg.z;
|
|
clouds.zMin = r_skyCloudZMin.GetFloat();
|
|
clouds.zMax = Max(r_skyCloudZMax.GetFloat(), clouds.zMin + 64.0f);
|
|
|
|
const idMat3& axis = viewDef->renderView.viewaxis;
|
|
clouds.viewForward[0] = axis[0].x;
|
|
clouds.viewForward[1] = axis[0].y;
|
|
clouds.viewForward[2] = axis[0].z;
|
|
clouds.viewRight[0] = -axis[1].x;
|
|
clouds.viewRight[1] = -axis[1].y;
|
|
clouds.viewRight[2] = -axis[1].z;
|
|
clouds.viewUp[0] = axis[2].x;
|
|
clouds.viewUp[1] = axis[2].y;
|
|
clouds.viewUp[2] = axis[2].z;
|
|
clouds.tanHalfFovX = idMath::Tan(DEG2RAD(viewDef->renderView.fov_x * 0.5f));
|
|
clouds.tanHalfFovY = idMath::Tan(DEG2RAD(viewDef->renderView.fov_y * 0.5f));
|
|
clouds.noiseScale = Max(r_skyCloudScale.GetFloat(), 0.00001f);
|
|
clouds.coverage = idMath::ClampFloat(0.0f, 1.0f, r_skyCloudCoverage.GetFloat());
|
|
clouds.density = drawClouds ? Max(r_skyCloudDensity.GetFloat(), 0.0f) : 0.0f;
|
|
clouds.seedOffset = r_skyCloudSeedOffset.GetFloat();
|
|
clouds.time = viewDef->renderView.time * 0.001f;
|
|
clouds.wind[0] = 55.0f;
|
|
clouds.wind[1] = -18.0f;
|
|
sscanf(r_skyCloudWind.GetString(), "%f %f", &clouds.wind[0], &clouds.wind[1]);
|
|
clouds.volume = idMath::ClampFloat(0.0f, 1.0f, r_skyCloudVolume.GetFloat());
|
|
|
|
glDrawSkyCloudsQD3D12(&clouds);
|
|
}
|
|
|
|
void R_InitSkyDome() {
|
|
if (g_skyDomeTri) {
|
|
return;
|
|
}
|
|
|
|
g_skyDomeTri = R_BuildSkyDomeMesh(
|
|
r_skyDomeRings.GetInteger(),
|
|
r_skyDomeSegments.GetInteger(),
|
|
r_skyDomeRadius.GetFloat()
|
|
);
|
|
}
|
|
|
|
void R_ShutdownSkyDome() {
|
|
if (g_skyDomeTri) {
|
|
if (g_skyDomeTri->ambientCache) {
|
|
vertexCache.Free(g_skyDomeTri->ambientCache);
|
|
g_skyDomeTri->ambientCache = 0;
|
|
}
|
|
|
|
if (g_skyDomeTri->indexCache) {
|
|
vertexCache.Free(g_skyDomeTri->indexCache);
|
|
g_skyDomeTri->indexCache = 0;
|
|
}
|
|
|
|
R_FreeStaticTriSurf(g_skyDomeTri);
|
|
g_skyDomeTri = NULL;
|
|
}
|
|
|
|
g_skyDomeMat = NULL;
|
|
}
|
|
|
|
static const idMaterial* R_GetSkyDomeMaterial() {
|
|
return backEnd.viewDef->renderView.skyDomeMaterial;
|
|
}
|
|
|
|
void RB_DrawSkyDome(const viewDef_t* viewDef) {
|
|
if (!r_skyDome.GetBool()) {
|
|
return;
|
|
}
|
|
|
|
if (!g_skyDomeTri || !g_skyDomeTri->ambientCache || !g_skyDomeTri->indexCache) {
|
|
return;
|
|
}
|
|
|
|
g_skyDomeMat = R_GetSkyDomeMaterial();
|
|
|
|
if (!g_skyDomeMat) {
|
|
return;
|
|
}
|
|
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
|
|
float mv[16];
|
|
memcpy(mv, backEnd.viewDef->worldSpace.modelViewMatrix, sizeof(mv));
|
|
|
|
mv[12] = 0.0f;
|
|
mv[13] = 0.0f;
|
|
mv[14] = 0.0f;
|
|
|
|
glLoadMatrixf(mv);
|
|
|
|
GL_SelectTexture(0);
|
|
|
|
idImage* skyImage = R_GetSkyDomeImage();
|
|
|
|
if (skyImage) {
|
|
skyImage->Bind();
|
|
}
|
|
else {
|
|
globalImages->whiteImage->Bind();
|
|
}
|
|
|
|
idDrawVert* verts = (idDrawVert*)vertexCache.Position(g_skyDomeTri->ambientCache);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glVertexPointer(
|
|
3,
|
|
GL_FLOAT,
|
|
sizeof(idDrawVert),
|
|
verts->xyz.ToFloatPtr()
|
|
);
|
|
|
|
glTexCoordPointer(
|
|
2,
|
|
GL_FLOAT,
|
|
sizeof(idDrawVert),
|
|
verts->st.ToFloatPtr()
|
|
);
|
|
|
|
glGeometryFlagf(GEOMETRY_FLAG_UNLIT + GEOMETRY_FLAG_SKYDOME);
|
|
RB_DrawElementsWithCounters(g_skyDomeTri);
|
|
RB_DrawSkyCloudOverlay(viewDef);
|
|
glGeometryFlagf(GEOMETRY_FLAG_NONE);
|
|
|
|
glPopMatrix();
|
|
|
|
GL_State(GLS_DEFAULT);
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
}
|