#pragma strict

@script ExecuteInEditMode
@script RequireComponent (Camera)
@script AddComponentMenu ("Image Effects/Bloom (HDR, Lens Flares)")

enum LensflareStyle34 {
	Ghosting = 0,
	Anamorphic = 1,
	Combined = 2,
}

enum TweakMode34 {
	Basic = 0,
	Complex = 1,
}

enum HDRBloomMode {
	Auto = 0,
	On = 1,
	Off = 2,
}

enum BloomScreenBlendMode {
	Screen = 0,
	Add = 1,	
}
				
class BloomAndLensFlares extends PostEffectsBase {
	public var tweakMode : TweakMode34 = 0;
	public var screenBlendMode : BloomScreenBlendMode = BloomScreenBlendMode.Add;
	
	public var hdr : HDRBloomMode = HDRBloomMode.Auto;
	private var doHdr : boolean = false;
	public var sepBlurSpread : float = 1.5f;
	public var useSrcAlphaAsMask : float = 0.5f;
	
	public var bloomIntensity : float = 1.0f;
	public var bloomThreshhold : float = 0.5f;
	public var bloomBlurIterations : int = 2;	
		
	public var lensflares : boolean = false;
	public var hollywoodFlareBlurIterations : int = 2;
	public var lensflareMode : LensflareStyle34 = 1;
	public var hollyStretchWidth : float = 3.5f;
	public var lensflareIntensity : float = 1.0f;
	public var lensflareThreshhold : float = 0.3f;
	public var flareColorA : Color = Color (0.4f, 0.4f, 0.8f, 0.75f);
	public var flareColorB : Color = Color (0.4f, 0.8f, 0.8f, 0.75f);
	public var flareColorC : Color = Color (0.8f, 0.4f, 0.8f, 0.75f);
	public var flareColorD : Color = Color (0.8f, 0.4f, 0.0f, 0.75f);
	public var blurWidth : float = 1.0f;	
	public var lensFlareVignetteMask : Texture2D;
				
	public var lensFlareShader : Shader; 
	private var lensFlareMaterial : Material;
	
	public var vignetteShader : Shader;
	private var vignetteMaterial : Material;
	
	public var separableBlurShader : Shader;
	private var separableBlurMaterial : Material;
	
	public var addBrightStuffOneOneShader: Shader;
	private var addBrightStuffBlendOneOneMaterial : Material;

	public var screenBlendShader : Shader;
	private var screenBlend : Material;
	
	public var hollywoodFlaresShader: Shader;
	private var hollywoodFlaresMaterial : Material;
	
	public var brightPassFilterShader : Shader;
	private var brightPassFilterMaterial : Material;
	
    function OnDisable()
    {
		if (screenBlend) 
		    DestroyImmediate(screenBlend);
		if (lensFlareMaterial) 
		    DestroyImmediate(lensFlareMaterial);
		if (vignetteMaterial) 
		    DestroyImmediate(vignetteMaterial);
		if (separableBlurMaterial) 
		    DestroyImmediate(separableBlurMaterial);
		if (addBrightStuffBlendOneOneMaterial) 
		    DestroyImmediate(addBrightStuffBlendOneOneMaterial);
		if (hollywoodFlaresMaterial) 
		    DestroyImmediate(hollywoodFlaresMaterial);
		if (brightPassFilterMaterial) 
		    DestroyImmediate(brightPassFilterMaterial);
    }
	function CheckResources () : boolean {
		CheckSupport (false);
		
		screenBlend = CheckShaderAndCreateMaterial (screenBlendShader, screenBlend);
		lensFlareMaterial = CheckShaderAndCreateMaterial(lensFlareShader,lensFlareMaterial);
		vignetteMaterial = CheckShaderAndCreateMaterial(vignetteShader,vignetteMaterial);
		separableBlurMaterial = CheckShaderAndCreateMaterial(separableBlurShader,separableBlurMaterial);
		addBrightStuffBlendOneOneMaterial = CheckShaderAndCreateMaterial(addBrightStuffOneOneShader,addBrightStuffBlendOneOneMaterial);
		hollywoodFlaresMaterial = CheckShaderAndCreateMaterial (hollywoodFlaresShader, hollywoodFlaresMaterial);
		brightPassFilterMaterial = CheckShaderAndCreateMaterial(brightPassFilterShader, brightPassFilterMaterial);
		
		if(!isSupported)
			ReportAutoDisable ();
		return isSupported;
	}
	
