/*
===========================================================================
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 - direct lighting from dynamic lights


#include "crp_local.h"
#include "compshaders/crp/fullscreen.h"
#include "compshaders/crp/dl_draw.h"
#include "compshaders/crp/dl_denoising.h"


#pragma pack(push, 4)

struct DynamicLightsRC
{
	uint32_t blueNoiseTextureIndex;
};


struct DenoiseRC
{
	uint32_t textureIndex;
};

#pragma pack(pop)


void DynamicLights::Init()
{
	if(!rhiInfo.hasInlineRaytracing)
	{
		return;
	}

	{
		GraphicsPipelineDesc desc("Dynamic Lights");
		desc.shortLifeTime = true;
		desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
		desc.pixelShader = ShaderByteCode(g_dl_draw_ps);
		desc.depthStencil.DisableDepth();
		desc.rasterizer.cullMode = CT_TWO_SIDED;
		desc.AddRenderTarget(0, crp.renderTargetFormat);
		pipeline = CreateGraphicsPipeline(desc);
	}

	{
		GraphicsPipelineDesc desc("Dynamic Lights Denoising");
		desc.shortLifeTime = true;
		desc.vertexShader = ShaderByteCode(g_fullscreen_vs);
		desc.pixelShader = ShaderByteCode(g_dl_denoising_ps);
		desc.depthStencil.DisableDepth();
		desc.rasterizer.cullMode = CT_TWO_SIDED;
		desc.AddRenderTarget(0, crp.renderTargetFormat);
		denoisingPipeline = CreateGraphicsPipeline(desc);
	}
}

void DynamicLights::Draw()
{
	if(r_dynamiclight->integer == 0 ||
		(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) != 0 ||
		!IsViewportFullscreen(backEnd.viewParms))
	{
		return;
	}

	if(!crp.raytracing.CanRaytrace() ||
		backEnd.refdef.num_dlights <= 0)
	{
		CmdBeginBarrier();
		CmdTextureBarrier(crp.lightTexture, ResourceStates::RenderTargetBit);
		CmdEndBarrier();
		CmdClearColorTarget(crp.lightTexture, colorBlack);
		return;
	}

	srp.renderMode = RenderMode::None;

	{
		SCOPED_RENDER_PASS("Dynamic Lights", 1.0f, 1.0f, 1.0f);

		CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);

		CmdBeginBarrier();
		CmdTextureBarrier(crp.shadingPositionTexture, ResourceStates::PixelShaderAccessBit);
		CmdTextureBarrier(crp.normalTexture, ResourceStates::PixelShaderAccessBit);
		CmdTextureBarrier(crp.noisyLightTexture, ResourceStates::RenderTargetBit);
		CmdEndBarrier();

		DynamicLightsRC rc = {};
		rc.blueNoiseTextureIndex = GetTextureIndexSRV(crp.blueNoise2D);
		CmdBindRenderTargets(1, &crp.noisyLightTexture, NULL);
		CmdBindPipeline(pipeline);
		CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
		CmdDraw(3, 0);
	}

	{
		SCOPED_RENDER_PASS("DL Denoising", 1.0f, 1.0f, 1.0f);

		CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);

		CmdBeginBarrier();
		CmdTextureBarrier(crp.shadingPositionTexture, ResourceStates::PixelShaderAccessBit);
		CmdTextureBarrier(crp.noisyLightTexture, ResourceStates::PixelShaderAccessBit);
		CmdTextureBarrier(crp.lightTexture, ResourceStates::RenderTargetBit);
		CmdEndBarrier();

		DenoiseRC rc = {};
		rc.textureIndex = GetTextureIndexSRV(crp.noisyLightTexture);
		CmdBindRenderTargets(1, &crp.lightTexture, NULL);
		CmdBindPipeline(denoisingPipeline);
		CmdSetGraphicsRootConstants(0, sizeof(rc), &rc);
		CmdDraw(3, 0);
	}
}

bool DynamicLights::WantRTASUpdate(const trRefdef_t& scene)
{
	return scene.num_dlights > 0;
}