context: using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;
  
  [AddComponentMenu("2D Toolkit/Camera/tk2dCamera")]
  [ExecuteInEditMode]
  /// <summary>
  /// Maintains a screen resolution camera. 
  /// Whole number increments seen through this camera represent one pixel.
  /// For example, setting an object to 300, 300 will position it at exactly that pixel position.
  /// </summary>
  public class tk2dCamera : MonoBehaviour 
  {
  	static int CURRENT_VERSION = 1;
  	public int version = 0;
  
  	[SerializeField] private tk2dCameraSettings cameraSettings = new tk2dCameraSettings();
  
  	/// <summary>
  	/// The unity camera settings. 
  	/// Use this instead of camera.XXX to change parameters.
  	/// </summary>
  	public tk2dCameraSettings CameraSettings {
  		get {
  			return cameraSettings;
  		}
  	}
  
  	/// <summary>
  	/// Resolution overrides, if necessary. See <see cref="tk2dCameraResolutionOverride"/>
  	/// </summary>
  	public tk2dCameraResolutionOverride[] resolutionOverride = new tk2dCameraResolutionOverride[1] {
  		tk2dCameraResolutionOverride.DefaultOverride
  	};
  
  	/// <summary>
  	/// The currently used override
  	/// </summary>
  	public tk2dCameraResolutionOverride CurrentResolutionOverride {
  		get { 
  			tk2dCamera settings = SettingsRoot;
  			Camera cam = ScreenCamera;
  
  			float pixelWidth = cam.pixelWidth;
  			float pixelHeight = cam.pixelHeight;
  #if UNITY_EDITOR
  			if (settings.useGameWindowResolutionInEditor) {
  				pixelWidth = settings.gameWindowResolution.x;
  				pixelHeight = settings.gameWindowResolution.y;
  			}
  			else if (settings.forceResolutionInEditor)
  			{
  				pixelWidth = settings.forceResolution.x;
  				pixelHeight = settings.forceResolution.y;
  			}
  #endif
  
  			tk2dCameraResolutionOverride currentResolutionOverride = null;
  
  			if ((currentResolutionOverride == null ||
  				(currentResolutionOverride != null && (currentResolutionOverride.width != pixelWidth || currentResolutionOverride.height != pixelHeight))
  				))
  			{
  				currentResolutionOverride = null;
  
  				// find one if it matches the current resolution
  				if (settings.resolutionOverride != null)
  				{
  					foreach (var ovr in settings.resolutionOverride)
  					{
  						if (ovr.Match((int)pixelWidth, (int)pixelHeight))
  						{
  							currentResolutionOverride = ovr;
  							break;
  						}
  					}
  				}
  			}
  
  			return currentResolutionOverride;
  		}
  	}
  
  	/// <summary>
  	/// A tk2dCamera to inherit configuration from. 
  	/// All resolution and override settings will be pulled from the root inherited camera.
  	/// This allows you to create a tk2dCamera prefab in your project or a master camera
  	/// in the scene and guarantee that multiple instances of tk2dCameras referencing this
  	/// will use exactly the same paramaters. 
  	/// </summary>
  	public tk2dCamera InheritConfig {
  		get { return inheritSettings; }
  		set {
  			if (inheritSettings != value) {
  				inheritSettings = value;
  				_settingsRoot = null;
  			}
  		}
  	}
  
  	[SerializeField]
  	private tk2dCamera inheritSettings = null;
  	
  	/// <summary>
  	/// Native resolution width of the camera. Override this in the inspector.
  	/// Don't change this at runtime unless you understand the implications.
  	/// </summary>
  	public int nativeResolutionWidth = 960;
  
  	/// <summary>
  	/// Native resolution height of the camera. Override this in the inspector.
  	/// Don't change this at runtime unless you understand the implications.
  	/// </summary>
  	public int nativeResolutionHeight = 640;
  	
  	[SerializeField]
  	private Camera _unityCamera;
  	private Camera UnityCamera {
  		get {
  			if (_unityCamera == null) {
  				_unityCamera = GetComponent<Camera>();
  				if (_unityCamera == null) {
  					Debug.LogError("A unity camera must be attached to the tk2dCamera script");
  				}
  			}
  			return _unityCamera;
  		}
  	}
  
  
  	static tk2dCamera inst;
  
  	/// <summary>
  	/// Global instance, used by sprite and textmesh class to quickly find the tk2dCamera instance.
  	/// </summary>
  	public  static tk2dCamera Instance {
  		get {
  			return inst;
  		}
  	}
  
  	// Global instance of active tk2dCameras, used to quickly find cameras matching a particular layer.
  	private static List<tk2dCamera> allCameras = new List<tk2dCamera>();
  
  	/// <summary>
  	/// Returns the first camera in the list that can "see" this layer, or null if none can be found
  	/// </summary>
  	public static tk2dCamera CameraForLayer( int layer ) {
  		int layerMask = 1 << layer;
  		int cameraCount = allCameras.Count;
  		for (int i = 0; i < cameraCount; ++i) {
  			tk2dCamera cam = allCameras[i];
  			if ((cam.UnityCamera.cullingMask & layerMask) == layerMask) {
  				return cam;
  			}
  		}
  		return null;
  	}
  
  	/// <summary>
  	/// Returns screen extents - top, bottom, left and right will be the extent of the physical screen
  	/// Regardless of resolution or override
  	/// </summary>
  	public Rect ScreenExtents { get { return _screenExtents; } }
  
  	/// <summary>
  	/// Returns screen extents - top, bottom, left and right will be the extent of the native screen
  	/// before it gets scaled and processed by overrides
  	/// </summary>
  	public Rect NativeScreenExtents { get { return _nativeScreenExtents; } }
  
  	/// <summary>
  	/// Enable/disable viewport clipping.
  	/// ScreenCamera must be valid for it to be actually enabled when rendering.
  	/// </summary>
  	public bool viewportClippingEnabled = false;
  
  	/// <summary>
  	/// Viewport clipping region.
  	/// </summary>
  	public Vector4 viewportRegion = new Vector4(0, 0, 100, 100);
  
  	/// <summary>
  	/// Target resolution
  	/// The target resolution currently being used.
  	/// If displaying on a 960x640 display, this will be the number returned here, regardless of scale, etc.
  	/// If the editor resolution is forced, the returned value will be the forced resolution.
  	/// </summary>
  	public Vector2 TargetResolution { get { return _targetResolution; } }
  	Vector2 _targetResolution = Vector2.zero;
  
  	/// <summary>
  	/// Native resolution
  	/// The native resolution of this camera.
  	/// This is the native resolution of the camera before any scaling is performed.
  	/// The resolution the game is set up to run at initially.
  	/// </summary>
  	public Vector2 NativeResolution { get { return new Vector2(nativeResolutionWidth, nativeResolutionHeight); } }
  
  	// Some obselete functions, use ScreenExtents instead
  	[System.Obsolete] public Vector2 ScreenOffset { get { return new Vector2(ScreenExtents.xMin - NativeScreenExtents.xMin, ScreenExtents.yMin - NativeScreenExtents.yMin); } }
  	[System.Obsolete] public Vector2 resolution { get { return new Vector2( ScreenExtents.xMax, ScreenExtents.yMax ); } }
  	[System.Obsolete] public Vector2 ScreenResolution { get { return new Vector2( ScreenExtents.xMax, ScreenExtents.yMax ); } }
  	[System.Obsolete] public Vector2 ScaledResolution { get { return new Vector2( ScreenExtents.width, ScreenExtents.height ); } }
  
  	/// <summary>
  	/// Zooms the current display
  	/// A zoom factor of 2 will zoom in 2x, i.e. the object on screen will be twice as large
  	/// Anchors will still be anchored, but will be scaled with the zoomScale.
  	/// It is recommended to use a second camera for HUDs if necessary to avoid this behaviour.
  	/// </summary>
  	public float ZoomFactor {
  		get { return zoomFactor; }
  		set { zoomFactor = Mathf.Max(0.01f, value); }
  	}
  
  	/// <summary>
  	/// Obselete - use <see cref="ZoomFactor"/> instead.
  	/// </summary>
  	[System.Obsolete]
  	public float zoomScale {
  		get { return 1.0f / Mathf.Max(0.001f, zoomFactor); }
  		set { ZoomFactor = 1.0f / Mathf.Max(0.001f, value); }
  	}
  
  	[SerializeField] float zoomFactor = 1.0f;
  
  
  	[HideInInspector]
  	/// <summary>
  	/// Forces the resolution in the editor. This option is only used when tk2dCamera can't detect the game window resolution.
  	/// </summary>
  	public bool forceResolutionInEditor = false;
  
  	[HideInInspector]
  	/// <summary>
  	/// The resolution to force the game window to when <see cref="forceResolutionInEditor"/> is enabled.
  	/// </summary>
  	public Vector2 forceResolution = new Vector2(960, 640);
  	
  #if UNITY_EDITOR
  	// When true, overrides the "forceResolutionInEditor" flag above
     	bool useGameWindowResolutionInEditor = false;
  
  	// Usred when useGameWindowResolutionInEditor == true
  	Vector2 gameWindowResolution = new Vector2(960, 640);
  #endif
  
  	/// <summary>
  	/// The camera that sees the screen - i.e. if viewport clipping is enabled, its the camera that sees the entire screen
  	/// </summary>
  	public Camera ScreenCamera {
  		get {
  			bool viewportClippingEnabled = this.viewportClippingEnabled && this.inheritSettings != null && this.inheritSettings.UnityCamera.rect == unitRect;
  			return viewportClippingEnabled ? this.inheritSettings.UnityCamera : UnityCamera;
  		}
  	}
  
  	// Use this for initialization
  	void Awake () {
  		Upgrade();
  		if (allCameras.IndexOf(this) == -1) {
  			allCameras.Add(this);
  		}
  
  		tk2dCamera settings = SettingsRoot;
  		tk2dCameraSettings inheritedCameraSettings = settings.CameraSettings;
  		if (inheritedCameraSettings.projection == tk2dCameraSettings.ProjectionType.Perspective) {
  			UnityCamera.transparencySortMode = inheritedCameraSettings.transparencySortMode;
  		}
  	}
  
  	void OnEnable() {
  		if (UnityCamera != null) {
  			UpdateCameraMatrix();
  		}
  		else {
  			this.GetComponent<Camera>().enabled = false;
  		}
  		
  		if (!viewportClippingEnabled) // the main camera can't display rect
  			inst = this;
  
  		if (allCameras.IndexOf(this) == -1) {
  			allCameras.Add(this);
  		}
  	}
  
  	void OnDestroy() {
  		int idx = allCameras.IndexOf(this);
  		if (idx != -1) {
  			allCameras.RemoveAt( idx );
  		}
  	}
  	
  	void OnPreCull() {
  		// Commit all pending changes - this more or less guarantees
  		// everything is committed before drawing this camera.
  		tk2dUpdateManager.FlushQueues();
  		UpdateCameraMatrix();
  	}
  
  #if UNITY_EDITOR
  	void LateUpdate() {
  		if (!Application.isPlaying) {
  			UpdateCameraMatrix();
  		}
  	}
  #endif
  
  	Rect _screenExtents;
  	Rect _nativeScreenExtents;
  	Rect unitRect = new Rect(0, 0, 1, 1);
  
  	// Gives you the size of one pixel in world units at the native resolution
  	// For perspective cameras, it is dependent on the distance to the camera.
  	public float GetSizeAtDistance(float distance) {
  		tk2dCameraSettings cameraSettings = SettingsRoot.CameraSettings;
  		switch (cameraSettings.projection) {
  			case tk2dCameraSettings.ProjectionType.Orthographic:
  				if (cameraSettings.orthographicType == tk2dCameraSettings.OrthographicType.PixelsPerMeter) {
  					return 1.0f / cameraSettings.orthographicPixelsPerMeter;
  				}
  				else {
  					return 2.0f * cameraSettings.orthographicSize / SettingsRoot.nativeResolutionHeight;
  				}
  			case tk2dCameraSettings.ProjectionType.Perspective:
  				return Mathf.Tan(CameraSettings.fieldOfView * Mathf.Deg2Rad * 0.5f) * distance * 2.0f / SettingsRoot.nativeResolutionHeight;
  		}
  		return 1;
  	}
  
  	
  	// This returns the tk2dCamera object which has the settings stored on it
  	// Trace back to the source, however far up the hierarchy that may be
  	// You can't change this at runtime
  	tk2dCamera _settingsRoot;
  	public tk2dCamera SettingsRoot {
  		get { 
  			if (_settingsRoot == null) {
  				_settingsRoot = (inheritSettings == null || inheritSettings == this) ? this : inheritSettings.SettingsRoot;	
  			}
  			return _settingsRoot;
  		}
  	}
  
  #if UNITY_EDITOR
  	public static tk2dCamera Editor__Inst {
  		get {
  			if (inst != null) {
  				return inst;
  			}
  			return GameObject.FindObjectOfType(typeof(tk2dCamera)) as tk2dCamera;
  		}
  	}
  #endif
  	
  #if UNITY_EDITOR
  	static bool Editor__getGameViewSizeError = false;
  	public static bool Editor__gameViewReflectionError = false;
  
  	// Try and get game view size
  	// Will return true if it is able to work this out
  	// If width / height == 0, it means the user has selected an aspect ratio "Resolution"
  	public static bool Editor__GetGameViewSize(out float width, out float height, out float aspect) {
  		try {
  			Editor__gameViewReflectionError = false;
  
  			System.Type gameViewType = System.Type.GetType("UnityEditor.GameView,UnityEditor");
  			System.Reflection.MethodInfo GetMainGameView = gameViewType.GetMethod("GetMainGameView", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
  			object mainGameViewInst = GetMainGameView.Invoke(null, null);
  			if (mainGameViewInst == null) {
  				width = height = aspect = 0;
  				return false;
  			}
  			System.Reflection.FieldInfo s_viewModeResolutions = gameViewType.GetField("s_viewModeResolutions", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
  			if (s_viewModeResolutions == null) {
  				System.Reflection.PropertyInfo currentGameViewSize = gameViewType.GetProperty("currentGameViewSize", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
  				object gameViewSize = currentGameViewSize.GetValue(mainGameViewInst, null);
  				System.Type gameViewSizeType = gameViewSize.GetType();
  				int gvWidth = (int)gameViewSizeType.GetProperty("width").GetValue(gameViewSize, null);
  				int gvHeight = (int)gameViewSizeType.GetProperty("height").GetValue(gameViewSize, null);
  				int gvSizeType = (int)gameViewSizeType.GetProperty("sizeType").GetValue(gameViewSize, null);
  				if (gvWidth == 0 || gvHeight == 0) {
  					width = height = aspect = 0;
  					return false;
  				}
  				else if (gvSizeType == 0) {
  					width = height = 0;
  					aspect = (float)gvWidth / (float)gvHeight;
  					return true;
  				}
  				else {
  					width = gvWidth; height = gvHeight;
  					aspect = (float)gvWidth / (float)gvHeight;
  					return true;
  				}
  			}
  			else {
  				Vector2[] viewModeResolutions = (Vector2[])s_viewModeResolutions.GetValue(null);
  				float[] viewModeAspects = (float[])gameViewType.GetField("s_viewModeAspects", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(null);
  				string[] viewModeStrings = (string[])gameViewType.GetField("s_viewModeAspectStrings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(null);
  				if (mainGameViewInst != null 
  					&& viewModeStrings != null
  					&& viewModeResolutions != null && viewModeAspects != null) {
  					int aspectRatio = (int)gameViewType.GetField("m_AspectRatio", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(mainGameViewInst);
  					string thisViewModeString = viewModeStrings[aspectRatio];
  					if (thisViewModeString.Contains("Standalone")) {
  						width = UnityEditor.PlayerSettings.defaultScreenWidth; height = UnityEditor.PlayerSettings.defaultScreenHeight;
  						aspect = width / height;
  					}
  					else if (thisViewModeString.Contains("Web")) {
  						width = UnityEditor.PlayerSettings.defaultWebScreenWidth; height = UnityEditor.PlayerSettings.defaultWebScreenHeight;
  						aspect = width / height;
  					}
  					else {
  						width = viewModeResolutions[ aspectRatio ].x; height = viewModeResolutions[ aspectRatio ].y;
  						aspect = viewModeAspects[ aspectRatio ];
  						// this is an error state
  						if (width == 0 && height == 0 && aspect == 0) {
  							return false;
  						}
  					}
  					return true;
  				}
  			}
  		}
  		catch (System.Exception e) {
  			if (Editor__getGameViewSizeError == false) {
  				Debug.LogError("tk2dCamera.GetGameViewSize - has a Unity update broken this?\nThis is not a fatal error, but a warning that you've probably not got the latest 2D Toolkit update.\n\n" + e.ToString());
  				Editor__getGameViewSizeError = true;
  			}
  			Editor__gameViewReflectionError = true;
  		}
  		width = height = aspect = 0;
  		return false;
  	}
  #endif
  
  	public Matrix4x4 OrthoOffCenter(Vector2 scale, float left, float right, float bottom, float top, float near, float far) {
  		// Additional half texel offset
  		// Takes care of texture unit offset, if necessary.
  		
  		float x =  (2.0f) / (right - left) * scale.x;
  		float y = (2.0f) / (top - bottom) * scale.y;
  		float z = -2.0f / (far - near);
  
  		float a = -(right + left) / (right - left);
  		float b = -(bottom + top) / (top - bottom);
  		float c = -(far + near) / (far - near);
  		
  		Matrix4x4 m = new Matrix4x4();
  		m[0,0] = x;  m[0,1] = 0;  m[0,2] = 0;  m[0,3] = a;
  		m[1,0] = 0;  m[1,1] = y;  m[1,2] = 0;  m[1,3] = b;
  		m[2,0] = 0;  m[2,1] = 0;  m[2,2] = z;  m[2,3] = c;
  		m[3,0] = 0;  m[3,1] = 0;  m[3,2] = 0;  m[3,3] = 1;
  
  		return m;
  	}
  
  	Vector2 GetScaleForOverride(tk2dCamera settings, tk2dCameraResolutionOverride currentOverride, float width, float height) {
  		Vector2 scale = Vector2.one;
  		float s = 1.0f;
  
  		if (currentOverride == null) {
  			return scale;
  		}
  
  		switch (currentOverride.autoScaleMode)
  		{
  		case tk2dCameraResolutionOverride.AutoScaleMode.PixelPerfect:
  			s = 1;
  			scale.Set(s, s);
  			break;
  
  		case tk2dCameraResolutionOverride.AutoScaleMode.FitHeight: 
  			s = height / settings.nativeResolutionHeight; 
  			scale.Set(s, s);
  			break;
  
  		case tk2dCameraResolutionOverride.AutoScaleMode.FitWidth: 
  			s = width / settings.nativeResolutionWidth; 
  			scale.Set(s, s);
  			break;
  
  		case tk2dCameraResolutionOverride.AutoScaleMode.FitVisible:
  		case tk2dCameraResolutionOverride.AutoScaleMode.ClosestMultipleOfTwo:
  			float nativeAspect = (float)settings.nativeResolutionWidth / settings.nativeResolutionHeight;
  			float currentAspect = width / height;
  			if (currentAspect < nativeAspect)
  				s = width / settings.nativeResolutionWidth;
  			else
  				s = height / settings.nativeResolutionHeight;
  			
  			if (currentOverride.autoScaleMode == tk2dCameraResolutionOverride.AutoScaleMode.ClosestMultipleOfTwo)
  			{
  				if (s > 1.0f)
  					s = Mathf.Floor(s); // round number
  				else
  					s = Mathf.Pow(2, Mathf.Floor(Mathf.Log(s, 2))); // minimise only as power of two
  			}
  			
  			scale.Set(s, s);
  			break;
  
  		case tk2dCameraResolutionOverride.AutoScaleMode.StretchToFit:
  			scale.Set(width / settings.nativeResolutionWidth, height / settings.nativeResolutionHeight);
  			break;
  
  		case tk2dCameraResolutionOverride.AutoScaleMode.Fill:
  			s = Mathf.Max(width / settings.nativeResolutionWidth,height / settings.nativeResolutionHeight);
  			scale.Set(s, s);
  			break;
  
  		default:
  		case tk2dCameraResolutionOverride.AutoScaleMode.None: 
  			s = currentOverride.scale;
  			scale.Set(s, s);
  			break;
  		}
  
  		return scale;
  	}
  
  	Vector2 GetOffsetForOverride(tk2dCamera settings, tk2dCameraResolutionOverride currentOverride, Vector2 scale, float width, float height) {
  		Vector2 offset = Vector2.zero;
  		if (currentOverride == null) {
  			return offset;
  		}
  
  		switch (currentOverride.fitMode) {
  			case tk2dCameraResolutionOverride.FitMode.Center:
  				if (settings.cameraSettings.orthographicOrigin == tk2dCameraSettings.OrthographicOrigin.BottomLeft) {
  					offset = new Vector2(Mathf.Round((settings.nativeResolutionWidth  * scale.x - width ) / 2.0f), 
  										 Mathf.Round((settings.nativeResolutionHeight * scale.y - height) / 2.0f));
  				}
  				break;
  				
  			default:
  			case tk2dCameraResolutionOverride.FitMode.Constant: 
  				offset = -currentOverride.offsetPixels; 
  				break;
  		}
  		return offset;
  	}
  
  #if UNITY_EDITOR
  	private Matrix4x4 Editor__GetPerspectiveMatrix() {
  		float aspect = (float)nativeResolutionWidth / (float)nativeResolutionHeight;
  		return Matrix4x4.Perspective(SettingsRoot.CameraSettings.fieldOfView, aspect, UnityCamera.nearClipPlane, UnityCamera.farClipPlane);
  	}
  
  	public Matrix4x4 Editor__GetNativeProjectionMatrix(  ) {
  		tk2dCamera settings = SettingsRoot;
  		if (settings.CameraSettings.projection == tk2dCameraSettings.ProjectionType.Perspective) {
  			return Editor__GetPerspectiveMatrix();
  		}
  		Rect rect1 = new Rect(0, 0, 1, 1);
  		Rect rect2 = new Rect(0, 0, 1, 1);
  		return GetProjectionMatrixForOverride( settings, null, nativeResolutionWidth, nativeResolutionHeight, false, out rect1, out rect2 );
  	}
  
  	public Matrix4x4 Editor__GetFinalProjectionMatrix(  ) {
  		tk2dCamera settings = SettingsRoot;
  		if (settings.CameraSettings.projection == tk2dCameraSettings.ProjectionType.Perspective) {
  			return Editor__GetPerspectiveMatrix();
  		}
  		Vector2 resolution = GetScreenPixelDimensions(settings);
  		Rect rect1 = new Rect(0, 0, 1, 1);
  		Rect rect2 = new Rect(0, 0, 1, 1);
  		return GetProjectionMatrixForOverride( settings, settings.CurrentResolutionOverride, resolution.x, resolution.y, false, out rect1, out rect2 );
  	}
  #endif
  
  	Matrix4x4 GetProjectionMatrixForOverride( tk2dCamera settings, tk2dCameraResolutionOverride currentOverride, float pixelWidth, float pixelHeight, bool halfTexelOffset, out Rect screenExtents, out Rect unscaledScreenExtents ) {
  		Vector2 scale = GetScaleForOverride( settings, currentOverride, pixelWidth, pixelHeight );
  		Vector2 offset = GetOffsetForOverride( settings, currentOverride, scale, pixelWidth, pixelHeight);
  		
  		float left = offset.x, bottom = offset.y;
  		float right = pixelWidth + offset.x, top = pixelHeight + offset.y;
  		Vector2 nativeResolutionOffset = Vector2.zero;
  
  		bool usingLegacyViewportClipping = false;
  
  		// Correct for viewport clipping rendering
  		// Coordinates in subrect are "native" pixels, but origin is from the extrema of screen
  		if (this.viewportClippingEnabled && this.InheritConfig != null) {
  			float vw = (right - left) / scale.x;
  			float vh = (top - bottom) / scale.y;
  			Vector4 sr = new Vector4((int)this.viewportRegion.x, (int)this.viewportRegion.y,
  									 (int)this.viewportRegion.z, (int)this.viewportRegion.w);
  
  			usingLegacyViewportClipping = true;
  	
  			float viewportLeft = -offset.x / pixelWidth + sr.x / vw;
  			float viewportBottom = -offset.y / pixelHeight + sr.y / vh;
  			float viewportWidth = sr.z / vw;
  			float viewportHeight = sr.w / vh;
  			if (settings.cameraSettings.orthographicOrigin == tk2dCameraSettings.OrthographicOrigin.Center) {
  				viewportLeft += (pixelWidth - settings.nativeResolutionWidth * scale.x) / pixelWidth / 2.0f;
  				viewportBottom += (pixelHeight - settings.nativeResolutionHeight * scale.y) / pixelHeight / 2.0f;
  			}
  
  			Rect r = new Rect( viewportLeft, viewportBottom, viewportWidth, viewportHeight );
  			if (UnityCamera.rect.x != viewportLeft ||
  				UnityCamera.rect.y != viewportBottom ||
  				UnityCamera.rect.width != viewportWidth ||
  				UnityCamera.rect.height != viewportHeight) {
  				UnityCamera.rect = r;
  			}
  
  			float maxWidth = Mathf.Min( 1.0f - r.x, r.width );
  			float maxHeight = Mathf.Min( 1.0f - r.y, r.height );
  
  			float rectOffsetX = sr.x * scale.x - offset.x;
  			float rectOffsetY = sr.y * scale.y - offset.y;
  
  			if (settings.cameraSettings.orthographicOrigin == tk2dCameraSettings.OrthographicOrigin.Center) {
  				rectOffsetX -= settings.nativeResolutionWidth * 0.5f * scale.x;
  				rectOffsetY -= settings.nativeResolutionHeight * 0.5f * scale.y;
  			}
  
  			if (r.x < 0.0f) {
  				rectOffsetX += -r.x * pixelWidth;
  				maxWidth = (r.x + r.width);
  			}
  			if (r.y < 0.0f) {
  				rectOffsetY += -r.y * pixelHeight;
  				maxHeight = (r.y + r.height);
  			}
  
  			left += rectOffsetX;
  			bottom += rectOffsetY;
  			right = pixelWidth * maxWidth + offset.x + rectOffsetX;
  			top = pixelHeight * maxHeight + offset.y +  rectOffsetY;
  		}
  		else {
  			if (UnityCamera.rect != CameraSettings.rect) {
  				UnityCamera.rect = CameraSettings.rect;
  			}
  
  			// By default the camera is orthographic, bottom left, 1 pixel per meter
  			if (settings.cameraSettings.orthographicOrigin == tk2dCameraSettings.OrthographicOrigin.Center) {
  				float w = (right - left) * 0.5f;
  				left -= w; right -= w;
  				float h = (top - bottom) * 0.5f;
  				top -= h; bottom -= h;
  				nativeResolutionOffset.Set(-nativeResolutionWidth / 2.0f, -nativeResolutionHeight / 2.0f);
  			}
  
  		}
  
  		float zoomScale = 1.0f / ZoomFactor;
  
  		// Only need the half texel offset on PC/D3D
  		bool needHalfTexelOffset = (Application.platform == RuntimePlatform.WindowsPlayer ||
  						   			Application.platform == RuntimePlatform.WebGLPlayer ||
  						   			Application.platform == RuntimePlatform.WindowsEditor);
  		float halfTexel = (halfTexelOffset && needHalfTexelOffset) ? 0.5f : 0.0f;
  
  		float orthoSize = settings.cameraSettings.orthographicSize;
  		switch (settings.cameraSettings.orthographicType) {
  			case tk2dCameraSettings.OrthographicType.OrthographicSize:
  				orthoSize = 2.0f * settings.cameraSettings.orthographicSize / settings.nativeResolutionHeight;
  				break;
  			case tk2dCameraSettings.OrthographicType.PixelsPerMeter:
  				orthoSize = 1.0f / settings.cameraSettings.orthographicPixelsPerMeter;
  				break;
  		}
  
  		// Fixup for clipping
  		if (!usingLegacyViewportClipping) {
  			float clipWidth = Mathf.Min(UnityCamera.rect.width, 1.0f - UnityCamera.rect.x);
  			float clipHeight = Mathf.Min(UnityCamera.rect.height, 1.0f - UnityCamera.rect.y);
  			if (clipWidth > 0 && clipHeight > 0) {
  				scale.x /= clipWidth;
  				scale.y /= clipHeight;
  			}
  		}
  
  		float s = orthoSize * zoomScale;
  		screenExtents = new Rect(left * s / scale.x, bottom * s / scale.y, 
  						   		 (right - left) * s / scale.x, (top - bottom) * s / scale.y);
  
  		unscaledScreenExtents = new Rect(nativeResolutionOffset.x * s, nativeResolutionOffset.y * s,
  										 nativeResolutionWidth * s, nativeResolutionHeight * s);
  
  		// Near and far clip planes are tweakable per camera, so we pull from current camera instance regardless of inherited values
  		return OrthoOffCenter(scale, orthoSize * (left + halfTexel) * zoomScale, orthoSize * (right + halfTexel) * zoomScale, 
  									 orthoSize * (bottom - halfTexel) * zoomScale, orthoSize * (top - halfTexel) * zoomScale, 
  									 UnityCamera.nearClipPlane, UnityCamera.farClipPlane);
  	}
  
  	Vector2 GetScreenPixelDimensions(tk2dCamera settings) {
  		Vector2 dimensions = new Vector2(ScreenCamera.pixelWidth, ScreenCamera.pixelHeight);
  
  #if UNITY_EDITOR
  		// This bit here allocates memory, but only runs in the editor
  		float gameViewPixelWidth = 0, gameViewPixelHeight = 0;
  		float gameViewAspect = 0;
  		settings.useGameWindowResolutionInEditor = false;
  		if (Editor__GetGameViewSize( out gameViewPixelWidth, out gameViewPixelHeight, out gameViewAspect)) {
  			if (gameViewPixelWidth != 0 && gameViewPixelHeight != 0) {
  				if (!settings.useGameWindowResolutionInEditor ||
  					settings.gameWindowResolution.x != gameViewPixelWidth ||
  					settings.gameWindowResolution.y != gameViewPixelHeight) {
  					settings.useGameWindowResolutionInEditor = true;
  					settings.gameWindowResolution.x = gameViewPixelWidth;
  					settings.gameWindowResolution.y = gameViewPixelHeight;
  				}
  				dimensions.x = settings.gameWindowResolution.x;
  				dimensions.y = settings.gameWindowResolution.y;
  			}
  		}
  
  		if (!settings.useGameWindowResolutionInEditor && settings.forceResolutionInEditor)
  		{
  			dimensions.x = settings.forceResolution.x;
  			dimensions.y = settings.forceResolution.y;
  		}
  #endif
  		
  		return dimensions;
  	}
  
  	private void Upgrade() {
  		if (version != CURRENT_VERSION) {
  			if (version == 0) {
  				// Backwards compatibility
  				cameraSettings.orthographicPixelsPerMeter = 1;
  				cameraSettings.orthographicType = tk2dCameraSettings.OrthographicType.PixelsPerMeter;
  				cameraSettings.orthographicOrigin = tk2dCameraSettings.OrthographicOrigin.BottomLeft;
  				cameraSettings.projection = tk2dCameraSettings.ProjectionType.Orthographic;
  
  				foreach (tk2dCameraResolutionOverride ovr in resolutionOverride) {
  					ovr.Upgrade( version );
  				}
  
  				// Mirror camera settings
  				Camera unityCamera = GetComponent<Camera>();
  				if (unityCamera != null) {
  					cameraSettings.rect = unityCamera.rect;
  					if (!unityCamera.orthographic) {
  						cameraSettings.projection = tk2dCameraSettings.ProjectionType.Perspective;
  						cameraSettings.fieldOfView = unityCamera.fieldOfView * ZoomFactor;
  					}
  
  					unityCamera.hideFlags = HideFlags.HideInInspector | HideFlags.HideInHierarchy;
  				}
  			}
  
  			Debug.Log("tk2dCamera '" + this.name + "' - Upgraded from version " + version.ToString());
  			version = CURRENT_VERSION;
  		}
  	}
  
  	/// <summary>
  	/// Updates the camera matrix to ensure 1:1 pixel mapping
  	/// Or however the override is set up.
  	/// </summary>
  	public void UpdateCameraMatrix()
  	{
  		Upgrade();
  
  		if (!this.viewportClippingEnabled)
  			inst = this;
  
  		Camera unityCamera = UnityCamera;
  		tk2dCamera settings = SettingsRoot;
  		tk2dCameraSettings inheritedCameraSettings = settings.CameraSettings;
  
  		if (unityCamera.rect != cameraSettings.rect) unityCamera.rect = cameraSettings.rect;
  
  		// Projection type is inherited from base camera
  		_targetResolution = GetScreenPixelDimensions(settings);
  
  		if (inheritedCameraSettings.projection == tk2dCameraSettings.ProjectionType.Perspective) {
  			if (unityCamera.orthographic == true) unityCamera.orthographic = false;
  			float fov = Mathf.Min(179.9f, inheritedCameraSettings.fieldOfView / Mathf.Max(0.001f, ZoomFactor));
  			if (unityCamera.fieldOfView != fov) unityCamera.fieldOfView = fov;
  			_screenExtents.Set( -unityCamera.aspect, -1, unityCamera.aspect * 2, 2 );
  			_nativeScreenExtents = _screenExtents;
  			unityCamera.ResetProjectionMatrix();
  		}
  		else {
  			if (unityCamera.orthographic == false) unityCamera.orthographic = true;
  			// Find an override if necessary
  			Matrix4x4 m = GetProjectionMatrixForOverride( settings, settings.CurrentResolutionOverride, _targetResolution.x, _targetResolution.y, true, out _screenExtents, out _nativeScreenExtents );
  
  #if !(UNITY_3_5 || UNITY_4_0 || UNITY_4_1)
  			// Windows phone?
  			if (Application.platform == RuntimePlatform.WP8Player &&
  			    (Screen.orientation == ScreenOrientation.LandscapeLeft || Screen.orientation == ScreenOrientation.LandscapeRight)) {
  				float angle = (Screen.orientation == ScreenOrientation.LandscapeRight) ? 90.0f : -90.0f;
  				Matrix4x4 m2 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 0, angle), Vector3.one);
  
gt: 				m = m2 * m;