// Originally By James O'Hare, from his Gist: https://gist.github.com/Farfarer/5664694
// This takes in the cubemap generated by your cubemap camera and feeds back out an equirectangular image.
// Create a new material and give it this shader. Then give that material to the "cubemapToEquirectangularMateral" property of the dynamicAmbient.js script in this gist.
// You could probably abstract this to C#/JS code and feed it in a pre-baked cubemap to sample and then spit out an equirectangular map if you don't have render textures.
Shader "Hidden/CubemapToEquirectangular" 
{
	Properties 
	{
		_MainTex	("Cubemap (RGB)",	CUBE)	= "" {}
		//
		_Face_Left	("_Face_Left",		2D)		= "white" {}
		_Face_Right	("_Face_Right",		2D)		= "white" {}
		_Face_Up	("_Face_Up",		2D)		= "white" {}
		_Face_Down	("_Face_Down",		2D)		= "white" {}
		_Face_Front	("_Face_Front",		2D)		= "white" {}
		_Face_Back	("_Face_Back",		2D)		= "white" {}
	}

	CGINCLUDE

	#include "UnityCG.cginc"
	
	struct appdata
	{
		float4 vertex : POSITION;
		float2 texcoord : TEXCOORD0;
	};

	struct v2f 
	{
		float4 pos : SV_POSITION;
		float2 uv : TEXCOORD0;
	};

#ifndef USE_BLENDING
	uniform samplerCUBE	_MainTex;
#else
	uniform sampler2D	_Face_Left;
	uniform sampler2D	_Face_Right;
	uniform sampler2D	_Face_Up;
	uniform sampler2D	_Face_Down;
	uniform sampler2D	_Face_Front;
	uniform sampler2D	_Face_Back;
	uniform float		_inverseOverlap;
#endif
	uniform float		_FlipX;

	#define PI 3.141592653589793
	#define HALFPI 1.57079632679

	v2f vert(appdata v )
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.vertex);

		#if STEREOPACK_TOP
		o.pos.y = (o.pos.y / 2.0) - 0.5;
		#elif STEREOPACK_BOTTOM
		o.pos.y = (o.pos.y / 2.0) + 0.5;
		#elif STEREOPACK_LEFT
		o.pos.x = (o.pos.x / 2.0) - 0.5;
		#elif STEREOPACK_RIGHT
		o.pos.x = (o.pos.x / 2.0) + 0.5;
		#endif

		float2 uv = v.texcoord.xy;
		if (_FlipX > 0.5)
		{
			uv.x = 1.0 - uv.x;
		}
		#if defined(LAYOUT_EQUIRECT180)
		uv = uv * float2(1.0, 2.0) - float2(0.0, 1.0);
		#else
		uv = uv * 2.0 - float2(0.5, 1.0);
		#endif
		uv *= float2(PI, HALFPI);
		
		o.uv = uv;
		return o;
	}

#if USE_BLENDING
	fixed4 texCUBE_Face(sampler2D tex, float3 normal, float inverseOverlap = 1.0f)
	{
		float2 original	= (normal.xy * (inverseOverlap / normal.z));
		float2 weight	= smoothstep(1.0f, inverseOverlap, abs(original));
		float2 uv		= ((original + 1.0f) * 0.5f);
		uv.y			= (1.0f - uv.y);

		fixed4 colour;
		// RJT TODO: Better blending?
		colour.a = /*length(weight);*/min(weight.x, weight.y);
		colour.rgb = (tex2D(tex, uv) * colour.a);
		return colour;
	}
#endif

	fixed4 frag(v2f i) : COLOR 
	{
		float cosy = cos(i.uv.y);
		float3 normal;
		normal.x = cos(i.uv.x) * cosy;
		normal.y = i.uv.y;
		normal.z = cos(i.uv.x - HALFPI) * cosy;
#ifndef USE_BLENDING
		return texCUBE(_MainTex, /*normalize*/(normal));
#else
		fixed4 colour;
		float3 absNormal = abs(normal);
	#if 0	// Individual faces, no blending
		if ((absNormal.y <= absNormal.x) && (absNormal.z <= absNormal.x))	// X
		{
			float3 _normal = float3((normal.z * -sign(normal.x)), normal.y, absNormal.x);
			if (normal.x < 0.0f) { colour = texCUBE_Face(_Face_Left, _normal); }
			else { colour = texCUBE_Face(_Face_Right, _normal); }
		}
		else if (absNormal.z <= absNormal.y)								// Y
		{
			float3 _normal = float3(normal.x, (normal.z * -sign(normal.y)), absNormal.y);
			if (normal.y < 0.0f) { colour = texCUBE_Face(_Face_Down, _normal); }
			else { colour = texCUBE_Face(_Face_Up, _normal); }
		}
		else																// Z
		{
			float3 _normal = float3((normal.x * sign(normal.z)), normal.y, absNormal.z);
			if (normal.z < 0.0f) { colour = texCUBE_Face(_Face_Back, _normal); }
			else { colour = texCUBE_Face(_Face_Front, _normal); }
		}
	#else	// Individual faces, with blending
		colour = fixed4(0.0f, 0.0f, 0.0f, 0.0f);
		float3 originalAbsNormal = (absNormal * _inverseOverlap);
		if ((originalAbsNormal.y <= absNormal.x) && (originalAbsNormal.z <= absNormal.x))	// X
		{
			float3 _normal = float3((normal.z * -sign(normal.x)), normal.y, absNormal.x);
			if (normal.x < 0.0f) { colour += texCUBE_Face(_Face_Left, _normal, _inverseOverlap); }
			else { colour += texCUBE_Face(_Face_Right, _normal, _inverseOverlap); }
		}
		if ((originalAbsNormal.x <= absNormal.y) && (originalAbsNormal.z <= absNormal.y))	// Y
		{
			float3 _normal = float3(normal.x, (normal.z * -sign(normal.y)), absNormal.y);
			if (normal.y < 0.0f) { colour += texCUBE_Face(_Face_Down, _normal, _inverseOverlap); }
			else { colour += texCUBE_Face(_Face_Up, _normal, _inverseOverlap); }
		}
		if ((originalAbsNormal.x <= absNormal.z) && (originalAbsNormal.y <= absNormal.z))	// Z
		{
			float3 _normal = float3((normal.x * sign(normal.z)), normal.y, absNormal.z);
			if (normal.z < 0.0f) { colour += texCUBE_Face(_Face_Back, _normal, _inverseOverlap); }
			else { colour += texCUBE_Face(_Face_Front, _normal, _inverseOverlap); }
		}
		colour.rgb /= colour.a;
	#endif
		colour.a = 1.0;
		return colour;
#endif	// #ifndef USE_BLENDING #else
	}
	ENDCG

	Subshader 
	{
		ZTest Always
		Cull Off
		ZWrite Off
		Fog{ Mode off }

		Pass
		{
			CGPROGRAM
			#pragma shader_feature USE_BLENDING
			#pragma multi_compile __ STEREOPACK_TOP STEREOPACK_BOTTOM STEREOPACK_LEFT STEREOPACK_RIGHT
			#pragma multi_compile LAYOUT_EQUIRECT360 LAYOUT_EQUIRECT180
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
	}
	Fallback Off
}