Shader "CrispyPin/RaymarchMultiObject"
{
	Properties
	{
		[Header(Object positions)]
		_ObjectA ("Object A", Vector) = (0, 0, 0)
		_ObjectB ("Object B", Vector) = (0, 0, 0)
		[Header(Raymarcher Properties)]
		_MaxSteps ("Max steps", Integer) = 256
		_MaxDist ("Max distance", Float) = 100
		_SurfDist ("Surface distance threshold", Range(0.00001, 0.05)) = 0.001
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		Cull Off
		LOD 100
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag


			#include "UnityCG.cginc"
			#include "UnityLightingCommon.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 ro : TEXCOORD1;
				float3 hitPos : TEXCOORD2;
			};

			struct fragOut
			{
				fixed4 col : SV_Target;
				float depth : SV_Depth;
			};

			int _MaxSteps;
			float _MaxDist;
			float _SurfDist;
			float3 _ObjectA;
			float3 _ObjectB;

			v2f vert (appdata v) {
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				//object space
				o.ro = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
				o.hitPos = v.vertex;
				//world space
				//o.ro = _WorldSpaceCameraPos;
				//o.hitPos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}

			float smin(float a, float b, float k) {
				return min(a, b) - pow(max(k - abs(a-b), 0), 3)/(6*k*k);
			}

			float sdSphere(float3 p, float3 o, float r) {
				return length(p - o) - r;
			}

			float sdBox(float3 p, float3 dim) {
				return length(float3(
					max(abs(p.x) - dim.x/2.0, 0),
					max(abs(p.y) - dim.y/2.0, 0),
					max(abs(p.z) - dim.z/2.0, 0)));
			}

			float sdLine(float3 p, float3 a, float3 b, float r) {
				float3 pa = p - a;
				float3 ba = b - a;
				float h = clamp(dot(pa, ba) / dot(ba, ba), 0, 1);
				return length(p- a - (b-a) * h) - r;
			}

			float sdCappedTorus( float3 p, float2 sc, float ra, float rb) {
				p.x = abs(p.x);
				float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy);
				return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
			}

			float sdTorus( float3 p, float2 t) {
				float2 q = float2(length(p.xz) - t.x, p.y);
				return length(q) - t.y;
			}

			float3 RotateY(float3 p, float a) {
				return mul(float3x3(cos(a), 0, sin(a), 0, 1, 0, -sin(a), 0, cos(a)), p);
			}

			float Kijetesantakalu(float3 p, float r) {
				float d = sdSphere(p, float3(0, 0, 0.2f), r);// left eye
				d = min(d, sdSphere(p, float3(0.15f, 0, 0.2f), r)); // right eye
				d = min(d, sdLine(p, float3(0, 0, -0.4f), float3(-0.35f, 0, -0.4f), r)); // bottom line
				d = smin(d, sdLine(p, float3(0, 0, -0.4f), float3(-0.1f, 0, -0.15f), r), 0.003f); // leg hook
				d = min(d, sdLine(p, float3(0.15f, 0, -0.4f), float3(0.15f, 0, -0.1f), r)); // middle leg
				d = min(d, sdLine(p, float3(0.3f, 0, -0.4f), float3(0.3f, 0, 0), r)); // front leg
				d = smin(d, sdLine(p, float3(0.45f, 0, 0.1f), float3(0.3f, 0, 0), r), 0.003f); // lower snout
				d = smin(d, sdLine(p, float3(0.45f, 0, 0.1f), float3(0.2f, 0, 0.35f), r), 0.003f); // upper snout
				d = smin(d, sdLine(p, float3(0.2f, 0, 0.45f), float3(0.2f, 0, 0.35f), r), 0.003f); // ear
				float an = 0.9;
				d = smin(d, sdCappedTorus((RotateY(p, 0.8f) - float3(0.15f, 0, -0.05f)).xzy, float2(sin(an),cos(an)), 0.4, r), 0.003f); // ear
				d = smin(d, sdLine(p, float3(-0.25f, 0, 0), float3(-0.25f, 0, -0.4f), r), 0.003f); // back
				d = smin(d, sdLine(p, float3(-0.25f, 0, -0.1f), float3(-0.35f, 0, -0.1f), r), 0.003f); // tail top
				d = smin(d, sdLine(p, float3(-0.35f, 0, -0.1f), float3(-0.35f, 0, -0.4f), r), 0.003f); // stripe
				an = 3.1415f*0.5;
				d = smin(d, sdCappedTorus((RotateY(p, an) - float3(-0.25f, 0, 0.35f)).xzy, float2(sin(an),cos(an)), 0.15f, r), 0.003f); // tail

				return d;
			}

			float FirstShape(float3 p) {
				return Kijetesantakalu(p, 0.05f);
			}

			float SecondShape(float3 p) {
				// return sdBox(p, 0.3f) - 0.01f;
				// return Kijetesantakalu(p, 0.04f) - 0.01f;
				// return sdSphere(p, 0, 0.3f);

				float3 a =_ObjectA;
				float3 b =_ObjectB;

				float o =  sdLine(p, a, b, 0.1);
				o = smin(o, sdSphere(p, a, 0.2), 0.05);
				o = smin(o, sdSphere(p, b, 0.2), 0.05);
				return o;
				// return sdTorus(p - _ObjectA, float2(, 0.2f));
			}


			float GetDist(float3 p) {

				float first = FirstShape(p);
				// float3 second_pos = mul(unity_WorldToObject, _WorldSpaceLightPos0);
				float second = SecondShape(p);
				return smin(first, second, 0.15f);

			}

			//marches a ray through the scene
			float Raymarch(float3 ro, float3 rd) {
				float rayLen = 0;// total distance marched / distance from origin
				float dist; // distance from the raymarched scene
				for ( int i = 0; i < _MaxSteps; i++)
				{
					//position = origin + distance * direction
					float3 p = ro + rayLen * rd;
					dist = GetDist(p);
					rayLen += dist;// move forward
					if (dist < _SurfDist || rayLen > _MaxDist) {
						break;
					}
				}

				return rayLen;
			}

			float3 GetNormal(float3 p) {
				float2 e = float2(0.001, 0);
				float3 n = GetDist(p) - float3(
					GetDist(p-e.xyy),
					GetDist(p-e.yxy),
					GetDist(p-e.yyx));
				return normalize(n);
			}

			fragOut frag (v2f i) {
				float3 ro = i.ro;
				float3 rd = normalize(i.hitPos - ro);

				float d = Raymarch(ro, rd);
				fixed4 col = 1;

				if (d >= _MaxDist)
				{
					discard;
				}
				float3 p = ro + rd * d;
				float3 n = GetNormal(p);

				// col.rgb = max(dot(n, normalize(float3(1,0.5,1))), 0.05f);

				// float3 edgeCol = float3(0,1,1);
				float3 edgeCol = abs(p);
				col.rgb = lerp(0.001, edgeCol, pow(1 - dot(-rd, n), 2));

				// col *= float4(d, 1, 1, 1);
				fragOut o;
				o.col = col;
				// o.col = _LightColor0;
				float4 vClipPos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(p, 1)));
				float zDepth = vClipPos.z / vClipPos.w;
				#if !defined(UNITY_REVERSED_Z) // basically only OpenGL (unity editor on linux)
				zDepth = zDepth * 0.5 + 0.5; // remap -1 to 1 range to 0.0 to 1.0
				#endif
				o.depth = zDepth;
				return o;
			}

			ENDCG
		}
	}
}