// SPDX-License-Identifier: (Not available for this version, you are only allowed to use this software if you have express permission from the copyright holder and agreed to the latest NiloToonURP EULA)
// Copyright (c) 2021 Kuroneko ShaderLab Limited

// For more information, visit -> https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample

// #pragma once is a safe guard best practice in almost every .hlsl, 
// doing this can make sure your .hlsl's user can include this .hlsl anywhere anytime without producing any multi include conflict
#pragma once

//#include "NiloInvLerpRemapUtil.hlsl" // TODO: not sure why we can't include this, we should be able to include this if #pragma once exist in that .hlsl file

// warning: using perspective removal may make CPU per renderer culling not perfect, since CPU won't know what is happening in GPU
float4 NiloDoPerspectiveRemoval(float4 originalPositionCS, float perspectiveRemovalAmount, float centerPosVSz)
{
    // resources:
    // - https://zhuanlan.zhihu.com/p/268433650?utm_source=ZHShareTargetIDMore
    // - https://zhuanlan.zhihu.com/p/332804613

    // resources link's demo method
    /*
    float originalPositionCSZ = output.positionCS.z;
    float4 perspectiveCorrectPosVS = mul(UNITY_MATRIX_I_P, output.positionCS);
    perspectiveCorrectPosVS.z -= centerPosVSz;
    perspectiveCorrectPosVS.z *= lerp(1,0.1,perspectiveCorrectUsage); // Flatten model's pos z in view space
    perspectiveCorrectPosVS.z += centerPosVSz;    
    output.positionCS = mul(UNITY_MATRIX_P, perspectiveCorrectPosVS);
    output.positionCS.z = originalPositionCSZ;
    */

    // our method
    float2 newPosCSxy = originalPositionCS.xy;
    newPosCSxy *= abs(originalPositionCS.w); // cancel Hardware w-divide
    newPosCSxy *= rcp(abs(centerPosVSz)); // do our flattened w-divide
    originalPositionCS.xy = lerp(originalPositionCS.xy, newPosCSxy, perspectiveRemovalAmount); // apply 0~100% perspective removal  

    return originalPositionCS;    
}

// this global float can be optionally controlled if user call to NiloToonPlanarReflectionHelper.cs
// originally added for supporting planar reflection (CalculateObliqueMatrix will make our perspective removal method fail)
float _GlobalShouldDisableNiloToonPerspectiveRemoval; // default 0 in GPU, so even no one assign this float, the code will still function correctly by default

// high level helper function
float4 NiloDoPerspectiveRemoval(float4 originalPositionCS, float3 positionWS, float3 removalCenterPositionWS, float removalRadius, float removalAmount, float removalStartHeight, float removalEndHeight)
{
    // only do perspective removal if is perspective camera
    // high level function contain global disable logic, to reduce code complexity of this .hlsl's user code
    if(_GlobalShouldDisableNiloToonPerspectiveRemoval || !IsPerspectiveProjection())
        return originalPositionCS;

    float perspectiveRemovalAreaSphere = saturate(removalRadius - distance(positionWS,removalCenterPositionWS) / removalRadius);
    float perspectiveRemovalAreaWorldHeight = saturate(invLerp(removalStartHeight, removalEndHeight, positionWS.y));
    float perspectiveRemovalFinalAmount = removalAmount * perspectiveRemovalAreaSphere * perspectiveRemovalAreaWorldHeight;
    float centerPosVSz = mul(UNITY_MATRIX_V, float4(removalCenterPositionWS,1)).z;

    return NiloDoPerspectiveRemoval(originalPositionCS, perspectiveRemovalFinalAmount, centerPosVSz);
}
