float4 TESR_ParallaxData : register(c8);
float4 TESR_TextureData : register(c9);

#define	g_nLODThreshold 10
#define	bBlendThreshold	(g_nLODThreshold / 2)
#define	bBlendRange		(g_nLODThreshold - bBlendThreshold)
#define	bBlendMIPs		(fMipLevelFrac = fMipLevel - (float)bBlendThreshold) > 0
#define	bBlendFraction	(fMipLevelFrac / bBlendRange)

void psParallax(in float2 BaseUV, in float3 CameraDir, inout float2 uv, inout float ao) {
	
	if (TESR_ParallaxData.y == -1.0f) return;
	
	float2 iParallaxOffset = CameraDir.xy / length(CameraDir.xyz);
	iParallaxOffset *= TESR_ParallaxData.x;

	float viewAngle = saturate(dot(normalize(CameraDir), float3(0.0f, 0.0f, 1.0f)));
	float2 fTexCoordsPerSize = BaseUV.xy * TESR_TextureData.xy;
	float2 dxSize, dySize;
	float2 dx, dy;
	float4(dxSize, dx) = ddx(float4(fTexCoordsPerSize, BaseUV.xy));
	float4(dySize, dy) = ddy(float4(fTexCoordsPerSize, BaseUV.xy));
	float  fMipLevel;
	float  fMipLevelInt;
	float  fMipLevelFrac;
	float  fMinTexCoordDelta;
	float2 dTexCoords = dxSize * dxSize + dySize * dySize;

	fMinTexCoordDelta = max(dTexCoords.x, dTexCoords.y);
	fMipLevel = max(0.5 * log2(fMinTexCoordDelta), 0);

	float2 texSample = uv.xy;
	float fOcclusionShadow = 1.0;

	if (fMipLevel <= (float)g_nLODThreshold) {
	   int nNumSteps = (int)lerp(TESR_ParallaxData.z, TESR_ParallaxData.y, viewAngle);
	   float fCurrHeight = 0.0f;
	   float fStepSize   = 1.0f / (float)nNumSteps;
	   float fPrevHeight = 1.0f;
	   float fNextHeight = 0.0f;
	   int    nStepIndex = 0;
	   bool   bCondition = true;
	   float2 vTexOffsetPerStep = fStepSize * iParallaxOffset;
	   float2 vTexCurrentOffset = BaseUV.xy;
	   float  fCurrentBound     = 1.0f;
	   float  fParallaxAmount   = 0.0f;
	   float2 pt1 = 0.0f;
	   float2 pt2 = 0.0f;
	   float2 texOffset2 = 0.0f;

	   while (nStepIndex < nNumSteps) {
	      vTexCurrentOffset -= vTexOffsetPerStep;
	      fCurrHeight = tex2Dgrad(TESR_samplerBaseMap, vTexCurrentOffset, dx, dy).a;
	      fCurrentBound -= fStepSize;
	      if (fCurrHeight > fCurrentBound) {
	         pt1 = float2(fCurrentBound            , fCurrHeight);
	         pt2 = float2(fCurrentBound + fStepSize, fPrevHeight);
	         texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
	         nStepIndex = nNumSteps + 1;
	      }
	      else {
	         fPrevHeight = fCurrHeight;
	         nStepIndex++;
	      }
	   }

	   float fDelta2 = pt2.x - pt2.y;
	   float fDelta1 = pt1.x - pt1.y;
	   float fDenominator = fDelta2 - fDelta1;

	   if (fDenominator == 0.0f)
	       fParallaxAmount = 0.0f;
	   else
	       fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1) / fDenominator;

	   float2 vParallaxOffset = iParallaxOffset * (1 - fParallaxAmount);
	   float2 texSampleBase = BaseUV.xy - vParallaxOffset;
	   texSample = texSampleBase;

	   if (bBlendMIPs) {
	      fMipLevelFrac = pow(frac(bBlendFraction), 2);
	      texSample = lerp(texSampleBase, uv, fMipLevelFrac);
	      fOcclusionShadow = lerp(fOcclusionShadow, 1, fMipLevelFrac);
	   } 

	}
	uv = texSample;
	ao = fOcclusionShadow;
}