﻿/*
 *  The zlib/libpng License
 *
 *  Copyright 2018-2024 whiteflare.
 *
 *  This software is provided ‘as-is’, without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  1. The origin of this software must not be misrepresented; you must not
 *  claim that you wrote the original software. If you use this software
 *  in a product, an acknowledgment in the product documentation would be
 *  appreciated but is not required.
 *
 *  2. Altered source versions must be plainly marked as such, and must not be
 *  misrepresented as being the original software.
 *
 *  3. This notice may not be removed or altered from any source
 *  distribution.
 */

#ifndef INC_UNLIT_WF_COMMON_BUILTIN_RP
#define INC_UNLIT_WF_COMMON_BUILTIN_RP

    #include "UnityCG.cginc"
    #include "Lighting.cginc"

    ////////////////////////////
    // Texture Definition
    ////////////////////////////

    #define DECL_MAIN_TEX2D(name)                       UNITY_DECLARE_TEX2D(name)
    #define PICK_MAIN_TEX2D(tex, uv)                    UNITY_SAMPLE_TEX2D(tex, uv)

    #define DECL_SUB_TEX2D(name)                        UNITY_DECLARE_TEX2D_NOSAMPLER(name)
    #define PICK_SUB_TEX2D(tex, name, uv)               UNITY_SAMPLE_TEX2D_SAMPLER(tex, name, uv)

    #define DECL_MAIN_TEXCUBE(name)                     UNITY_DECLARE_TEXCUBE(name)
    #define PICK_MAIN_TEXCUBE_LOD(tex, dir, lod)        UNITY_SAMPLE_TEXCUBE_LOD(tex, dir, lod)

    #define PICK_SUB_TEXCUBE_LOD(tex, name, dir, lod)   UNITY_SAMPLE_TEXCUBE_SAMPLER_LOD(tex, name, dir, lod)

#ifdef SHADER_API_D3D11
    #define DECL_VERT_TEX2D(name)                       UNITY_DECLARE_TEX2D(name)
    #define PICK_VERT_TEX2D_LOD(tex, uv, lod)           tex.SampleLevel(sampler##tex, uv, lod)
#else
    #define DECL_VERT_TEX2D(name)                       sampler2D name
    #define PICK_VERT_TEX2D_LOD(tex, uv, lod)           tex2Dlod(tex, float4(uv.x, uv.y, 0, lod))
