Shader "CrispyPin/Nut"
{
	Properties
	{
		[Header(Raymarcher Properties)]
		_MaxSteps ("Max steps", Integer) = 128
		_MaxDist ("Max distance", Float) = 128
		_SurfDist ("Surface distance threshold", Range(0.0001, 0.05)) = 0.001
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		Cull Front
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#define PLAYER_COUNT 4
			float4 _PlayerRightHandPositions[PLAYER_COUNT];
			float4 _PlayerLeftHandPositions[PLAYER_COUNT];

			int _MaxSteps;
			float _MaxDist;
			float _SurfDist;
			#define MAX_STEPS _MaxSteps
			#define MAX_DIST _MaxDist
			#define SURF_DIST _SurfDist

			#define REFLECTIONS 2

			#define SCENE_FN main
			#define LIGHT_FN lighting

			#define SCENE_SCALE 0.7

			// #define DISCARD_ON_MISS
			#define LIMIT_DEPTH_TO_MESH

			#include "lib/libgarbage.cginc"

			float3 checkers(float3 p, float3 a, float3 b, float2 size) {
				float2 q = p.xz / size;
				q = int2(abs(q) + 0.5);
				int s = ((q.x + q.y) % 2);
				return s * a + (1 - s) * b;
			}

			float3 floor(float3 p) {
				return lerp(0.42,
					checkers(p - 1, 0.35, 0.45, .2),
					smoothstep(16, 0, length(p))
				);
			}

			SurfacePoint mRoundedHex(float3 p, float girth, float height) {
				SurfacePoint d;
				d = qRound(mHexPrism(p, girth, height), 0.01);
				float rounding = 3;
				d = qIntersect(d, mSphere(p - float3(0, -rounding + height, 0), rounding), 0.01);
				d = qIntersect(d, mSphere(p + float3(0, -rounding + height, 0), rounding), 0.01);
				return d;
			}

			SurfacePoint main(float3 p) {
				float girth = 0.3;
				float height = 0.1;
				float inner_radius = 0.18;
				float thread = 0.013;
				float3 thread_box = float3(thread, thread, UNITY_PI/4);
				SurfacePoint d;

				float3 rp = p;
				rp = rotY(rp, _Time.y);
				rp = rotX(rp, _Time.x);
				// nut
				d = mRoundedHex(rp, girth, height);
				d = qSub(d, mInfCylinder(rp, float3(0, 0, inner_radius)), 0.02);
				d = qSub(d, mHelixSquare(rp - float3(0, -1, 0), inner_radius, thread_box, thread/2), 0.003);

				// bolt
				SurfacePoint bolt_thread = mInfCylinder(rp, float3(0, 0, inner_radius + 0.3));
				bolt_thread = qSub(bolt_thread, mInfCylinder(rp, float3(0, 0, inner_radius)), 0.02);
				bolt_thread = qSub(bolt_thread, mHelixSquare(rp - float3(0, -1, 0), inner_radius, thread_box, thread/2), 0.03);


				// d = qUnion(d, bolt_thread);
				float h = sin(_Time.y) * 0.15 + 0.2;
				float3 bp = rotY(rp, sin(_Time.y) * UNITY_PI * 4);
				bp.y -= h;
				SurfacePoint bolt = qSub(mCylinder(bp, inner_radius+thread*2, 0.2), bolt_thread);
				bolt = qUnion(bolt, mRoundedHex(bp - float3(0, 0.13, 0), girth, height));
				Material metal1 = mat(float3(0.62, 0.62, 0.64), 1);
				Material metal2 = mat(float3(0.58, 0.32, 0.12), 1);
				// Material metal1 = mat(0.3, 1);
				// Material metal2 = mat(0.3, 1);
				d.mat = metal1;
				bolt.mat = metal2;

				// cube
				// float3 cp = rotX(bp - float3(0, -0.32, 0), _Time.w * 5);
				// bolt = qUnion(bolt, mBox(cp, 0.1, mat(float3(0,1,1))),.1);

				d = qUnion(d, bolt);

				// d = qUnion(d, mSphere(p - float3(0,0,.3), 0.15, mat(1,0,0.5)), 0.1);

				// floor

				// d = qUnion(d, mPlaneY(p, -0.5, mat(floor_col, 0)), 0.01);
				// d = qIntersect(d, mPlaneY(-p, 0.6, mat(floor_col, 0)), 0.01);
				d = qUnion(d, mBox(p - float3(0, -0.5 / SCENE_SCALE, 0), float3(6, 0.05, 6), mat(floor(p), 0)));

				[unroll]
				for (int i = 0; i < PLAYER_COUNT; i++) {
					// float visible = (float)(length(_PlayerRightHandPositions[i]) > 0);
					// float visible = step(0.01, length(_PlayerRightHandPositions[i]));
					[flatten]
					if (length(_PlayerRightHandPositions[i]) > 0.01) {
						float rad = 0.1;
						float3 hand_r = mul(unity_WorldToObject, float4(_PlayerRightHandPositions[i].xyz, 1)).xyz / SCENE_SCALE;
						d = qUnion(d, mSphere(p - hand_r, rad, mat(0.1, sin(_Time.y)*0.5 +0.5, 1)), 0.2);
						float3 hand_l = mul(unity_WorldToObject, float4(_PlayerLeftHandPositions[i].xyz, 1)).xyz / SCENE_SCALE;
						d = qUnion(d, mSphere(p - hand_l, rad, mat(float3(1, 0, 0.1), 0.5)), 0.2);
					}
				}


				// d = qUnion(d, mSphere(p - float3(0, -0.5, 1), 0.4));
				return d;
			}

			float3 lighting(Ray ray) {
				float3 sun_dir = normalize(float3(4, 2, 1));

				if (ray.missed) {
					if (ray.dir.y >= 0) {
						return lRenderSky(ray.dir, sun_dir);
					}
					else {
						float3 cam = ray.start;
						cam.y += 0.5 / SCENE_SCALE;
						float3 dir = ray.dir;
						float3 surface_pos = float3 (
							cam.x - cam.y / (dir.y / dir.x),
							0,
							cam.z - cam.y / (dir.y / dir.z)
						);
						float col = 1;
						col = floor(surface_pos);
						return col * (lSky(UP) + lSun(UP, sun_dir));
					}
				}
				float3 light = lSun(ray.normal, sun_dir)
					* lShadow(ray.hit_pos + ray.normal * SURF_DIST, sun_dir, 50)
				 	+ lSky(ray.normal);

				return light;
			}


			ENDCG
		}
	}
}
