
#define _MAT_VARIANT1(NEW_NAME, BASE_FN, TYPE_1) \
	SurfacePoint NEW_NAME(float3 p, TYPE_1 ARG_1, Material mat = DEFAULT_MAT) {\
		SurfacePoint o;\
		o.dist = BASE_FN(p, ARG_1);\
		o.mat = mat;\
		return o;\
	}

#define _MAT_VARIANT2(NEW_NAME, BASE_FN, TYPE_1, TYPE_2) \
	SurfacePoint NEW_NAME(float3 p, TYPE_1 ARG_1, TYPE_2 ARG_2, Material mat = DEFAULT_MAT) {\
		SurfacePoint o;\
		o.dist = BASE_FN(p, ARG_1, ARG_2);\
		o.mat = mat;\
		return o;\
	}

#define _MAT_VARIANT3(NEW_NAME, BASE_FN, TYPE_1, TYPE_2, TYPE_3) \
	SurfacePoint NEW_NAME(float3 p, TYPE_1 ARG_1, TYPE_2 ARG_2, TYPE_3 ARG_3, Material mat = DEFAULT_MAT) {\
		SurfacePoint o;\
		o.dist = BASE_FN(p, ARG_1, ARG_2, ARG_3);\
		o.mat = mat;\
		return o;\
	}

//
// Most of these are taken from https://iquilezles.org/articles/distfunctions/
// Thank you Inigo Quilez <3

float sdBox(float3 p, float3 size) {
	float3 q = abs(p) - size / 2.0;
	return length(max(q, 0)) + min(max(q.x, max(q.y, q.z)), 0);
}
_MAT_VARIANT1(mBox, sdBox, float3)

float sdSphere(float3 p, float radius) {
	return length(p) - radius;
}
_MAT_VARIANT1(mSphere, sdSphere, float)

float sdTorus(float3 p, float radius, float thickness) {
	float2 q = float2(length(p.xz) - radius, p.y);
	return length(q) - thickness;
}
_MAT_VARIANT2(mTorus, sdTorus, float, float)

float sdCappedTorus(float3 p, float2 sc, float r1, float r2) {
	p.x = abs(p.x);
	float k = (sc.y * p.x > sc.x * p.z) ? dot(p.xz, sc) : length(p.xz);
	return sqrt(dot(p, p) + r1 * r1 - 2.0 * r1 * k) - r2;
}
_MAT_VARIANT3(mCappedTorus, sdCappedTorus, float2, float, float)

float sdPlaneY(float3 p, float height) {
	return p.y - height;
}
_MAT_VARIANT1(mPlaneY, sdPlaneY, float)

float sdPlane(float3 p, float3 normal, float height) {
	return dot(p, normal) + height;
}
_MAT_VARIANT2(mPlane, sdPlane, float3, float)

float sdHexPrism(float3 p, float width, float height) {
  const float3 k = float3(-0.8660254, 0.5, 0.57735);
  p = abs(p);
  p.xz -= 2.0 * min(dot(k.xy, p.xz), 0) * k.xy;
  float2 d = float2(length(p.xz - float2(clamp(p.x,-k.z * width, k.z * width), width)) * sign(p.z - width), p.y - height);
  return min(max(d.x, d.y), 0) + length(max(d, 0));
}
_MAT_VARIANT2(mHexPrism, sdHexPrism, float, float)

float sdInfCylinder(float3 p, float3 c) {
	return length(p.xz - c.xy) - c.z;
}
_MAT_VARIANT1(mInfCylinder, sdInfCylinder, float3)

float sdCylinder(float3 p, float r, float h) {
	float2 d = abs(float2(length(p.xz), p.y)) - float2(r, h);
	return min(max(d.x, d.y), 0) + length(max(d, 0));
}
_MAT_VARIANT2(mCylinder, sdCylinder, float, float)

float sdHelix(float3 p, float r1, float r2, float incline) {
	float x2 = length(p.xz) - r1; // vertical plane
	float angle = atan2(p.z, p.x); // angle around y axis
	float y = angle * incline - p.y;
	y = fmod(y, UNITY_PI * incline * 2) + UNITY_PI * incline;
	// 2d circle
	float dist = length(float2(x2, y)) - r2;
	return dist * 0.8;
}
_MAT_VARIANT3(mHelix, sdHelix, float, float, float)

// box = (width, height, rotation)
float sdHelixSquare(float3 p, float r1, float3 box, float incline) {
	float x2 = length(p.xz) - r1; // vertical plane
	float angle = atan2(p.z, p.x); // angle around y axis
	float y = angle * incline - p.y;
	y = fmod(y, UNITY_PI * incline * 2) + UNITY_PI * incline;
	// rotated 2d box
	float2 p2 = float2(x2, y);
	p2 = mul(float2x2(cos(box.z), -sin(box.z), sin(box.z), cos(box.z)), p2);
	float2 d = abs(p2) - box.xy;
	float dist = length(max(d, 0)) + min(max(d.x, d.y), 0);
	return dist * 0.8;
}
_MAT_VARIANT3(mHelixSquare, sdHelixSquare, float, float3, float)


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;
}
_MAT_VARIANT3(mLine, sdLine, float3, float3, float3)

float sdGyroid(float3 p, float scale, float bias, float thickness) {
	float dist = abs(dot(sin(p * scale), cos(p.zxy * scale)) + bias) - thickness;
	dist *= .58 / scale;
	return dist;
}
_MAT_VARIANT3(mGyroid, sdGyroid, float, float, float)


SurfacePoint mFromDist(float d, Material mat = DEFAULT_MAT) {
	SurfacePoint o;
	o.dist = d;
	o.mat = mat;
	return o;
}