	function OnRenderImage (source : RenderTexture, destination : RenderTexture) {			
		if(CheckResources()==false) {
			Graphics.Blit (source, destination);
			return;
		}
				
		// screen blend is not supported when HDR is enabled (will cap values)
		
		doHdr = false;
		if(hdr == HDRBloomMode.Auto)
			doHdr = source.format == RenderTextureFormat.ARGBHalf && camera.hdr;
		else {
			doHdr = hdr == HDRBloomMode.On;
		}
		
		doHdr = doHdr && supportHDRTextures;
		
		var realBlendMode : BloomScreenBlendMode = screenBlendMode;
		if(doHdr)
			realBlendMode = BloomScreenBlendMode.Add;
		
		var rtFormat = (doHdr) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.Default;
		var halfRezColor : RenderTexture = RenderTexture.GetTemporary (source.width / 2, source.height / 2, 0, rtFormat);			
		var quarterRezColor : RenderTexture = RenderTexture.GetTemporary (source.width / 4, source.height / 4, 0, rtFormat);	
		var secondQuarterRezColor : RenderTexture = RenderTexture.GetTemporary (source.width / 4, source.height / 4, 0, rtFormat);	
		var thirdQuarterRezColor : RenderTexture = RenderTexture.GetTemporary (source.width / 4, source.height / 4, 0, rtFormat);	
		
		var widthOverHeight : float = (1.0f * source.width) / (1.0f * source.height);
		var oneOverBaseSize : float = 1.0f / 512.0f;
		
		// downsample
		 
		Graphics.Blit (source, halfRezColor, screenBlend, 2); // <- 2 is stable downsample
		Graphics.Blit (halfRezColor, quarterRezColor, screenBlend, 2); // <- 2 is stable downsample	
		
		RenderTexture.ReleaseTemporary (halfRezColor);			

		// cut colors (threshholding)			
		
		BrightFilter (bloomThreshhold, useSrcAlphaAsMask, quarterRezColor, secondQuarterRezColor);		
				
		// blurring
		
		if (bloomBlurIterations < 1) bloomBlurIterations = 1;	
				        
		for (var iter : int = 0; iter < bloomBlurIterations; iter++ ) {
			var spreadForPass : float = (1.0f + (iter * 0.5f)) * sepBlurSpread;
			separableBlurMaterial.SetVector ("offsets", Vector4 (0.0f, spreadForPass * oneOverBaseSize, 0.0f, 0.0f));	
			Graphics.Blit (iter == 0 ? secondQuarterRezColor : quarterRezColor, thirdQuarterRezColor, separableBlurMaterial); 
			separableBlurMaterial.SetVector ("offsets", Vector4 ((spreadForPass / widthOverHeight) * oneOverBaseSize, 0.0f, 0.0f, 0.0f));	
			Graphics.Blit (thirdQuarterRezColor, quarterRezColor, separableBlurMaterial);		
		}

		// lens flares: ghosting, anamorphic or a combination 
		
		if (lensflares) {							
			 
			if (lensflareMode == 0) {
							
				BrightFilter (lensflareThreshhold, 0.0f, quarterRezColor, thirdQuarterRezColor);				
				
				// smooth a little, this needs to be resolution dependent
				/*				
				separableBlurMaterial.SetVector ("offsets", Vector4 (0.0f, (2.0f) / (1.0f * quarterRezColor.height), 0.0f, 0.0f));	
				Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, separableBlurMaterial);				
				separableBlurMaterial.SetVector ("offsets", Vector4 ((2.0f) / (1.0f * quarterRezColor.width), 0.0f, 0.0f, 0.0f));	
				Graphics.Blit (secondQuarterRezColor, thirdQuarterRezColor, separableBlurMaterial); 
				*/
				// no ugly edges!
				
				Vignette (0.975, thirdQuarterRezColor, secondQuarterRezColor);		
				BlendFlares (secondQuarterRezColor, quarterRezColor);
			} 
			
			// (b) hollywood/anamorphic flares?
			
			else {
				
				// thirdQuarter has the brightcut unblurred colors
				// quarterRezColor is the blurred, brightcut buffer that will end up as bloom
				
				hollywoodFlaresMaterial.SetVector ("_Threshhold", Vector4 (lensflareThreshhold, 1.0f / (1.0f - lensflareThreshhold), 0.0f, 0.0f));
				hollywoodFlaresMaterial.SetVector ("tintColor", Vector4 (flareColorA.r, flareColorA.g, flareColorA.b, flareColorA.a) * flareColorA.a * lensflareIntensity);
				Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, hollywoodFlaresMaterial, 2); 	
				Graphics.Blit (secondQuarterRezColor, thirdQuarterRezColor, hollywoodFlaresMaterial, 3); 	
				
				hollywoodFlaresMaterial.SetVector ("offsets", Vector4 ((sepBlurSpread * 1.0f / widthOverHeight) * oneOverBaseSize, 0.0, 0.0, 0.0));	
				hollywoodFlaresMaterial.SetFloat ("stretchWidth", hollyStretchWidth);
				Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, hollywoodFlaresMaterial, 1);	
				hollywoodFlaresMaterial.SetFloat ("stretchWidth", hollyStretchWidth * 2.0f);
				Graphics.Blit (secondQuarterRezColor, thirdQuarterRezColor, hollywoodFlaresMaterial, 1);	
				hollywoodFlaresMaterial.SetFloat ("stretchWidth", hollyStretchWidth * 4.0f);
				Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, hollywoodFlaresMaterial, 1);	
												
