#version 330 core
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrixInv;
uniform float instanceCount = 100.0f;
uniform float density = 0.5f;

out jit_PerVertex {
	vec3 vertexPos;
	vec2 texcoord;
	flat float instanceId;
	flat float transparencyIntensity;
} jit_out;

struct ProcedualVertexData {
	vec3 vertex;
	vec2 texcoord;
};

// User can't have more than 750 slices, this allows us to precompute log instead of calling it for each pixel
#define MAX_SLICES 750
const float LOG2_LOOKUP[MAX_SLICES] = float[MAX_SLICES](0.0000,1.0000,1.5850,2.0000,2.3219,2.5850,2.8074,3.0000,3.1699,3.3219,3.4594,3.5850,3.7004,3.8074,3.9069,4.0000,4.0875,4.1699,4.2479,4.3219,4.3923,4.4594,4.5236,4.5850,4.6439,4.7004,4.7549,4.8074,4.8580,4.9069,4.9542,5.0000,5.0444,5.0875,5.1293,5.1699,5.2095,5.2479,5.2854,5.3219,5.3576,5.3923,5.4263,5.4594,5.4919,5.5236,5.5546,5.5850,5.6147,5.6439,5.6724,5.7004,5.7279,5.7549,5.7814,5.8074,5.8329,5.8580,5.8826,5.9069,5.9307,5.9542,5.9773,6.0000,6.0224,6.0444,6.0661,6.0875,6.1085,6.1293,6.1497,6.1699,6.1898,6.2095,6.2288,6.2479,6.2668,6.2854,6.3038,6.3219,6.3399,6.3576,6.3750,6.3923,6.4094,6.4263,6.4429,6.4594,6.4757,6.4919,6.5078,6.5236,6.5392,6.5546,6.5699,6.5850,6.5999,6.6147,6.6294,6.6439,6.6582,6.6724,6.6865,6.7004,6.7142,6.7279,6.7415,6.7549,6.7682,6.7814,6.7944,6.8074,6.8202,6.8329,6.8455,6.8580,6.8704,6.8826,6.8948,6.9069,6.9189,6.9307,6.9425,6.9542,6.9658,6.9773,6.9887,7.0000,7.0112,7.0224,7.0334,7.0444,7.0553,7.0661,7.0768,7.0875,7.0980,7.1085,7.1189,7.1293,7.1396,7.1497,7.1599,7.1699,7.1799,7.1898,7.1997,7.2095,7.2192,7.2288,7.2384,7.2479,7.2574,7.2668,7.2761,7.2854,7.2946,7.3038,7.3129,7.3219,7.3309,7.3399,7.3487,7.3576,7.3663,7.3750,7.3837,7.3923,7.4009,7.4094,7.4179,7.4263,7.4346,7.4429,7.4512,7.4594,7.4676,7.4757,7.4838,7.4919,7.4998,7.5078,7.5157,7.5236,7.5314,7.5392,7.5469,7.5546,7.5622,7.5699,7.5774,7.5850,7.5925,7.5999,7.6073,7.6147,7.6221,7.6294,7.6366,7.6439,7.6511,7.6582,7.6653,7.6724,7.6795,7.6865,7.6935,7.7004,7.7074,7.7142,7.7211,7.7279,7.7347,7.7415,7.7482,7.7549,7.7616,7.7682,7.7748,7.7814,7.7879,7.7944,7.8009,7.8074,7.8138,7.8202,7.8265,7.8329,7.8392,7.8455,7.8517,7.8580,7.8642,7.8704,7.8765,7.8826,7.8887,7.8948,7.9009,7.9069,7.9129,7.9189,7.9248,7.9307,7.9366,7.9425,7.9484,7.9542,7.9600,7.9658,7.9715,7.9773,7.9830,7.9887,7.9944,8.0000,8.0056,8.0112,8.0168,8.0224,8.0279,8.0334,8.0389,8.0444,8.0498,8.0553,8.0607,8.0661,8.0715,8.0768,8.0821,8.0875,8.0928,8.0980,8.1033,8.1085,8.1137,8.1189,8.1241,8.1293,8.1344,8.1396,8.1447,8.1497,8.1548,8.1599,8.1649,8.1699,8.1749,8.1799,8.1849,8.1898,8.1948,8.1997,8.2046,8.2095,8.2143,8.2192,8.2240,8.2288,8.2336,8.2384,8.2432,8.2479,8.2527,8.2574,8.2621,8.2668,8.2715,8.2761,8.2808,8.2854,8.2900,8.2946,8.2992,8.3038,8.3083,8.3129,8.3174,8.3219,8.3264,8.3309,8.3354,8.3399,8.3443,8.3487,8.3531,8.3576,8.3619,8.3663,8.3707,8.3750,8.3794,8.3837,8.3880,8.3923,8.3966,8.4009,8.4051,8.4094,8.4136,8.4179,8.4221,8.4263,8.4305,8.4346,8.4388,8.4429,8.4471,8.4512,8.4553,8.4594,8.4635,8.4676,8.4717,8.4757,8.4798,8.4838,8.4878,8.4919,8.4959,8.4998,8.5038,8.5078,8.5118,8.5157,8.5196,8.5236,8.5275,8.5314,8.5353,8.5392,8.5430,8.5469,8.5507,8.5546,8.5584,8.5622,8.5661,8.5699,8.5736,8.5774,8.5812,8.5850,8.5887,8.5925,8.5962,8.5999,8.6036,8.6073,8.6110,8.6147,8.6184,8.6221,8.6257,8.6294,8.6330,8.6366,8.6402,8.6439,8.6475,8.6511,8.6546,8.6582,8.6618,8.6653,8.6689,8.6724,8.6760,8.6795,8.6830,8.6865,8.6900,8.6935,8.6970,8.7004,8.7039,8.7074,8.7108,8.7142,8.7177,8.7211,8.7245,8.7279,8.7313,8.7347,8.7381,8.7415,8.7448,8.7482,8.7515,8.7549,8.7582,8.7616,8.7649,8.7682,8.7715,8.7748,8.7781,8.7814,8.7846,8.7879,8.7912,8.7944,8.7977,8.8009,8.8041,8.8074,8.8106,8.8138,8.8170,8.8202,8.8234,8.8265,8.8297,8.8329,8.8361,8.8392,8.8424,8.8455,8.8486,8.8517,8.8549,8.8580,8.8611,8.8642,8.8673,8.8704,8.8734,8.8765,8.8796,8.8826,8.8857,8.8887,8.8918,8.8948,8.8978,8.9009,8.9039,8.9069,8.9099,8.9129,8.9159,8.9189,8.9218,8.9248,8.9278,8.9307,8.9337,8.9366,8.9396,8.9425,8.9454,8.9484,8.9513,8.9542,8.9571,8.9600,8.9629,8.9658,8.9687,8.9715,8.9744,8.9773,8.9801,8.9830,8.9858,8.9887,8.9915,8.9944,8.9972,9.0000,9.0028,9.0056,9.0084,9.0112,9.0140,9.0168,9.0196,9.0224,9.0251,9.0279,9.0307,9.0334,9.0362,9.0389,9.0417,9.0444,9.0471,9.0498,9.0526,9.0553,9.0580,9.0607,9.0634,9.0661,9.0688,9.0715,9.0741,9.0768,9.0795,9.0821,9.0848,9.0875,9.0901,9.0928,9.0954,9.0980,9.1007,9.1033,9.1059,9.1085,9.1111,9.1137,9.1163,9.1189,9.1215,9.1241,9.1267,9.1293,9.1319,9.1344,9.1370,9.1396,9.1421,9.1447,9.1472,9.1497,9.1523,9.1548,9.1573,9.1599,9.1624,9.1649,9.1674,9.1699,9.1724,9.1749,9.1774,9.1799,9.1824,9.1849,9.1874,9.1898,9.1923,9.1948,9.1972,9.1997,9.2021,9.2046,9.2070,9.2095,9.2119,9.2143,9.2167,9.2192,9.2216,9.2240,9.2264,9.2288,9.2312,9.2336,9.2360,9.2384,9.2408,9.2432,9.2456,9.2479,9.2503,9.2527,9.2550,9.2574,9.2597,9.2621,9.2644,9.2668,9.2691,9.2715,9.2738,9.2761,9.2784,9.2808,9.2831,9.2854,9.2877,9.2900,9.2923,9.2946,9.2969,9.2992,9.3015,9.3038,9.3061,9.3083,9.3106,9.3129,9.3151,9.3174,9.3197,9.3219,9.3242,9.3264,9.3287,9.3309,9.3332,9.3354,9.3376,9.3399,9.3421,9.3443,9.3465,9.3487,9.3509,9.3531,9.3554,9.3576,9.3597,9.3619,9.3641,9.3663,9.3685,9.3707,9.3729,9.3750,9.3772,9.3794,9.3815,9.3837,9.3859,9.3880,9.3902,9.3923,9.3945,9.3966,9.3987,9.4009,9.4030,9.4051,9.4073,9.4094,9.4115,9.4136,9.4157,9.4179,9.4200,9.4221,9.4242,9.4263,9.4284,9.4305,9.4325,9.4346,9.4367,9.4388,9.4409,9.4429,9.4450,9.4471,9.4491,9.4512,9.4533,9.4553,9.4574,9.4594,9.4615,9.4635,9.4656,9.4676,9.4696,9.4717,9.4737,9.4757,9.4778,9.4798,9.4818,9.4838,9.4858,9.4878,9.4898,9.4919,9.4939,9.4959,9.4979,9.4998,9.5018,9.5038,9.5058,9.5078,9.5098,9.5118,9.5137,9.5157,9.5177,9.5196,9.5216,9.5236,9.5255,9.5275,9.5294,9.5314,9.5333,9.5353,9.5372,9.5392,9.5411,9.5430,9.5450,9.5469,9.5488,9.5507);

