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


#include "common.hlsli"
#include "fullscreen.hlsli"
#include "scene_view.h.hlsli"


cbuffer RootConstants
{
	uint textureIndex;
};

float4 ps(VOut input) : SV_Target
{
	SceneView scene = GetSceneView();
	Texture2D shadingPositionTexture = ResourceDescriptorHeap[scene.shadingPositionTextureIndex];
	Texture2D texture0 = ResourceDescriptorHeap[textureIndex];
	int2 textureMax = GetTextureSize(texture0) - int2(1, 1);

	int2 tcFrag = int2(input.position.xy);
	float3 positionFrag = shadingPositionTexture.Load(uint3(tcFrag, 0)).xyz;
	float distThreshold = 8.0;

	float maxError = 0.0;
	for(int y = -4; y <= 4; y++)
	{
		for(int x = -4; x <= 4; x++)
		{
			int2 tc = tcFrag + int2(x, y);
			if(!IsValueInRange(tcFrag, int2(0, 0), textureMax))
			{
				continue;
			}

			uint3 tc3 = uint3(tc, 0);
			float errorFrag = texture0.Load(tc3).w;
			maxError = max(maxError, errorFrag);
		}
	}
	maxError = saturate(maxError);

	float3 accum = float3(0, 0, 0);
	float weightSum = 0.0;
	int blurRadius = 1 + int(maxError * 8.0);
	for(int y = -blurRadius; y <= blurRadius; y++)
	{
		for(int x = -blurRadius; x <= blurRadius; x++)
		{
			int2 tc = tcFrag + int2(x, y);
			if(!IsValueInRange(tcFrag, int2(0, 0), textureMax))
			{
				continue;
			}

			uint3 tc3 = uint3(tc, 0);
			float3 positionSample = shadingPositionTexture.Load(tc3).xyz;
			float3 colorSample = texture0.Load(tc3).rgb;
			float posWeight = 1.0 - saturate(distance(positionSample, positionFrag) / distThreshold);
			float weight = posWeight;
			accum += colorSample * posWeight;
			weightSum += weight;
		}
	}

	if(weightSum > 0.0)
	{
		accum /= weightSum;
	}
	float4 result = float4(accum, 1);

	return result;
}
