Files
DoomRTX/neo/engine/renderer/tr_skydome.cpp
T
2026-05-28 01:09:16 -07:00

343 lines
8.8 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", "0.85 0.62 0.48", CVAR_RENDERER, "sky cloud color override as 'r g b' in 0..1 or 0..255; auto samples the skydome");
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 || !r_skyClouds.GetBool()) {
return;
}
glSkyClouds_t clouds;
memset(&clouds, 0, sizeof(clouds));
R_GetSkyCloudColor(clouds.skyColor);
clouds.opacity = idMath::ClampFloat(0.0f, 1.0f, r_skyCloudOpacity.GetFloat());
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 = Max(r_skyCloudDensity.GetFloat(), 0.0f);
clouds.seedOffset = r_skyCloudSeedOffset.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);
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);
}