#define SQUARE_ROOT_OF_THREE 1.7320508075688772f

ProcedualVertexData getViewAlignedPlaneVertex(in vec3 planeCenter, out vec3 planeNormal) {
	vec3 LOCAL_UP = (modelViewMatrixInv * vec4(0,1,0,0)).xyz;

	const float planeSize = 0.75;
	vec3 dir = normalize((modelViewMatrixInv * vec4(vec3(0,0,-1), 0.0)).xyz);
	planeNormal = -dir;

	vec3 viewRightAngle = normalize(cross(LOCAL_UP, planeNormal));
	vec3 planeRight = viewRightAngle;
	vec3 planeUp = normalize(cross(planeNormal, viewRightAngle));

	if (gl_VertexID == 0) return ProcedualVertexData(planeCenter + ((-viewRightAngle - planeUp) * planeSize), vec2(1,0));
	if (gl_VertexID == 1) return ProcedualVertexData(planeCenter + ((viewRightAngle - planeUp) * planeSize), vec2(0,0));
	if (gl_VertexID == 2) return ProcedualVertexData(planeCenter + ((viewRightAngle + planeUp) * planeSize), vec2(0,1));
	if (gl_VertexID == 3) return ProcedualVertexData(planeCenter + ((viewRightAngle + planeUp) * planeSize), vec2(0,1));
	if (gl_VertexID == 4) return ProcedualVertexData(planeCenter + ((-viewRightAngle + planeUp) * planeSize), vec2(1,1));
	if (gl_VertexID == 5) return ProcedualVertexData(planeCenter + ((-viewRightAngle - planeUp) * planeSize), vec2(1,0));
}

