cnq3/code/renderer/crp_fog.cpp

208 lines
5.7 KiB
C++

/*
===========================================================================
Copyright (C) 2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
Challenge Quake 3 is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Challenge Quake 3 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Cinematic Rendering Pipeline - fog volumes
#include "crp_local.h"
namespace fog_outside
{
#include "compshaders/crp/fog_outside_vs.h"
#include "compshaders/crp/fog_outside_ps.h"
}
namespace fog_inside
{
#include "compshaders/crp/fog_inside_vs.h"
#include "compshaders/crp/fog_inside_ps.h"
}
#pragma pack(push, 4)
struct FogRC
{
float modelViewMatrix[16];
float projectionMatrix[16];
float boxMin[4];
float boxMax[4];
float color[4];
float depth;
float linearDepthA;
float linearDepthB;
uint32_t depthTextureIndex;
};
#pragma pack(pop)
void Fog::Init()
{
{
const uint32_t indices[] =
{
0, 1, 2, 2, 1, 3,
4, 0, 6, 6, 0, 2,
7, 5, 6, 6, 5, 4,
3, 1, 7, 7, 1, 5,
4, 5, 0, 0, 5, 1,
3, 7, 2, 2, 7, 6
};
BufferDesc desc("box index", sizeof(indices), ResourceStates::IndexBufferBit);
desc.shortLifeTime = true;
boxIndexBuffer = CreateBuffer(desc);
uint8_t* mapped = BeginBufferUpload(boxIndexBuffer);
memcpy(mapped, indices, sizeof(indices));
EndBufferUpload(boxIndexBuffer);
}
{
const float vertices[] =
{
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f
};
BufferDesc desc("box vertex", sizeof(vertices), ResourceStates::VertexBufferBit);
desc.shortLifeTime = true;
boxVertexBuffer = CreateBuffer(desc);
uint8_t* mapped = BeginBufferUpload(boxVertexBuffer);
memcpy(mapped, vertices, sizeof(vertices));
EndBufferUpload(boxVertexBuffer);
}
{
GraphicsPipelineDesc desc("fog outside");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(fog_outside::g_vs);
desc.pixelShader = ShaderByteCode(fog_outside::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_BACK_SIDED;
desc.rasterizer.polygonOffset = false;
desc.rasterizer.clampDepth = true;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, crp.renderTargetFormat);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, DataType::Float32, 3, 0);
fogOutsidePipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("fog inside");
desc.shortLifeTime = true;
desc.vertexShader = ShaderByteCode(fog_inside::g_vs);
desc.pixelShader = ShaderByteCode(fog_inside::g_ps);
desc.depthStencil.DisableDepth();
desc.rasterizer.cullMode = CT_FRONT_SIDED;
desc.rasterizer.polygonOffset = false;
desc.rasterizer.clampDepth = true;
desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, crp.renderTargetFormat);
desc.vertexLayout.AddAttribute(0, ShaderSemantic::Position, DataType::Float32, 3, 0);
fogInsidePipeline = CreateGraphicsPipeline(desc);
}
}
void Fog::Draw()
{
// @NOTE: fog 0 is invalid, it must be skipped
if(tr.world == NULL ||
tr.world->numfogs <= 1 ||
(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0)
{
return;
}
SCOPED_RENDER_PASS("Fog", 0.25f, 0.125f, 0.0f);
srp.renderMode = RenderMode::World;
const uint32_t stride = sizeof(vec3_t);
CmdBindVertexBuffers(1, &boxVertexBuffer, &stride, NULL);
CmdBindIndexBuffer(boxIndexBuffer, IndexType::UInt32, 0);
const TextureBarrier barriers[] =
{
TextureBarrier(crp.depthTexture, ResourceStates::PixelShaderAccessBit),
TextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit)
};
CmdBarrier(ARRAY_LEN(barriers), barriers);
CmdBindRenderTargets(1, &crp.renderTarget, NULL);
int insideIndex = -1;
for(int f = 1; f < tr.world->numfogs; ++f)
{
const fog_t& fog = tr.world->fogs[f];
bool inside = true;
for(int a = 0; a < 3; ++a)
{
if(backEnd.viewParms.orient.origin[a] <= fog.bounds[0][a] ||
backEnd.viewParms.orient.origin[a] >= fog.bounds[1][a])
{
inside = false;
break;
}
}
if(inside)
{
insideIndex = f;
break;
}
}
FogRC rc = {};
memcpy(rc.modelViewMatrix, backEnd.viewParms.world.modelMatrix, sizeof(rc.modelViewMatrix));
memcpy(rc.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(rc.projectionMatrix));
RB_LinearDepthConstants(&rc.linearDepthA, &rc.linearDepthB);
rc.depthTextureIndex = GetTextureIndexSRV(crp.depthTexture);
CmdBindPipeline(fogOutsidePipeline);
for(int f = 1; f < tr.world->numfogs; ++f)
{
if(f == insideIndex)
{
continue;
}
const fog_t& fog = tr.world->fogs[f];
VectorScale(fog.parms.color, tr.identityLight, rc.color);
rc.depth = fog.parms.depthForOpaque;
VectorCopy(fog.bounds[0], rc.boxMin);
VectorCopy(fog.bounds[1], rc.boxMax);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDrawIndexed(36, 0, 0);
}
if(insideIndex > 0)
{
CmdBindPipeline(fogInsidePipeline);
const fog_t& fog = tr.world->fogs[insideIndex];
VectorScale(fog.parms.color, tr.identityLight, rc.color);
rc.depth = fog.parms.depthForOpaque;
VectorCopy(fog.bounds[0], rc.boxMin);
VectorCopy(fog.bounds[1], rc.boxMax);
CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
CmdDrawIndexed(36, 0, 0);
}
}