/******************************************************************************/
/*
  Project   - MudBun
  Publisher - Long Bunny Labs
              http://LongBunnyLabs.com
  Author    - Ming-Lun "Allen" Chou
              http://AllenChou.net
*/
/******************************************************************************/

#ifndef MUDBUN_SHADER_COMMON
#define MUDBUN_SHADER_COMMON

#include "../Math/Codec.cginc"
#include "../Math/MathConst.cginc"
#include "../Math/Vector.cginc"
#include "../Noise/RandomNoise.cginc"

#if defined(SHADER_API_D3D11) || defined(SHADER_API_VULKAN) || defined(SHADER_API_GLCORE) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) || defined(SHADER_API_METAL)
  #define MUDBUN_VALID (1)
#endif

#ifdef MUDBUN_VALID
  #include "../Noise/ClassicNoise3D.cginc"
  #include "../Noise/SimplexNoise3D.cginc"
#endif

#ifdef SHADERPASS // HDRP & URP
  #if SHADERPASS == SHADERPASS_SHADOWS && !defined(SHADERPASS_SHADOWCASTER)
    #define SHADERPASS_SHADOWCASTER
    #define MUDBUN_SHADOW_PASS
  #endif
#endif

#if defined(UNITY_PASS_SHADOWCASTER) && !defined(SHADERPASS_SHADOWCASTER) // built-in RP
  #define SHADERPASS_SHADOWCASTER
  #define MUDBUN_SHADOW_PASS
#endif

int enable2dMode;
int splatNormalsMatchCameraFacing;
int splatShadowsMatchCameraFacing;

float4 _Color;
float4 _Emission;
#ifndef MUDBUN_URP_TEMPLATE
float _Metallic;
float _Smoothness;
#endif

#ifdef MUDBUN_BUILT_IN_RP
float _AlphaCutoutThreshold;
float _Dithering;
int _RandomDither;

sampler2D _DitherTexture;
int _DitherTextureSize;

int _UseTex0;
sampler2D _MainTex;
float4 _MainTex_ST;
int _MainTexX;
int _MainTexY;
int _MainTexZ;

int _UseTex1;
sampler2D _Tex1;
float4 _Tex1_ST;
int _Tex1X;
int _Tex1Y;
int _Tex1Z;

int _UseTex2;
sampler2D _Tex2;
float4 _Tex2_ST;
int _Tex2X;
int _Tex2Y;
int _Tex2Z;

int _UseTex3;
sampler2D _Tex3;
float4 _Tex3_ST;
int _Tex3X;
int _Tex3Y;
int _Tex3Z;

int _UseNorm0;
sampler2D _MainNorm;
float4 _MainNorm_ST;
int _MainNormX;
int _MainNormY;
int _MainNormZ;

int _UseNorm1;
sampler2D _Norm1;
float4 _Norm1_ST;
int _Norm1X;
int _Norm1Y;
int _Norm1Z;

int _UseNorm2;
sampler2D _Norm2;
float4 _Norm2_ST;
int _Norm2X;
int _Norm2Y;
int _Norm2Z;

int _UseNorm3;
sampler2D _Norm3;
float4 _Norm3_ST;
int _Norm3X;
int _Norm3Y;
int _Norm3Z;
#endif

int scaleSign;
float splatSize;
float splatSizeJitter;
float splatNormalShift;
float splatNormalShiftJitter;
float splatColorJitter;
float splatPositionJitter;
float splatRotationJitter;
float splatOrientationJitter;
float splatOriginalNormalBlend;
float splatJitterNoisiness;
float splatCameraFacing;
float splatScreenSpaceFlattening;
//float splatSmoothNormalBlend;

float4x4 localToWorld;
float4x4 localToWorldIt;
float4 localToWorldScale;
float4x4 worldToLocal;
float4x4 worldToLocalIt;

struct Vertex
{
  float4 vertex    : POSITION;
  float3 normal    : NORMAL;
  float4 tangent   : TANGENT;
  float4 color     : COLOR;
  float4 texcoord1 : TEXCOORD1;
  float4 texcoord2 : TEXCOORD2;
  uint id          : SV_VertexID;

  UNITY_VERTEX_INPUT_INSTANCE_ID
  UNITY_VERTEX_OUTPUT_STEREO
};

#ifndef SHADER_GRAPH
struct Input
{
  float2 tex                 : TEXCOORD0;
  float4 color               : COLOR;
  float4 tangent             : TANGENT;
  float4 emissionHash        : TEXCOORD3;
  float2 metallicSmoothness  : TEXCOORD4;
  float4 texWeight           : TEXCOORD5;
  float3 localPos            : TEXCOORD6;
  float3 localNorm           : TEXCOORD7;
  float4 screenPos;
};
#endif

void computeOpaqueTransparency
(
  float2 screenPos, 
  float3 pos, 
  float hash, 
  sampler2D ditherTexture, 
  int ditherTextureSize, 
  bool useRandomDither, 
  float alphaCutoutThreshold, 
  float ditheringBlend, 
  inout float alpha, 
  out float alphaThreshold
)
{
  alpha = saturate(1.02f * (alpha - 0.5f) + 0.5f);

  float ditherThreshold = 0.0f;
  if (useRandomDither > 0)
  {
    ditherThreshold = mbn_rand(pos);
  }
  else
  {
    ditherThreshold = tex2D(ditherTexture, screenPos / ditherTextureSize).r;
  }

  ditherThreshold = 0.98f * (ditherThreshold - 0.5f) + 0.5f;

  alphaThreshold = lerp(alphaCutoutThreshold, max(alphaCutoutThreshold, ditherThreshold), ditheringBlend);
}

float4 tex2D_triplanar
(
  sampler2D tex, 
  float4 texSt, 
  float3 weight, 
  float3 localPos, 
  bool projectX, 
  bool projectY, 
  bool projectZ
)
{
  float4 color = 0.0f;
  float totalWeight = 0.0f;
  if (projectX)
  {
    color += tex2D(tex, localPos.yz * texSt.xy + texSt.zw) * weight.x;
    totalWeight += weight.x;
  }
  if (projectY)
  {
    color += tex2D(tex, localPos.zx * texSt.xy + texSt.zw) * weight.y;
    totalWeight += weight.y;
  }
  if (projectZ)
  {
    color += tex2D(tex, localPos.xy * texSt.xy + texSt.zw) * weight.z;
    totalWeight += weight.z;
  }

  if (totalWeight <= 0.0f)
    return 1.0f;

  return color / totalWeight;
}

float3 compute_tangent(float3 normal)
{
  const float kTangentFallbackBlendDist = 0.3f;
  const float kTangentFallbackBlendDistComp = 1.0f - kTangentFallbackBlendDist;

  float normalAbsY = abs(normal.y);
  float3 tangentDefault = cross(normal, kUnitY);
  float3 tangentFallback = sign(normal.y) * cross(normal, kUnitZ);

  float3 tangent = (normalAbsY < kTangentFallbackBlendDistComp) ? tangentDefault : tangentFallback;
  if (normalAbsY > 0.0001f)
   tangent = lerp(tangentDefault, tangentFallback, saturate((normalAbsY - kTangentFallbackBlendDistComp) / kTangentFallbackBlendDist));

  return normalize(tangent);
}

inline float3 gamma_to_linear_space(float3 sRGB)
{
  // Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
  return sRGB * (sRGB * (sRGB * 0.305306011f + 0.682171111f) + 0.012522878f);
}

#endif

