#version 330 core

uniform mat4 ModelViewProjectionMatrix;
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform float time;
uniform vec4 sphereParams; // X, Y, amp, rad
uniform vec4 spikeParams; // X, Y, amp, power
uniform vec4 knotParams; // p, q, knot radius, ring radius
uniform float effectScale;
uniform float wobble;
uniform float morph;

layout (location = 0) in vec3 POSITION;
layout (location = 1) in vec3 NORMAL;
layout (location = 4) in vec2 TEXCOORD;

out vec2 pos_zw;
out vec3 normal;
out vec3 ec_pos;
out vec2 uv;

#define PI 3.1415926
#define TWO_PI 6.2831853

// nrmalized grip position
vec2 getGridPos(int width, int height)
{
	int y = gl_InstanceID / width;
	int x = int(mod(gl_InstanceID - y*width, width));
	return vec2( float(x) / float(width), float(y) / float(height) );
}

vec3 fromSpherical(float phi, float theta, float radius)
{
	vec3 pos;
	pos.x = radius*sin(phi)*cos(theta);
	pos.y = radius*sin(phi)*sin(theta);
	pos.z = radius*cos(phi);
	return pos;
}


vec3 spherePos()
{
	// 128 * 128 = 16384
	int planeSize = 128;
	vec2 gp = getGridPos(planeSize, planeSize);

	float phi = TWO_PI * gp.x + time*0.5 + wobble;
	float theta = PI * gp.y + time*0.25;

	float radius = (sin(phi*sphereParams.x + time + wobble) + sin(theta*sphereParams.y + time))*sphereParams.z + sphereParams.w;
	radius += pow(abs(sin(phi*spikeParams.x + time + wobble)*sin(theta*spikeParams.y + time)), spikeParams.w) * spikeParams.z;
	vec3 pos = fromSpherical(phi, theta, radius*effectScale);
	return pos;
}

vec3 fromKnot(float angle, float radius, float p, float q)
{
	vec3 pos;
	float r = 0.5 * (2.0 + sin(angle * q)) * radius;
	pos.x = r * cos(angle * p);
	pos.y = r * cos(angle * q);
	pos.z = r * sin(angle * p);
	return pos;
}


vec3 knotPos()
{
	// 1024 * 16 = 16384
	int ringSize = 32;
	int knotSize = 16384 / ringSize;
	vec2 gp = getGridPos(knotSize, ringSize);

	float knotAngle = TWO_PI * gp.x + time*0.5 + wobble;
	float ringAngle = TWO_PI * gp.y + time*0.25 + wobble*0.5;

	float deltaKnotAngle = TWO_PI / float(knotSize);

	float knotRadWob = sin(knotAngle*4.0*sphereParams.x + time + wobble)*sphereParams.z * 0.25;
	float knotRadSpike = pow(abs(sin(knotAngle*spikeParams.x + time + wobble)), spikeParams.w) * spikeParams.z * 0.5;
	float knotRadius = (1.0 + knotRadWob + knotRadSpike) * knotParams.z * effectScale;

	vec3 p0 = fromKnot(knotAngle, knotRadius, knotParams.x, knotParams.y);
	vec3 p1 = fromKnot(knotAngle + deltaKnotAngle, knotRadius, knotParams.x, knotParams.y);
	vec3 t = normalize(p1 - p0);
	vec3 n = normalize(p1 + p0);
	vec3 b = cross(t, n);
	n = cross(b, t);

	//
	float ringRadWob = knotRadWob;
	float ringRadSpike = knotRadSpike*0.25 + pow(abs(sin(knotAngle*spikeParams.x + time + wobble)), spikeParams.w*2.0) * spikeParams.z * 0.25;
	float ringRadius = (1.0 + ringRadWob + ringRadSpike) * knotParams.w * effectScale;

	float s = sin(ringAngle);
	float c = cos(ringAngle);
	vec2 ring = vec2(c-s, s+c) * ringRadius;

	return p0 + n*ring.x + b*ring.y;
}

void main()
{

	vec3 sp = spherePos();
	vec3 kp  = knotPos();

	vec3 instPos = mix(sp, kp, morph);

	vec3 pos = POSITION*(1.0/92.0) + instPos.xyz;
	uv = TEXCOORD;
	normal = NormalMatrix * NORMAL;

	ec_pos = vec3(ModelViewMatrix * vec4(pos, 1.0));

	vec4 proj_pos = ModelViewProjectionMatrix * vec4(pos, 1.0);
	pos_zw = proj_pos.zw;
	gl_Position = proj_pos;
}
