#extension GL_ARB_gpu_shader5: enable

#conditional_include +"LARGE_SHADER" "main_large.sdr"
#conditional_include -"LARGE_SHADER" "main_small.sdr"

#ifdef MODEL_SDR_FLAG_THICK_OUTLINES
layout (triangles) in;
// For every triangle line we generate 2 triangles which can be done with 4 vertices so in total we will need 12 vertices
layout (triangle_strip, max_vertices = 12) out;
#elif defined(MODEL_SDR_FLAG_SHADOW_MAP)
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
#endif

#ifdef MODEL_SDR_FLAG_SHADOW_MAP
 #ifdef GL_ARB_gpu_shader5
layout(invocations = 4) in;
 #endif
#endif

#define MAX_LIGHTS 8
struct model_light {
	vec4 position;

	vec3 diffuse_color;
	int light_type;

	vec3 direction;
	float attenuation;

	float ml_sourceRadius;
};

layout (std140) uniform modelData {
	mat4 modelViewMatrix;
	mat4 modelMatrix;
	mat4 viewMatrix;
	mat4 projMatrix;
	mat4 textureMatrix;
	mat4 shadow_mv_matrix;
	mat4 shadow_proj_matrix[4];

	vec4 color;

	model_light lights[MAX_LIGHTS];

	float outlineWidth;
	float fogNear;
	float fogDensity;
	int buffer_matrix_offset;

	vec4 clip_equation;

	float thruster_scale;
	bool use_clip_plane;
	int n_lights;
	float defaultGloss;

	vec3 ambientFactor;
	int desaturate;

	vec3 diffuseFactor;
	int blend_alpha;

	vec3 emissionFactor;

	bool alphaGloss;

	bool gammaSpec;
	bool envGloss;
	int effect_num;
	int sBasemapIndex;

	vec4 fogColor;

	vec3 base_color;
	float anim_timer;

	vec3 stripe_color;
	float vpwidth;

	float vpheight;
	bool team_glow_enabled;
	float znear;
	float zfar;

	float veryneardist;
	float neardist;
	float middist;
	float fardist;

	int sGlowmapIndex;

	int sSpecmapIndex;
	int sNormalmapIndex;
	int sAmbientmapIndex;
	int sMiscmapIndex;

	float alphaMult;

	int flags;
};

in VertexOutput {
	mat3 tangentMatrix;

#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
	float fogDist;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG

#ifdef MODEL_SDR_FLAG_SHADOW_MAP
 #if !defined(GL_ARB_gpu_shader5)
	float instance;
 #endif
	float clipModel;
#else
	vec4 position;
#endif
	vec3 normal;
	vec4 texCoord;

#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
	vec4 shadowUV[4];
	vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
} vertIn[];

out VertexOutput {
	mat3 tangentMatrix;

#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
	float fogDist;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG

	vec4 position;
	vec3 normal;
	vec4 texCoord;

#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
	vec4 shadowUV[4];
	vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
} vertOut;

#ifdef MODEL_SDR_FLAG_SHADOW_MAP
	void main(void)
	{
		#ifdef GL_ARB_gpu_shader5
			int instanceID = gl_InvocationID;
		#else
			int instanceID = int(vertIn[0].instance);
		#endif
		for(int vert = 0; vert < gl_in.length(); vert++)
		{
			#prereplace IF_FLAG MODEL_SDR_FLAG_TRANSFORM
				if (vertIn[vert].clipModel > 0.9) {
					// If the model was clipped in the vertex shader then we do not apply the shadow projection matrix here
					gl_Position = gl_in[vert].gl_Position;
				} else {
					gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position;
				}
			#prereplace ELSE_FLAG //MODEL_SDR_FLAG_TRANSFORM
				gl_Position = shadow_proj_matrix[instanceID] * gl_in[vert].gl_Position;
			#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM

			vertOut.position = gl_in[vert].gl_Position;
			vertOut.normal = vertIn[vert].normal;
			vertOut.texCoord = vertIn[vert].texCoord;

			gl_Layer = instanceID;

			#prereplace IF_FLAG MODEL_SDR_FLAG_NORMAL
				vertOut.tangentMatrix = vertIn[vert].tangentMatrix;
			#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_NORMAL

			#prereplace IF_FLAG MODEL_SDR_FLAG_FOG
				vertOut.fogDist = vertIn[vert].fogDist;
			#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG

			#prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS
				vertOut.shadowUV[0] = vertIn[vert].shadowUV[0];
				vertOut.shadowUV[1] = vertIn[vert].shadowUV[1];
				vertOut.shadowUV[2] = vertIn[vert].shadowUV[2];
				vertOut.shadowUV[3] = vertIn[vert].shadowUV[3];
				vertOut.shadowPos = vertIn[vert].shadowPos;
			#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS

			if (use_clip_plane) {
				gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
			}
			EmitVertex();
		}
		EndPrimitive();
	}
#elif defined(MODEL_SDR_FLAG_THICK_OUTLINES)
	const vec2 pixelOffsetDir[4] = vec2[](
		vec2(0.0, 1.0),
		vec2(1.0, 1.0),
		vec2(1.0, -1.0),
		vec2(0.0, -1.0)
	);

	void main(void)
	{
		for(int vert = 0; vert < gl_in.length(); vert++)
		{
			int nextVert = (vert + 1) % gl_in.length();
			vec4 clip = gl_in[vert].gl_Position;
			vec4 diff = gl_in[nextVert].gl_Position - clip; // vector from vert to the next vertex in the list
			vec2 normal = normalize(vec2(diff.y, -diff.x)); // Computing the normal of a 2D vector is actually rather simple...
			for (int lineVert = 0; lineVert < 4; ++lineVert)
			{
				// This is the pixel offset along the normal axis
				vec2 yOffPixel = pixelOffsetDir[lineVert].y * normal * (outlineWidth / 2.0);
				// This is the offset of the vertex along the vector between this vertex and the next one in the triangle
				vec2 xOff = pixelOffsetDir[lineVert].x * diff.xy;

				// This is the final offset in clip space
				vec2 finalOffset = xOff + yOffPixel * vec2(vpwidth, vpheight) * clip.w;

				gl_Position = vec4(clip.xyz + vec3(finalOffset, 0.0), clip.w);
				vertOut.position = vertIn[vert].position;
				vertOut.normal = vertIn[vert].normal;
				vertOut.texCoord = vertIn[vert].texCoord;

                #prereplace IF_FLAG MODEL_SDR_FLAG_NORMAL
                	vertOut.tangentMatrix = vertIn[vert].tangentMatrix;
                #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_NORMAL

                #prereplace IF_FLAG MODEL_SDR_FLAG_FOG
                	vertOut.fogDist = vertIn[vert].fogDist;
                #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG

                #prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS
                	vertOut.shadowUV[0] = vertIn[vert].shadowUV[0];
                	vertOut.shadowUV[1] = vertIn[vert].shadowUV[1];
                	vertOut.shadowUV[2] = vertIn[vert].shadowUV[2];
                	vertOut.shadowUV[3] = vertIn[vert].shadowUV[3];
                	vertOut.shadowPos = vertIn[vert].shadowPos;
                #prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS

				if (use_clip_plane) {
					gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
				}
				EmitVertex();
			}

			EndPrimitive();
		}
	}
#endif
