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

#ifndef MUDBUN_NORMAL_FUNCS
#define MUDBUN_NORMAL_FUNCS

#include "NormalDefs.cginc"
#include "VoxelDefs.cginc"

#include "../Customization/CustomBrush.cginc"

// http://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm

// central differences
#define sdf_normal_diff(p, sdf_func, iBrushMask, h)                                                                    \
  normalize_safe                                                                                                       \
  (                                                                                                                    \
    float3                                                                                                             \
    (                                                                                                                  \
      sdf_func((p), float3(   (h), 0.0f, 0.0f), (iBrushMask)) - sdf_func((p), float3((-h), 0.0f, 0.0f), (iBrushMask)), \
      sdf_func((p), float3(0.0f,    (h), 0.0f), (iBrushMask)) - sdf_func((p), float3(0.0f, (-h), 0.0f), (iBrushMask)), \
      sdf_func((p), float3(0.0f, 0.0f,    (h)), (iBrushMask)) - sdf_func((p), float3(0.0f, 0.0f, (-h)), (iBrushMask))  \
    ),                                                                                                                 \
    float3(0.0f, 0.0f, 0.0f)                                                                                           \
  )

// tetrahedron technique
#define sdf_normal_tetra(p, sdf_func, iBrushMask, h)                                       \
  normalize_safe                                                                           \
  (                                                                                        \
      float3( 1.0f, -1.0f, -1.0f) * sdf_func((p), float3( (h), -(h), -(h)), (iBrushMask))  \
    + float3(-1.0f, -1.0f,  1.0f) * sdf_func((p), float3(-(h), -(h),  (h)), (iBrushMask))  \
    + float3(-1.0f,  1.0f, -1.0f) * sdf_func((p), float3(-(h),  (h), -(h)), (iBrushMask))  \
    + float3( 1.0f,  1.0f,  1.0f) * sdf_func((p), float3( (h),  (h),  (h)), (iBrushMask)), \
    float3(0.0f, 0.0f, 0.0f)                                                               \
  )

// use tetrahedron technique as default
#define sdf_normal(p, sdf_func, iBrushMask, h) sdf_normal_tetra(p, sdf_func, iBrushMask, h)

// macro that generates less inline code
#define SDF_NORMAL(normal, p, sdf_func, iBrushMask, h)                          \
  {                                                                             \
    float3 aSign[4] =                                                           \
    {                                                                           \
      float3( 1.0f, -1.0f, -1.0f),                                              \
      float3(-1.0f, -1.0f,  1.0f),                                              \
      float3(-1.0f,  1.0f, -1.0f),                                              \
      float3( 1.0f,  1.0f,  1.0f),                                              \
    };                                                                          \
    float3 aDelta[4] =                                                          \
    {                                                                           \
      float3( (h), -(h), -(h)),                                                 \
      float3(-(h), -(h),  (h)),                                                 \
      float3(-(h),  (h), -(h)),                                                 \
      float3( (h * 1.0001f), (h * 1.0002f), (h * 1.0003f)),                     \
    };                                                                          \
    float3 s = 0.0f;                                                            \
    SdfBrushMaterial nmat;                                                      \
    [loop] for (int iDelta = 0; iDelta < 4; ++iDelta)                           \
      s += aSign[iDelta] * sdf_func((p) + aDelta[iDelta], (iBrushMask), nmat, false); \
    normal = normalize_safe(s, float3(0.0f, 0.0f, 0.0f));                       \
  }

// macro that generates less inline code
#define SDF_NORMAL_2D(normal, p, sdf_func, iBrushMask, h)                       \
  {                                                                             \
    float3 aSign[4] =                                                           \
    {                                                                           \
      float3( 1.0f, -1.0f, 0.0f),                                               \
      float3(-1.0f, -1.0f, 0.0f),                                               \
      float3(-1.0f,  1.0f, 0.0f),                                               \
      float3( 1.0f,  1.0f, 0.0f),                                               \
    };                                                                          \
    float3 aDelta[4] =                                                          \
    {                                                                           \
      float3( (h), -(h), 0.0f),                                                 \
      float3(-(h), -(h), 0.0f),                                                 \
      float3(-(h),  (h), 0.0f),                                                 \
      float3( (h * 1.0001f), (h * 1.0002f), 0.0f),                              \
    };                                                                          \
    float3 s = 0.0f;                                                            \
    SdfBrushMaterial nmat;                                                      \
    [loop] for (int iDelta = 0; iDelta < 4; ++iDelta)                           \
      s += aSign[iDelta] * sdf_func((p) + aDelta[iDelta], (iBrushMask), nmat, false); \
    normal = normalize_safe(float3(s.xy, 0.0f), float3(0.0f, 0.0f, -1.0f));     \
  }

// macro that generates less inline code
#define SDF_NORMAL_FULL(normal, p, sdf_func, iBrushMask, h)                     \
  {                                                                             \
    float3 aDelta[6] =                                                          \
    {                                                                           \
      float3(-h, 0.0f, 0.0f),                                                   \
      float3( h, 0.0f, 0.0f),                                                   \
      float3(0.0f, -h, 0.0f),                                                   \
      float3(0.0f,  h, 0.0f),                                                   \
      float3(0.0f, 0.0f, -h),                                                   \
      float3(0.0f, 0.0f,  h),                                                   \
    };                                                                          \
    float aRes[6];                                                              \
    float3 s = 0.0f;                                                            \
    SdfBrushMaterial nmat;                                                      \
    [loop] for (int iDelta = 0; iDelta < 6; ++iDelta)                           \
      aRes[iDelta] = sdf_func((p) + aDelta[iDelta], (iBrushMask), nmat, false); \
    normal =                                                                    \
      normalize_safe                                                            \
      (                                                                         \
        float3                                                                  \
        (                                                                       \
          aRes[1] - aRes[0],                                                    \
          aRes[3] - aRes[2],                                                    \
          aRes[5] - aRes[4]                                                     \
        ),                                                                      \
        float3(0.0f, 0.0f, 0.0f)                                                \
      );                                                                        \
  }

float3 normal_2d_blend(float3 norm2d, float sdfValue)
{
  float t = 
    normal2dFadeDist < kEpsilon 
      ? 0.0f 
      : saturate((normal2dFadeDist + sdfValue) / normal2dFadeDist);

  float3 n = lerp(float3(0.0f, 0.0f, -1.0f), norm2d, t);
  n = lerp(float3(0.0f, 0.0f, -1.0f), n, normal2dStrength * saturate(normal2dFadeDist / voxelSize));

  return n;
}

#endif