				if (lensflareMode == 1) {															
					for (var itera : int = 0; itera < hollywoodFlareBlurIterations; itera++ ) {
						separableBlurMaterial.SetVector ("offsets", Vector4 ((hollyStretchWidth * 2.0f / widthOverHeight) * oneOverBaseSize, 0.0, 0.0, 0.0));	
						Graphics.Blit (secondQuarterRezColor, thirdQuarterRezColor, separableBlurMaterial);
						separableBlurMaterial.SetVector ("offsets", Vector4 ((hollyStretchWidth * 2.0f / widthOverHeight) * oneOverBaseSize, 0.0, 0.0, 0.0));	
						Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, separableBlurMaterial); 						
					}		
								
					AddTo (1.0, secondQuarterRezColor, quarterRezColor);
				}  
				else {			
				
					// (c) combined
																			
					for (var ix : int = 0; ix < hollywoodFlareBlurIterations; ix++ ) {
						separableBlurMaterial.SetVector ("offsets", Vector4 ((hollyStretchWidth * 2.0f / widthOverHeight) * oneOverBaseSize, 0.0, 0.0, 0.0));	
						Graphics.Blit (secondQuarterRezColor, thirdQuarterRezColor, separableBlurMaterial); 
						separableBlurMaterial.SetVector ("offsets", Vector4 ((hollyStretchWidth * 2.0f / widthOverHeight) * oneOverBaseSize, 0.0, 0.0, 0.0));	
						Graphics.Blit (thirdQuarterRezColor, secondQuarterRezColor, separableBlurMaterial); 							
					}		
				
					Vignette (1.0, secondQuarterRezColor, thirdQuarterRezColor);
					BlendFlares (thirdQuarterRezColor, secondQuarterRezColor);
					AddTo (1.0, secondQuarterRezColor, quarterRezColor);
				}																						
			}
		}		
		
		// screen blend bloom results to color buffer
		
		screenBlend.SetFloat ("_Intensity", bloomIntensity);
		screenBlend.SetTexture ("_ColorBuffer", source);
		Graphics.Blit (quarterRezColor, destination, screenBlend, realBlendMode);		
		
		RenderTexture.ReleaseTemporary (quarterRezColor);	
		RenderTexture.ReleaseTemporary (secondQuarterRezColor);	
		RenderTexture.ReleaseTemporary (thirdQuarterRezColor);		
	}
	
	private function AddTo (intensity_ : float, from : RenderTexture, to : RenderTexture) {
		addBrightStuffBlendOneOneMaterial.SetFloat ("_Intensity", intensity_);
		Graphics.Blit (from, to, addBrightStuffBlendOneOneMaterial); 		
	}
	
	private function BlendFlares (from : RenderTexture, to : RenderTexture) {
		lensFlareMaterial.SetVector ("colorA", Vector4 (flareColorA.r, flareColorA.g, flareColorA.b, flareColorA.a) * lensflareIntensity);
		lensFlareMaterial.SetVector ("colorB", Vector4 (flareColorB.r, flareColorB.g, flareColorB.b, flareColorB.a) * lensflareIntensity);
		lensFlareMaterial.SetVector ("colorC", Vector4 (flareColorC.r, flareColorC.g, flareColorC.b, flareColorC.a) * lensflareIntensity);
		lensFlareMaterial.SetVector ("colorD", Vector4 (flareColorD.r, flareColorD.g, flareColorD.b, flareColorD.a) * lensflareIntensity);
		Graphics.Blit (from, to, lensFlareMaterial);			
	}

	private function BrightFilter (thresh : float, useAlphaAsMask : float, from : RenderTexture, to : RenderTexture) {
		if(doHdr)
			brightPassFilterMaterial.SetVector ("threshhold", Vector4 (thresh, 1.0f, 0.0f, 0.0f));
		else
			brightPassFilterMaterial.SetVector ("threshhold", Vector4 (thresh, 1.0f / (1.0f-thresh), 0.0f, 0.0f));
		brightPassFilterMaterial.SetFloat ("useSrcAlphaAsMask", useAlphaAsMask);
		Graphics.Blit (from, to, brightPassFilterMaterial);			
	}
	
	private function Vignette (amount : float, from : RenderTexture, to : RenderTexture) {
		if(lensFlareVignetteMask) {
			screenBlend.SetTexture ("_ColorBuffer", lensFlareVignetteMask);
			Graphics.Blit (from, to, screenBlend, 3); 				
		} 
		else {
			vignetteMaterial.SetFloat ("vignetteIntensity", amount);
			Graphics.Blit (from, to, vignetteMaterial); 		
		}
	}

}