#version 450
/*------------------------------------------------------------------------------------------------
VolumetricHologram | FRAGMENT_SHADER
--------------------------------------------------------------------------------------------------
Unlit Shader meant to create rich-detailed, and highly optimized holograms meant to look 
like made of thousands of particles. Fixed-function Animations to take advantage of the
many "Quads" that compose the Mesh
------------------------------------------------------------------------------------------------*/

//------------------------------------------------------------------------------------------------
// Input and Output
//------------------------------------------------

// IN
layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec3 fragWorldPos;
layout(location = 2) in vec3 fragWorldNormal;
layout(location = 3) in vec4 fragData;

// OUT
layout(location = 0) out vec4 OutColor;
layout(location = 1) out vec4 OutEmissive;

//------------------------------------------------------------------------------------------------
// Custom Shader Utility and Helper Files
//------------------------------------------------

// Neccessary for cshf
#define FRAGMENT_SHADER

#include "UtilityCommon.cshf"
#define GLOBAL_UNIFORM_BINDING 0
#include "UniformCommon.cshf"
#include "PushCommon.cshf"
#define CUSTOM_UNIFORM_BINDING 2
#include "CustomParamsCommon.cshf"

//------------------------------------------------------------------------------------------------
// Texture Samplers
//------------------------------------------------

layout(binding = 1) uniform sampler2D PatternSampler;

//------------------------------------------------------------------------------------------------
// Parameters
//------------------------------------------------

// From Custom Params Uniform Buffer
#define uc_patternRepeat		custBuffer.paramsA.x
#define uc_patternRate			custBuffer.paramsA.y
#define uc_viewCullThreshold	custBuffer.paramsB.x
#define uc_emissiveIntensity	custBuffer.paramsD.x
#define uc_enableAnimPattern	custBuffer.paramsD.z
#define uc_enableLighting		custBuffer.paramsE.z

#define uc_animSliderB			custBuffer.paramsE.y
#define uc_chunkCount			custBuffer.paramsG.w

// Color and Alpha Params that should be passed in 
#define uc_hologramColor 		vec3(0.05f, 0.3f, 1.0f)
#define uc_insideColor 			vec3(1.0f, 0.0f, 0.0f)
#define uc_hologramAlpha 		0.45f

//------------------------------------------------------------------------------------------------
// HELPER FUNCTIONS
//------------------------------------------------

// Returns Green if Quad stays, Red if Quad will Cull
vec3 debug_view_culling(const vec3 worldNormal, const vec3 testDir)
{
	vec3 debugColor = vec3(1,0,0);
	const float dotTest = dot(worldNormal, testDir);
	if (dotTest > uc_viewCullThreshold)
	{
		debugColor = vec3(0,1,0);
	}
	return debugColor;

} // debug_view_culling()

//------------------------------------------------

// use a 16x16 noise Texture to create a looping pattern to make Quad seem like multiple "particles"
float calc_anim_pattern(const vec3 patterns, const float id)
{
	// Constant Params for Noise Pattern
	const float kRandomOffsetRate = 1;
	const float kNoiseOffsetRate = 3;
	const float kIdRange = 1.0f;

	// From Texture
	const float orderedSample = patterns.r;
	const float randomSample = patterns.g;
	const float noiseSample = patterns.b;

	const float idOffset = id * kIdRange;
	const float patternTime = uBuffer.gameTime * uc_patternRate;
	const float offsetTime = patternTime - idOffset;

	// Use Ordered Pattern for further randomizing the other patterns
	const float orderedOffsetA = mod(orderedSample + patternTime, 128);
	const float orderedOffsetB = mod(orderedSample + offsetTime, 255);

	const float randomTime = (offsetTime + orderedOffsetA) * kRandomOffsetRate;
	const float randomOffset = fract(randomSample + randomTime);

	const float noiseTime = (offsetTime + orderedOffsetB) * kNoiseOffsetRate;
	const float noiseOffset = fract(noiseSample + noiseTime);

	const float threshold = 1.0f - uc_chunkCount;
	const float randomNoise = randomOffset * noiseOffset;
	const float finalPattern = su_saturate(randomNoise - threshold);

	return finalPattern;

} // calc_anim_pattern()

//------------------------------------------------------------------------------------------------
// MAIN
//------------------------------------------------

void main()
{
	// Optimization and Feature Flags
	const bool enableAnimPatternFlag = (uc_enableAnimPattern != 0.0f);
	const bool enableLightingFlag = (uc_enableLighting != 0.0f);

	// Fragment Data
	const vec3 worldPos = fragWorldPos;
	const vec3 worldNormal = normalize(fragWorldNormal);

	const vec3 worldCamPos = uBuffer.invViewMat[3].xyz;
	const vec3 viewDir = normalize(worldCamPos - worldPos);
	const float decompressedId = fragColor.a;

	// Sample Pattern Texture
	const vec2 fragUV = fragData.xy;
	const vec2 patternUV = fragUV * uc_patternRepeat;
	const vec4 patternSample = texture(PatternSampler, patternUV);

	// Animated Texture Pattern
	float patternAlpha = patternSample.r;
	if (enableAnimPatternFlag)
	{
		patternAlpha = calc_anim_pattern(patternSample.rgb, decompressedId);
	}

	// Composite a Color Together
	const float quadDepth = fragData.z;
	const float depthBlend = ceil(quadDepth - 0.1f);
	const vec3 tempGradientColor = (fragColor.rgb - 0.5f) * 3.0f;
	const vec3 lighting = fragColor.rgb;
	const vec3 gradientColor = su_saturate(tempGradientColor);
	vec3 blendColor = mix(uc_hologramColor, uc_insideColor, depthBlend);

	const float finalAlpha = patternAlpha * uc_hologramAlpha;
	const vec3 finalColor = blendColor * finalAlpha * lighting;
	const vec3 finalEmissive = finalColor * uc_emissiveIntensity;

	// Output
	stencil_alpha_test(finalAlpha, 0.05f);
	OutColor = vec4(finalColor, finalAlpha);
	OutEmissive = vec4(finalEmissive, finalAlpha);
	
} // main()