float remap(float value, float min1, float max1, float min2, float max2) {
	return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}

void main() {
	// Calculate the distance between each quad inside of the bounding box area.
	float planeGap = (SQUARE_ROOT_OF_THREE / max(instanceCount, 1.0f));

	// Magic numbers picked by random decree, making the gap too small causes the
	// visualization illusion to fall apart.
	planeGap /= remap(clamp(density, 0.0, 1.0), 0.0, 1.0, 0.001, 4.0);

	vec3 planeNormal;
	vec3 planeCenter = vec3(0);
	vec3 viewRightAngle;

	ProcedualVertexData procedualData = getViewAlignedPlaneVertex(planeCenter, planeNormal);

	// Extrude vertices along planeNormal in both directions
	float displacement = gl_InstanceID % 2 == 0 ? 1 : -1;
	float t = (displacement * (planeGap * float(gl_InstanceID)));
	vec3 translatePlane = (t * planeNormal);

	// Use exponential decay to decide how transparent each slice should be
	float maxInstances = clamp(instanceCount, 0.0f, MAX_SLICES - 1);
	uint logLookupValue = uint(maxInstances);
	jit_out.transparencyIntensity = LOG2_LOOKUP[logLookupValue] / maxInstances;

	vec4 p = vec4(translatePlane + procedualData.vertex, 1.0);
	gl_Position = modelViewProjectionMatrix * vec4(p.xyz, 1.0);
	jit_out.vertexPos = p.xyz;
	jit_out.texcoord = procedualData.texcoord;
	jit_out.instanceId = uint(gl_InstanceID);

    // Prevent triangle fragments from rendering outside the bounding box.
	gl_ClipDistance[0] = dot(p, vec4(-2.0, 0.0, 0.0, 1.0));
	gl_ClipDistance[1] = dot(p, vec4(2.0, 0.0, 0.0, 1.0));
	gl_ClipDistance[2] = dot(p, vec4(0.0, -2.0, 0.0, 1.0));
	gl_ClipDistance[3] = dot(p, vec4(0.0, 0.0, -2.0, 1.0));
	gl_ClipDistance[4] = dot(p, vec4(0.0, 2.0, 0.0, 1.0));
	gl_ClipDistance[5] = dot(p, vec4(0.0, 0.0, 2.0, 1.0));
}