/* =========================================================================== 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 . =========================================================================== */ // Cinematic Rendering Pipeline - fog volumes #include "crp_local.h" #include "compshaders/crp/fog_outside.h" #include "compshaders/crp/fog_inside.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(g_outside_vs); desc.pixelShader = ShaderByteCode(g_outside_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(g_inside_vs); desc.pixelShader = ShaderByteCode(g_inside_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); } }