#endif

    #define DECL_GRAB_TEX2D(name)                       UNITY_DECLARE_SCREENSPACE_TEXTURE(name)
    #define PICK_GRAB_TEX2D(tex, uv)                    UNITY_SAMPLE_SCREENSPACE_TEXTURE(tex, uv)

    ////////////////////////////
    // Compatible
    ////////////////////////////

    #define UnityObjectToWorldPos(v)    ( mul(unity_ObjectToWorld, float4(v.xyz, 1)).xyz )
    #define UnityWorldToObjectPos(v)    ( mul(unity_WorldToObject, float4(v.xyz, 1)).xyz )

    ////////////////////////////
    // Lighting
    ////////////////////////////

    float3 getMainLightDirection() {
        return _WorldSpaceLightPos0.xyz;
    }

    half3 sampleMainLightColor() {
        return _LightColor0.rgb;
    }

    half3 sampleSHLightColor() {
        float3 col = float3(0, 0, 0);
        col += ShadeSH9( half4(+1, 0, 0, 1) );
        col += ShadeSH9( half4(-1, 0, 0, 1) );
        col += ShadeSH9( half4(0, 0, +1, 1) );
        col += ShadeSH9( half4(0, 0, -1, 1) );
        col /= 4;
        col += ShadeSH9( half4(0, +1, 0, 1) );
        col += ShadeSH9( half4(0, -1, 0, 1) );
        return col / 3;
    }

    float3 getPoint1LightPos() {
#ifdef VERTEXLIGHT_ON
        return float3(unity_4LightPosX0.x, unity_4LightPosY0.x, unity_4LightPosZ0.x);
#else
        return float3(0, 0, 0);
#endif
    }

    float3 calcPointLightWorldDir(float3 ws_light_pos, float3 ws_vertex) {
        ws_vertex = ws_light_pos - ws_vertex;
        if (dot(ws_vertex, ws_vertex) < 0.000001) {
            ws_vertex = float3(0, 1, 0);    // 至近距離ならば+Y方向を返却する
        }
        return normalize( ws_vertex );
    }

    float3 calcPointLight1WorldDir(float3 ws_vertex) {
#ifdef VERTEXLIGHT_ON
        float3 ws_lightPos = getPoint1LightPos();
        if (any(ws_lightPos)) {
            return calcPointLightWorldDir(ws_lightPos, ws_vertex);
        }
#endif
        return float3(0, 0, 0); // ポイントライトが無いときは 0, 0, 0 を返す
    }

    half3 samplePoint1LightColor(float3 ws_vertex) {
#ifdef VERTEXLIGHT_ON
        float3 ws_lightPos = getPoint1LightPos();
        if (any(ws_lightPos)) {
            float3 ls_lightPos = ws_lightPos - ws_vertex;
            float lengthSq = dot(ls_lightPos, ls_lightPos);
            float atten = 1.0 / (1.0 + lengthSq * unity_4LightAtten0.x);
            return unity_LightColor[0].rgb * atten;
        }
#endif
        return float3(0, 0, 0); // ポイントライトが無いときは 0, 0, 0 を返す
    }

    half3 OmniDirectional_Shade4PointLights(
        float4 lpX, float4 lpY, float4 lpZ,
        float3 col0, float3 col1, float3 col2, float3 col3,
        float4 lightAttenSq, float3 ws_vertex) {
        // UnityCG.cginc にある Shade4PointLights の等方向版

        if ( !any(float3(lpX.x, lpY.x, lpZ.x)) ) {
            col0.rgb = 0;
        }

        float4 toLightX = lpX - ws_vertex.x;
        float4 toLightY = lpY - ws_vertex.y;
        float4 toLightZ = lpZ - ws_vertex.z;

        float4 lengthSq
            = toLightX * toLightX
            + toLightY * toLightY
            + toLightZ * toLightZ;
        // ws_normal との内積は取らない。これによって反射光の強さではなく、頂点に当たるライトの強さが取れる。

        // attenuation
        float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);

        float3 col
            = col0 * atten.x
            + col1 * atten.y
            + col2 * atten.z
            + col3 * atten.w;
        return col;
    }


    half3 sampleAdditionalLightColor(float3 ws_vertex) {
#ifdef VERTEXLIGHT_ON
        return OmniDirectional_Shade4PointLights(
                unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
                unity_LightColor[0].rgb,
                unity_LightColor[1].rgb,
                unity_LightColor[2].rgb,
                unity_LightColor[3].rgb,
                unity_4LightAtten0,
                ws_vertex
            );
#else
        return float3(0, 0, 0);
#endif
    }

    half3 sampleAdditionalLightColorExclude1(float3 ws_vertex) {
#ifdef VERTEXLIGHT_ON
        return OmniDirectional_Shade4PointLights(
                unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
                float3(0, 0, 0),
                unity_LightColor[1].rgb,
                unity_LightColor[2].rgb,
                unity_LightColor[3].rgb,
                unity_4LightAtten0,
                ws_vertex
            );
#else
        return float3(0, 0, 0);
#endif
    }

    ////////////////////////////
    // Lightmap Sampler
    ////////////////////////////

    half3 pickLightmap(float2 uv_lmap) {
        float3 color = float3(0, 0, 0);
        #ifdef LIGHTMAP_ON
        {
            float2 uv = uv_lmap.xy * unity_LightmapST.xy + unity_LightmapST.zw;
            float4 lmap_tex = PICK_MAIN_TEX2D(unity_Lightmap, uv);
            float3 lmap_color = DecodeLightmap(lmap_tex);
            color += lmap_color;
        }
        #endif
        #ifdef DYNAMICLIGHTMAP_ON
        {
            float2 uv = uv_lmap.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
            float4 lmap_tex = PICK_MAIN_TEX2D(unity_DynamicLightmap, uv);
            float3 lmap_color = DecodeRealtimeLightmap(lmap_tex);
            color += lmap_color;
        }
        #endif
        return color;
    }

    half3 pickLightmapLod(float2 uv_lmap) {
#ifdef SHADER_API_D3D11
        float3 color = float3(0, 0, 0);
        #ifdef LIGHTMAP_ON
        {
            float2 uv = uv_lmap.xy * unity_LightmapST.xy + unity_LightmapST.zw;
            float4 lmap_tex = PICK_VERT_TEX2D_LOD(unity_Lightmap, uv, 0);
            float3 lmap_color = DecodeLightmap(lmap_tex);
            color += lmap_color;
        }
        #endif
        #ifdef DYNAMICLIGHTMAP_ON
        {
            float2 uv = uv_lmap.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
            float4 lmap_tex = PICK_VERT_TEX2D_LOD(unity_DynamicLightmap, uv, 0);
            float3 lmap_color = DecodeRealtimeLightmap(lmap_tex);
            color += lmap_color;
        }
        #endif
        return color;
#else
        return float3(1, 1, 1);
#endif
    }

    ////////////////////////////
    // ReflectionProbe Sampler
    ////////////////////////////

    half4 pickReflectionProbe(float3 ws_vertex, half3 ws_normal, float lod) {
        half3 ws_camera_dir = normalize(_WorldSpaceCameraPos - ws_vertex);
        float3 reflect_dir = reflect(-ws_camera_dir, ws_normal);

        float3 dir0 = BoxProjectedCubemapDirection(reflect_dir, ws_vertex, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
        float3 dir1 = BoxProjectedCubemapDirection(reflect_dir, ws_vertex, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);

        float4 color0 = PICK_MAIN_TEXCUBE_LOD(unity_SpecCube0, dir0, lod);
        float4 color1 = PICK_SUB_TEXCUBE_LOD(unity_SpecCube1, unity_SpecCube0, dir1, lod);

        color0.rgb = DecodeHDR(color0, unity_SpecCube0_HDR);
        color1.rgb = DecodeHDR(color1, unity_SpecCube1_HDR);

        return lerp(color1, color0, unity_SpecCube0_BoxMin.w);
    }

#endif
