Initial Commit

This commit is contained in:
Simeon Radivoev 2022-02-12 12:53:50 +02:00
commit ee5c2f922d
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
2255 changed files with 547750 additions and 0 deletions

View file

@ -0,0 +1,449 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Light2D;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Rendering;
namespace Light2D
{
/// <summary>
/// Custom sprite wich uses MeshFilter and MeshRenderer to render.
/// Main improvement from Unity SpriteRenderer is that you can access and modify mesh.
/// Also multiple CustomSprites could be merged to single mesh with MeshCombiner,
/// which gives much better performance for small meshes than StaticBatchingUtility.Combine.
/// </summary>
[ExecuteInEditMode]
public class CustomSprite : MonoBehaviour
{
/// <summary>
/// Vertex color of mesh.
/// </summary>
public Color Color = Color.white;
/// <summary>
/// Sprite from which mesh will be generated.
/// </summary>
public Sprite Sprite;
/// <summary>
/// Sorting order of MeshRenderer.
/// </summary>
public int SortingOrder;
/// <summary>
/// Material to be used.
/// </summary>
public Material Material;
// mesh data
protected Color[] _colors;
protected Vector2[] _uv0;
protected Vector2[] _uv1;
protected Vector3[] _vertices;
protected int[] _triangles;
protected Vector4[] _tangents;
protected bool _isMeshDirty = false;
protected MeshRenderer _meshRenderer;
protected MeshFilter _meshFilter;
protected Mesh _mesh;
private Color _oldColor;
private Sprite _oldSprite;
private Material _oldMaterial;
private MaterialKey _oldMaterialKey;
public static Dictionary<MaterialKey, MaterialValue> MaterialMap = new Dictionary<MaterialKey, MaterialValue>();
private const string GeneratedMaterialName = "Generated Material (DONT change it)";
private const string GeneratedMeshName = "Generated Mesh (DONT change it)";
public bool RendererEnabled { get; private set; }
/// <summary>
/// Is that sprite is staticaly batched?
/// </summary>
public bool IsPartOfStaticBatch
{
get { return _meshRenderer.isPartOfStaticBatch; }
}
protected virtual void OnEnable()
{
_colors = new Color[4];
_uv1 = new Vector2[4];
_uv0 = new Vector2[4];
_vertices = new Vector3[4];
_triangles = new[] {2, 1, 0, 1, 2, 3};
_meshRenderer = GetComponent<MeshRenderer>();
_meshFilter = GetComponent<MeshFilter>();
if (_meshRenderer == null)
{
_meshRenderer = gameObject.AddComponent<MeshRenderer>();
_meshRenderer.receiveShadows = false;
_meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
}
if (_meshFilter == null)
{
_meshFilter = gameObject.AddComponent<MeshFilter>();
}
if (Material == null)
{
#if UNITY_EDITOR
Material = AssetDatabase.GetBuiltinExtraResource<Material>("Sprites-Default.mat");
#else
Material = Resources.GetBuiltinResource<Material>("Sprites-Default.mat");
#endif
}
TryReleaseMesh();
_meshFilter.sharedMesh = _mesh = new Mesh();
_mesh.MarkDynamic();
_mesh.name = GeneratedMeshName;
_tangents = new Vector4[4];
for (int i = 0; i < _tangents.Length; i++)
_tangents[i] = new Vector4(1, 0, 0);
UpdateMeshData(true);
RendererEnabled = _meshRenderer.enabled;
}
protected virtual void Start()
{
UpdateMeshData(true);
}
private void OnWillRenderObject()
{
UpdateMeshData();
if (Application.isPlaying && LightingSystem.Instance.EnableNormalMapping)
{
RendererEnabled = _meshRenderer.enabled;
_meshRenderer.enabled = false;
}
}
private void OnRenderObject()
{
if (Application.isPlaying && LightingSystem.Instance.EnableNormalMapping)
{
_meshRenderer.enabled = RendererEnabled;
}
}
/// <summary>
/// Getting material from cache or instantiating new one.
/// </summary>
/// <returns></returns>
public Material GetOrCreateMaterial()
{
TryReleaseMaterial();
if (Material == null || Sprite == null)
return null;
MaterialValue matValue;
var key = new MaterialKey(Material, Sprite.texture);
if (!MaterialMap.TryGetValue(key, out matValue))
{
var mat = (Material)Instantiate(Material);
mat.name = GeneratedMaterialName;
mat.mainTexture = Sprite.texture;
MaterialMap[key] = matValue = new MaterialValue(mat, 1);
}
else
{
matValue.UsageCount++;
}
_oldMaterialKey = key;
return matValue.Material;
}
/// <summary>
/// Getting material from cache or instantiating new one.
/// </summary>
/// <returns></returns>
public static Material GetOrCreateMaterial(Material baseMaterial, Texture2D texture, out MaterialKey materialKey)
{
if (baseMaterial == null || texture == null)
{
materialKey = null;
return null;
}
MaterialValue matValue;
var key = materialKey = new MaterialKey(baseMaterial, texture);
if (!MaterialMap.TryGetValue(key, out matValue))
{
var mat = (Material)Instantiate(baseMaterial);
mat.name = GeneratedMaterialName;
mat.mainTexture = texture;
MaterialMap[key] = matValue = new MaterialValue(mat, 1);
}
else
{
matValue.UsageCount++;
}
return matValue.Material;
}
/// <summary>
/// Deleting material from cache with reference counting.
/// </summary>
/// <param name="key"></param>
public static void ReleaseMaterial(MaterialKey key)
{
MaterialValue matValue;
if (!MaterialMap.TryGetValue(key, out matValue))
return;
matValue.UsageCount--;
if (matValue.UsageCount <= 0)
{
Util.Destroy(matValue.Material);
MaterialMap.Remove(key);
}
}
void TryReleaseMesh()
{
if (_meshFilter != null && _meshFilter.sharedMesh != null &&
_meshFilter.sharedMesh.name == GeneratedMeshName && _mesh == _meshFilter.sharedMesh)
{
Util.Destroy(_meshFilter.sharedMesh);
_meshFilter.sharedMesh = null;
}
}
void TryReleaseMaterial()
{
if (_oldMaterialKey != default(MaterialKey))
{
ReleaseMaterial(_oldMaterialKey);
_oldMaterialKey = default(MaterialKey);
}
}
void OnDestroy()
{
TryReleaseMesh();
TryReleaseMaterial();
}
protected virtual void UpdateColor()
{
for (int i = 0; i < _colors.Length; i++)
_colors[i] = Color;
}
/// <summary>
/// Recreating mesh data for Sprite based on it's bounds.
/// </summary>
protected virtual void UpdateSprite()
{
if (Sprite == null)
return;
var rect = Sprite.textureRect;
var bounds = Sprite.bounds;
var tex = Sprite.texture;
var textureSize = new Point2(tex.width, tex.height);
// HACK: mipmap could cause texture padding sometimes so padded size of texture needs to be computed.
var realSize =
#if UNITY_EDITOR || UNITY_STANDALONE
tex.mipmapCount <= 1
#else
true
#endif
? textureSize
: new Point2(Mathf.NextPowerOfTwo(textureSize.x), Mathf.NextPowerOfTwo(textureSize.y));
var unitSize2 = rect.size/Sprite.pixelsPerUnit/2f;
var offest = (Vector2) bounds.center;
_vertices[0] = new Vector3(-unitSize2.x + offest.x, -unitSize2.y + offest.y, 0);
_vertices[1] = new Vector3(unitSize2.x + offest.x, -unitSize2.y + offest.y, 0);
_vertices[2] = new Vector3(-unitSize2.x + offest.x, unitSize2.y + offest.y, 0);
_vertices[3] = new Vector3(unitSize2.x + offest.x, unitSize2.y + offest.y, 0);
_uv0[0] = new Vector2(rect.xMin/realSize.x, rect.yMin/realSize.y); // 0, 0
_uv0[1] = new Vector2(rect.xMax/realSize.x, rect.yMin/realSize.y); // 1, 0
_uv0[2] = new Vector2(rect.xMin/realSize.x, rect.yMax/realSize.y); // 0, 1
_uv0[3] = new Vector2(rect.xMax/realSize.x, rect.yMax/realSize.y); // 1, 1
for (int i = 0; i < 4; i++)
{
_colors[i] = Color;
}
_meshRenderer.sharedMaterial = GetOrCreateMaterial();
}
/// <summary>
/// Clearing and rebuilding mesh.
/// </summary>
protected virtual void UpdateMesh()
{
_mesh.Clear();
_mesh.vertices = _vertices;
_mesh.triangles = _triangles;
_mesh.uv = _uv0;
_mesh.uv2 = _uv1;
_mesh.colors = _colors;
_mesh.tangents = _tangents;
_mesh.RecalculateBounds();
}
/// <summary>
/// Checking public fields and mesh data, then rebuilding internal state if changes found.
/// </summary>
/// <param name="forceUpdate">Force update even if no changes found.</param>
protected virtual void UpdateMeshData(bool forceUpdate = false)
{
if (_meshRenderer == null || _meshFilter == null || IsPartOfStaticBatch)
return;
_meshRenderer.sortingOrder = SortingOrder;
if (Color != _oldColor || forceUpdate)
{
UpdateColor();
_isMeshDirty = true;
_oldColor = Color;
}
if (Sprite != _oldSprite || Material != _oldMaterial || forceUpdate)
{
UpdateSprite();
_isMeshDirty = true;
_oldSprite = Sprite;
_oldMaterial = Material;
}
if (_isMeshDirty)
{
UpdateMesh();
_isMeshDirty = false;
}
}
/// <summary>
/// Used as a value to material map to support reference counting.
/// </summary>
public class MaterialValue
{
/// <summary>
/// Instantiated material from MaterialKey.Material with texture from MaterialKey.Texture.
/// </summary>
public Material Material;
/// <summary>
/// Count of CustomSprites using that material.
/// </summary>
public int UsageCount;
public MaterialValue(Material material, int usageCount)
{
Material = material;
UsageCount = usageCount;
}
}
/// <summary>
/// Used as a key to material map.
/// </summary>
public class MaterialKey : IEquatable<MaterialKey>
{
/// <summary>
/// Sprite's texture.
/// </summary>
public Texture2D Texture;
/// <summary>
/// Non instantiated material.
/// </summary>
public Material Material;
public MaterialKey(Material material, Texture2D texture)
{
Material = material;
Texture = texture;
}
private sealed class TextureMaterialEqualityComparer : IEqualityComparer<MaterialKey>
{
public bool Equals(MaterialKey x, MaterialKey y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return Equals(x.Texture, y.Texture) && Equals(x.Material, y.Material);
}
public int GetHashCode(MaterialKey obj)
{
unchecked
{
return ((obj.Texture != null ? obj.Texture.GetHashCode() : 0)*397) ^ (obj.Material != null ? obj.Material.GetHashCode() : 0);
}
}
}
private static readonly IEqualityComparer<MaterialKey> TextureMaterialComparerInstance = new TextureMaterialEqualityComparer();
public static IEqualityComparer<MaterialKey> TextureMaterialComparer
{
get { return TextureMaterialComparerInstance; }
}
public bool Equals(MaterialKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(Texture, other.Texture) && Equals(Material, other.Material);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MaterialKey) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((Texture != null ? Texture.GetHashCode() : 0)*397) ^ (Material != null ? Material.GetHashCode() : 0);
}
}
public static bool operator ==(MaterialKey left, MaterialKey right)
{
return Equals(left, right);
}
public static bool operator !=(MaterialKey left, MaterialKey right)
{
return !Equals(left, right);
}
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 29d8c03e86a89194f8ee931d9cf69ba1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// That class is generating obstacles for object it attached to.
/// Obect must have MeshRenderer, SpriteRenderer or CustomSprite script from which texture for obstacle will be grabbed.
/// For rendering obstacle of SpriteRenderer and CustomSprite LightObstacleSprite with material "Material" (material with dual color shader by default) will be used.
/// For objects with MeshRenderer "Material" property is ignored. MeshRenderer.sharedMaterial is used instead.
/// </summary>
[ExecuteInEditMode]
public class LightObstacleGenerator : MonoBehaviour
{
/// <summary>
/// Vertex color.
/// </summary>
public Color MultiplicativeColor = new Color(0, 0, 0, 1);
/// <summary>
/// AdditiveColor that will be used for obstacle when SpriteRenderer or CustomSprite scripts is attached.
/// Only DualColor shader supports additive color.
/// </summary>
public Color AdditiveColor;
/// <summary>
/// Material that will be used for obstacle when SpriteRenderer or CustomSprite scripts is attached.
/// </summary>
public Material Material;
public float LightObstacleScale = 1;
private void Start()
{
#if UNITY_EDITOR
if (Material == null)
{
Material = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>("Assets/Light2D/Materials/DualColor.mat");
}
#endif
if (!Application.isPlaying)
return;
var obstacleObj = new GameObject(gameObject.name + " Light Obstacle");
obstacleObj.transform.parent = gameObject.transform;
obstacleObj.transform.localPosition = Vector3.zero;
obstacleObj.transform.localRotation = Quaternion.identity;
obstacleObj.transform.localScale = Vector3.one*LightObstacleScale;
if (LightingSystem.Instance != null)
obstacleObj.layer = LightingSystem.Instance.LightObstaclesLayer;
if (GetComponent<SpriteRenderer>() != null || GetComponent<CustomSprite>() != null)
{
var obstacleSprite = obstacleObj.AddComponent<LightObstacleSprite>();
obstacleSprite.Color = MultiplicativeColor;
obstacleSprite.AdditiveColor = AdditiveColor;
obstacleSprite.Material = Material;
}
else
{
var obstacleMesh = obstacleObj.AddComponent<LightObstacleMesh>();
obstacleMesh.MultiplicativeColor = MultiplicativeColor;
obstacleMesh.AdditiveColor = AdditiveColor;
obstacleMesh.Material = Material;
}
Destroy(this);
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a0184ff86886f974d95dfead14754f36
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Automatically updating mesh, material and main texture of light obstacle.
/// Class is copying all data used for rendering from parent.
/// </summary>
public class LightObstacleMesh : MonoBehaviour
{
public Color32 MultiplicativeColor;
public Color AdditiveColor;
public Material Material;
private MeshRenderer _parentMeshRenderer;
private MeshFilter _parentMeshFilter;
private MeshRenderer _meshRenderer;
private MeshFilter _meshFilter;
private Mesh _oldParentMesh;
private Color32 _oldMulColor;
private Color _oldAddColor;
private Material _oldMaterial;
private CustomSprite.MaterialKey _materialKey;
void Awake()
{
_parentMeshRenderer = transform.parent.GetComponent<MeshRenderer>();
_parentMeshFilter = transform.parent.GetComponent<MeshFilter>();
_meshRenderer = GetComponent<MeshRenderer>();
if (_meshRenderer == null) _meshRenderer = gameObject.AddComponent<MeshRenderer>();
_meshFilter = GetComponent<MeshFilter>();
if (_meshFilter == null) _meshFilter = gameObject.AddComponent<MeshFilter>();
}
void Update()
{
Refresh();
}
void Refresh()
{
if (_parentMeshFilter == null || _parentMeshFilter == null || _meshRenderer == null || _meshFilter == null ||
_parentMeshFilter.sharedMesh == null || _parentMeshRenderer.sharedMaterial == null)
{
if (_meshRenderer != null)
_meshRenderer.enabled = false;
return;
}
bool dirty = false;
if (_parentMeshFilter.mesh != _oldParentMesh)
{
if (_meshFilter.mesh != null)
Destroy(_meshFilter.mesh);
_meshFilter.mesh = (Mesh) Instantiate(_parentMeshFilter.sharedMesh);
_meshFilter.mesh.MarkDynamic();
if (_meshFilter.mesh.tangents == null)
{
var tangents = new Vector4[_meshFilter.mesh.vertexCount];
for (int i = 0; i < tangents.Length; i++)
tangents[i] = new Vector4(1, 0);
_meshFilter.mesh.tangents = tangents;
}
_oldParentMesh = _parentMeshFilter.sharedMesh;
dirty = true;
}
if (_oldMaterial != _parentMeshRenderer.sharedMaterial ||
(_oldMaterial != null && _parentMeshRenderer.sharedMaterial != null &&
_oldMaterial.mainTexture != _parentMeshRenderer.sharedMaterial.mainTexture))
{
if (_meshRenderer.sharedMaterial != null && _materialKey != null)
{
CustomSprite.ReleaseMaterial(_materialKey);
}
var baseMat = Material == null ? _parentMeshRenderer.sharedMaterial : Material;
var tex = _parentMeshRenderer.sharedMaterial.mainTexture as Texture2D;
_meshRenderer.sharedMaterial = CustomSprite.GetOrCreateMaterial(baseMat, tex, out _materialKey);
_oldMaterial = _parentMeshRenderer.sharedMaterial;
}
if (!MultiplicativeColor.Equals(_oldMulColor) || AdditiveColor != _oldAddColor || dirty)
{
var colors = _meshFilter.mesh.colors32;
if (colors == null || colors.Length != _meshFilter.mesh.vertexCount)
colors = new Color32[_meshFilter.mesh.vertexCount];
for (int i = 0; i < colors.Length; i++)
colors[i] = MultiplicativeColor;
_meshFilter.mesh.colors32 = colors;
var uv1 = new Vector2(
Util.DecodeFloatRGBA((Vector4) AdditiveColor),
Util.DecodeFloatRGBA(new Vector4(AdditiveColor.a, 0, 0)));
var uv1Arr = _meshFilter.mesh.uv2;
if (uv1Arr == null || uv1Arr.Length != colors.Length)
uv1Arr = new Vector2[colors.Length];
for (int i = 0; i < uv1Arr.Length; i++)
{
uv1Arr[i] = uv1;
}
_meshFilter.mesh.uv2 = uv1Arr;
_oldMulColor = MultiplicativeColor;
_oldAddColor = AdditiveColor;
}
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 53b0c4c5d380d084db011bc570d0fe96
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Sprite with dual color support. Grabs sprite from GameSpriteRenderer field.
/// </summary>
[ExecuteInEditMode]
public class LightObstacleSprite : CustomSprite
{
/// <summary>
/// Renderer from which sprite will be used.
/// </summary>
public Renderer GameSpriteRenderer;
/// <summary>
/// Color is packed in mesh UV1.
/// </summary>
public Color AdditiveColor;
private Color _oldSecondaryColor;
private Renderer _oldGameSpriteRenderer;
private SpriteRenderer _oldUnitySprite;
private CustomSprite _oldCustomSprite;
protected override void OnEnable()
{
#if UNITY_EDITOR
if (Material == null)
{
Material = AssetDatabase.LoadAssetAtPath<Material>("Assets/Light2D/Materials/DualColor.mat");
}
#endif
base.OnEnable();
if (GameSpriteRenderer == null && transform.parent != null)
GameSpriteRenderer = transform.parent.gameObject.GetComponent<Renderer>();
gameObject.layer = LightingSystem.Instance.LightObstaclesLayer;
UpdateMeshData(true);
}
private void UpdateSecondaryColor()
{
var uv1 = new Vector2(
Util.DecodeFloatRGBA((Vector4)AdditiveColor),
Util.DecodeFloatRGBA(new Vector4(AdditiveColor.a, 0, 0)));
for (int i = 0; i < _uv1.Length; i++)
{
_uv1[i] = uv1;
}
}
protected override void UpdateMeshData(bool forceUpdate = false)
{
if (_meshRenderer == null || _meshFilter == null || IsPartOfStaticBatch)
return;
if (GameSpriteRenderer != null && (GameSpriteRenderer != _oldGameSpriteRenderer || forceUpdate ||
(_oldUnitySprite != null && _oldUnitySprite.sprite != null && _oldUnitySprite.sprite != Sprite) ||
(_oldCustomSprite != null && _oldCustomSprite.Sprite != null && _oldCustomSprite.Sprite != Sprite)))
{
_oldGameSpriteRenderer = GameSpriteRenderer;
_oldCustomSprite = GameSpriteRenderer.GetComponent<CustomSprite>();
if (_oldCustomSprite != null)
{
Sprite = _oldCustomSprite.Sprite;
}
else
{
_oldUnitySprite = GameSpriteRenderer.GetComponent<SpriteRenderer>();
if (_oldUnitySprite != null)
Sprite = _oldUnitySprite.sprite;
}
Material.EnableKeyword("NORMAL_TEXCOORD");
}
if (_oldSecondaryColor != AdditiveColor || forceUpdate)
{
UpdateSecondaryColor();
_isMeshDirty = true;
_oldSecondaryColor = AdditiveColor;
}
base.UpdateMeshData(forceUpdate);
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c849e7d3a5bc0ff408d0cfcf61f81906
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Used to draw lights. Puts LightOrigin world position to UV1.
/// Supports Point and Line light types.
/// </summary>
[ExecuteInEditMode]
public class LightSprite : CustomSprite
{
public static List<LightSprite> AllLightSprites = new List<LightSprite>();
public Vector3 LightOrigin = new Vector3(0, 0, 1);
public LightShape Shape = LightShape.Point;
private Matrix4x4 _modelMatrix;
private Vector3 _oldLightOrigin;
private LightShape _oldLightShape;
public MeshRenderer Renderer
{
get { return _meshRenderer; }
}
protected override void OnEnable()
{
base.OnEnable();
AllLightSprites.Add(this);
}
void OnDisable()
{
AllLightSprites.Remove(this);
}
/// <summary>
/// Update UV1 which is used for raytracking in shader. UV1 is set to world position of LightOrigin.
/// </summary>
private void UpdatePosition()
{
if (Sprite == null || !Application.isPlaying)
return;
var mat = _modelMatrix;
Vector2 size = Sprite.bounds.size;
if (Shape == LightShape.Point)
{
// LightOrigin needs to be send in world position instead of local because
// Unity non uniform scaling is breaking model matrix in shader.
var pos = mat.MultiplyPoint(((Vector2)LightOrigin).Mul(size));
for (int i = 0; i < _uv1.Length; i++)
_uv1[i] = pos;
}
else if (Shape == LightShape.Line)
{
var lpos = mat.MultiplyPoint(new Vector2(-0.5f, LightOrigin.y).Mul(size));
var rpos = mat.MultiplyPoint(new Vector2(0.5f, LightOrigin.y).Mul(size));
_uv1[0] = lpos;
_uv1[1] = rpos;
_uv1[2] = lpos;
_uv1[3] = rpos;
}
}
protected override void UpdateMeshData(bool forceUpdate = false)
{
if (IsPartOfStaticBatch)
return;
var objMat = transform.localToWorldMatrix;
if (!objMat.FastEquals(_modelMatrix) ||
_oldLightOrigin != LightOrigin || _oldLightShape != Shape || forceUpdate)
{
_modelMatrix = objMat;
_oldLightOrigin = LightOrigin;
_oldLightShape = Shape;
UpdatePosition();
_isMeshDirty = true;
}
base.UpdateMeshData(forceUpdate);
}
public enum LightShape
{
Point,
Line,
}
private void OnDrawGizmosSelected()
{
if (Sprite == null)
return;
var size = Sprite.bounds.size;
if (Shape == LightShape.Point)
{
var center = transform.TransformPoint(LightOrigin);
Gizmos.DrawLine(
center + transform.TransformDirection(new Vector2(-0.1f, 0)),
center + transform.TransformDirection(new Vector2(0.1f, 0)));
Gizmos.DrawLine(
center + transform.TransformDirection(new Vector2(0, -0.1f)),
center + transform.TransformDirection(new Vector2(0, 0.1f)));
}
else if (Shape == LightShape.Line && Sprite != null)
{
var lpos = transform.TransformPoint(new Vector3(-0.5f, LightOrigin.y).Mul(size));
var rpos = transform.TransformPoint(new Vector3(0.5f, LightOrigin.y).Mul(size));
Gizmos.DrawLine(lpos, rpos);
}
}
public void DrawLightingNow(Vector2 lightCamLocalPos)
{
var material = _meshRenderer.sharedMaterial;
if (!material.SetPass(0))
return;
var v1 = _modelMatrix.MultiplyPoint(_vertices[0]) - (Vector3)lightCamLocalPos;
var v2 = _modelMatrix.MultiplyPoint(_vertices[2]) - (Vector3)lightCamLocalPos;
var v3 = _modelMatrix.MultiplyPoint(_vertices[3]) - (Vector3)lightCamLocalPos;
var v4 = _modelMatrix.MultiplyPoint(_vertices[1]) - (Vector3)lightCamLocalPos;
GL.Begin(GL.QUADS);
GL.Color(Color);
GL.MultiTexCoord(0, _uv0[0]);
GL.MultiTexCoord(1, _uv1[0] - lightCamLocalPos);
GL.Vertex(v1);
GL.MultiTexCoord(0, _uv0[2]);
GL.MultiTexCoord(1, _uv1[2] - lightCamLocalPos);
GL.Vertex(v2);
GL.MultiTexCoord(0, _uv0[3]);
GL.MultiTexCoord(1, _uv1[3] - lightCamLocalPos);
GL.Vertex(v3);
GL.MultiTexCoord(0, _uv0[1]);
GL.MultiTexCoord(1, _uv1[1] - lightCamLocalPos);
GL.Vertex(v4);
GL.End();
}
public void DrawLightNormalsNow(Material material)
{
Vector2 size = Sprite.bounds.size;
Vector2 center = _modelMatrix.MultiplyPoint3x4(((Vector2) LightOrigin).Mul(size));
var lightPos = new Vector4(center.x, center.y, LightOrigin.z);
material.SetVector("_LightPos", lightPos);
if (!material.SetPass(0))
return;
var v1 = _modelMatrix.MultiplyPoint3x4(_vertices[0]);
var v2 = _modelMatrix.MultiplyPoint3x4(_vertices[2]);
var v3 = _modelMatrix.MultiplyPoint3x4(_vertices[3]);
var v4 = _modelMatrix.MultiplyPoint3x4(_vertices[1]);
GL.Begin(GL.QUADS);
GL.Vertex(v1);
GL.Vertex(v2);
GL.Vertex(v3);
GL.Vertex(v4);
GL.End();
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 61930e16e81db5c43a4ecc22422a0953
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 2
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,773 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Main script for lights. Should be attached to camera.
/// Handles lighting operation like camera setup, shader setup, merging cameras output together, blurring and some others.
/// </summary>
[ExecuteInEditMode]
[RequireComponent(typeof (Camera))]
public class LightingSystem : MonoBehaviour
{
/// <summary>
/// Size of lighting pixel in Unity meters. Controls resoultion of lighting textures.
/// Smaller value - better quality, but lower performance.
/// </summary>
public float LightPixelSize = 0.05f;
/// <summary>
/// Needed for off screen lights to work correctly. Set that value to radius of largest light.
/// Used only when camera is in orthographic mode. Big values could cause a performance drop.
/// </summary>
public float LightCameraSizeAdd = 3;
/// <summary>
/// Needed for off screen lights to work correctly.
/// Used only when camera is in perspective mode.
/// </summary>
public float LightCameraFovAdd = 30;
/// <summary>
/// Enable/disable ambient lights. Disable it to improve performance if you not using ambient light.
/// </summary>
public bool EnableAmbientLight = true;
/// <summary>
/// LightSourcesBlurMaterial is applied to light sources texture if enabled. Disable to improve performance.
/// </summary>
public bool BlurLightSources = true;
/// <summary>
/// AmbientLightBlurMaterial is applied to ambient light texture if enabled. Disable to improve performance.
/// </summary>
public bool BlurAmbientLight = true;
/// <summary>
/// If true RGBHalf RenderTexture type will be used for light processing.
/// That could improve smoothness of lights. Will be turned off if device is not supports it.
/// </summary>
public bool HDR = true;
/// <summary>
/// If true light obstacles will be rendered in 2x resolution and then downsampled to 1x.
/// </summary>
public bool LightObstaclesAntialiasing = true;
/// <summary>
/// Set it to distance from camera to plane with light obstacles. Used only when camera in perspective mode.
/// </summary>
public float LightObstaclesDistance = 10;
/// <summary>
/// Billinear for blurred lights, Point for pixelated lights.
/// </summary>
public FilterMode LightTexturesFilterMode = FilterMode.Bilinear;
/// <summary>
/// Normal mapping. Not supported on mobiles.
/// </summary>
public bool EnableNormalMapping = false;
/// <summary>
/// If true lighting won't be seen on contents of previous cameras.
/// </summary>
public bool AffectOnlyThisCamera;
public Material AmbientLightComputeMaterial;
public Material LightOverlayMaterial;
public Material LightSourcesBlurMaterial;
public Material AmbientLightBlurMaterial;
public Camera LightCamera;
public int LightSourcesLayer;
public int AmbientLightLayer;
public int LightObstaclesLayer;
public LayerMask LightObstaclesReplacementShaderLayer;
private RenderTexture _ambientEmissionTexture;
private RenderTexture _ambientTexture;
private RenderTexture _prevAmbientTexture;
private RenderTexture _bluredLightTexture;
private RenderTexture _obstaclesUpsampledTexture;
private RenderTexture _lightSourcesTexture;
private RenderTexture _obstaclesTexture;
private RenderTexture _screenBlitTempTex;
private RenderTexture _normalMapBuffer;
private RenderTexture _singleLightSourceTexture;
private RenderTexture _renderTargetTexture;
private RenderTexture _oldActiveRenderTexture;
private Camera _camera;
private ObstacleCameraPostPorcessor _obstaclesPostProcessor;
private Point2 _extendedLightTextureSize;
private Point2 _smallLightTextureSize;
private Vector3 _oldPos;
private Vector3 _currPos;
private RenderTextureFormat _texFormat;
private int _aditionalAmbientLightCycles = 0;
private static LightingSystem _instance;
private Shader _normalMapRenderShader;
private Camera _normalMapCamera;
private List<LightSprite> _lightSpritesCache = new List<LightSprite>();
private Material _normalMappedLightMaterial;
private Material _lightCombiningMaterial;
private Material _alphaBlendedMaterial;
private bool _halfTexelOffest;
private Shader _lightBlockerReplacementShader;
#if LIGHT2D_2DTK
private tk2dCamera _tk2dCamera;
#endif
private float LightPixelsPerUnityMeter
{
get { return 1/LightPixelSize; }
}
public static LightingSystem Instance
{
get { return _instance != null ? _instance : (_instance = FindObjectOfType<LightingSystem>()); }
}
[ContextMenu("Create Camera")]
private void CreateCamera()
{
if (LightCamera == null)
{
var go = new GameObject("Ligt Camera",typeof(Camera));
go.transform.SetParent(transform,false);
LightCamera = go.GetComponent<Camera>();
}
}
private void OnEnable()
{
_instance = this;
_camera = GetComponent<Camera>();
}
private void Start()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Shader.SetGlobalTexture("_ObstacleTex", Texture2D.whiteTexture);
return;
}
#endif
if (LightCamera == null)
{
Debug.LogError(
"Lighting Camera in LightingSystem is null. Please, select Lighting Camera camera for lighting to work.");
enabled = false;
return;
}
if (LightOverlayMaterial == null)
{
Debug.LogError(
"LightOverlayMaterial in LightingSystem is null. Please, select LightOverlayMaterial camera for lighting to work.");
enabled = false;
return;
}
if (AffectOnlyThisCamera && _camera.targetTexture != null)
{
Debug.LogError("\"Affect Only This Camera\" will not work if camera.targetTexture is set.");
AffectOnlyThisCamera = false;
}
_camera = GetComponent<Camera>();
if (EnableNormalMapping && !_camera.orthographic)
{
Debug.LogError("Normal mapping is not supported with perspective camera.");
EnableNormalMapping = false;
}
if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf))
HDR = false;
_texFormat = HDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
var lightPixelsPerUnityMeter = LightPixelsPerUnityMeter;
_halfTexelOffest = SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 9");
InitTK2D();
if (_camera.orthographic)
{
var rawCamHeight = (_camera.orthographicSize + LightCameraSizeAdd)*2f;
var rawCamWidth = (_camera.orthographicSize*_camera.aspect + LightCameraSizeAdd)*2f;
_extendedLightTextureSize = new Point2(
Mathf.RoundToInt(rawCamWidth*lightPixelsPerUnityMeter),
Mathf.RoundToInt(rawCamHeight*lightPixelsPerUnityMeter));
var rawSmallCamHeight = _camera.orthographicSize*2f*lightPixelsPerUnityMeter;
_smallLightTextureSize = new Point2(
Mathf.RoundToInt(rawSmallCamHeight*_camera.aspect),
Mathf.RoundToInt(rawSmallCamHeight));
}
else
{
{
var lightCamHalfFov = (_camera.fieldOfView + LightCameraFovAdd)*Mathf.Deg2Rad/2f;
var lightCamSize = Mathf.Tan(lightCamHalfFov)*LightObstaclesDistance*2;
//var gameCamHalfFov = _camera.fieldOfView*Mathf.Deg2Rad/2f;
var texHeight = Mathf.RoundToInt(lightCamSize/LightPixelSize);
var texWidth = texHeight*_camera.aspect;
_extendedLightTextureSize = Point2.Round(new Vector2(texWidth, texHeight));
}
{
var lightCamHalfFov = _camera.fieldOfView*Mathf.Deg2Rad/2f;
var lightCamSize = Mathf.Tan(lightCamHalfFov)*LightObstaclesDistance*2;
//LightCamera.orthographicSize = lightCamSize/2f;
var gameCamHalfFov = _camera.fieldOfView*Mathf.Deg2Rad/2f;
var gameCamSize = Mathf.Tan(gameCamHalfFov)*LightObstaclesDistance*2;
_camera.orthographicSize = gameCamSize/2f;
var texHeight = Mathf.RoundToInt(lightCamSize/LightPixelSize);
var texWidth = texHeight*_camera.aspect;
_smallLightTextureSize = Point2.Round(new Vector2(texWidth, texHeight));
}
}
if (_extendedLightTextureSize.x%2 != 0)
_extendedLightTextureSize.x++;
if (_extendedLightTextureSize.y%2 != 0)
_extendedLightTextureSize.y++;
if (_extendedLightTextureSize.x > 1024 || _extendedLightTextureSize.y > 1024 ||
_smallLightTextureSize.x > 1024 || _smallLightTextureSize.y > 1024)
{
Debug.LogError("LightPixelSize is too small. Turning off lighting system.");
enabled = false;
return;
}
if (_extendedLightTextureSize.x < 4 || _extendedLightTextureSize.y < 4 ||
_smallLightTextureSize.x < 4 || _smallLightTextureSize.y < 4)
{
Debug.LogError("LightPixelSize is too big. Turning off lighting system.");
enabled = false;
return;
}
_screenBlitTempTex = new RenderTexture((int)_camera.pixelWidth, (int)_camera.pixelHeight, 0, _texFormat);
_screenBlitTempTex.filterMode = FilterMode.Point;
LightCamera.orthographic = _camera.orthographic;
if (EnableNormalMapping)
{
_lightSourcesTexture = new RenderTexture((int)_camera.pixelWidth, (int)_camera.pixelHeight,
0, _texFormat);
_lightSourcesTexture.filterMode = FilterMode.Point;
}
else
{
_lightSourcesTexture = new RenderTexture(_smallLightTextureSize.x, _smallLightTextureSize.y,
0, _texFormat);
_lightSourcesTexture.filterMode = LightTexturesFilterMode;
}
_obstaclesTexture = new RenderTexture(_extendedLightTextureSize.x, _extendedLightTextureSize.y,
0, _texFormat);
_ambientTexture = new RenderTexture(_extendedLightTextureSize.x, _extendedLightTextureSize.y,
0, _texFormat);
_ambientTexture.filterMode = LightTexturesFilterMode;
var upsampledObstacleSize = _extendedLightTextureSize * (LightObstaclesAntialiasing ? 2 : 1);
_obstaclesUpsampledTexture = new RenderTexture(
upsampledObstacleSize.x, upsampledObstacleSize.y, 0, _texFormat);
if (AffectOnlyThisCamera)
{
_renderTargetTexture = new RenderTexture((int)_camera.pixelWidth, (int)_camera.pixelHeight, 0, RenderTextureFormat.ARGB32);
_renderTargetTexture.filterMode = FilterMode.Point;
}
_obstaclesPostProcessor = new ObstacleCameraPostPorcessor();
_lightBlockerReplacementShader = Shader.Find(@"Light2D/Internal/LightBlockerReplacementShader");
LoopAmbientLight(100);
}
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
#if UNITY_EDITOR
if (!Application.isPlaying || Util.IsSceneViewFocused)
{
Shader.SetGlobalTexture("_ObstacleTex", Texture2D.whiteTexture);
if (dest != null)
dest.DiscardContents();
Graphics.Blit(src, dest);
return;
}
#endif
Update2DTK();
UpdateCamera();
RenderObstacles();
SetupShaders();
RenderNormalBuffer();
RenderLightSources();
RenderLightSourcesBlur();
RenderAmbientLight();
RenderLightOverlay(src, dest);
}
void OnPreRender()
{
if (Application.isPlaying && AffectOnlyThisCamera)
{
_oldActiveRenderTexture = RenderTexture.active;
RenderTexture.active = _renderTargetTexture;
GL.Clear(true, true, new Color(0, 0, 0, 0));
}
}
void OnPostRender()
{
if (Application.isPlaying && AffectOnlyThisCamera)
{
RenderTexture.active = _oldActiveRenderTexture;
//if (_alphaBlendedMaterial == null)
// _alphaBlendedMaterial = new Material(Shader.Find("Unlit/Transparent"));
//_alphaBlendedMaterial.SetTexture("_MainTex", _screenBlitTempTex);
//Graphics.Blit(_screenBlitTempTex, _renderTargetTexture, _alphaBlendedMaterial);
}
}
void InitTK2D()
{
#if LIGHT2D_2DTK
_tk2dCamera = GetComponent<tk2dCamera>();
if (_tk2dCamera != null && _tk2dCamera.CameraSettings.projection == tk2dCameraSettings.ProjectionType.Orthographic)
{
_camera.orthographic = true;
_camera.orthographicSize = _tk2dCamera.ScreenExtents.yMax;
}
#endif
}
void Update2DTK()
{
#if LIGHT2D_2DTK
if (_tk2dCamera != null && _tk2dCamera.CameraSettings.projection == tk2dCameraSettings.ProjectionType.Orthographic)
{
_camera.orthographic = true;
_camera.orthographicSize = _tk2dCamera.ScreenExtents.yMax;
}
#endif
}
private void LateUpdate()
{
#if UNITY_EDITOR
if (!Application.isPlaying && LightCamera != null)
{
_camera = GetComponent<Camera>();
if (_camera != null)
{
InitTK2D();
LightCamera.orthographic = _camera.orthographic;
if (_camera.orthographic)
{
LightCamera.orthographicSize = _camera.orthographicSize + LightCameraSizeAdd;
}
else
{
LightCamera.fieldOfView = _camera.fieldOfView + LightCameraFovAdd;
}
}
}
if (!Application.isPlaying || Util.IsSceneViewFocused)
{
Shader.SetGlobalTexture("_ObstacleTex", Texture2D.whiteTexture);
return;
}
#endif
}
private void RenderObstacles()
{
ConfigLightCamera(true);
var oldColor = LightCamera.backgroundColor;
var oldClearFlags = LightCamera.clearFlags;
LightCamera.enabled = false;
LightCamera.targetTexture = _obstaclesUpsampledTexture;
//main
LightCamera.clearFlags = CameraClearFlags.SolidColor;
LightCamera.cullingMask = 1 << LightObstaclesLayer;
LightCamera.backgroundColor = new Color(1, 1, 1, 0);
_obstaclesPostProcessor.DrawMesh(LightCamera, LightObstaclesAntialiasing ? 2 : 1);
LightCamera.Render();
//replacement
LightCamera.clearFlags = CameraClearFlags.Nothing;
LightCamera.cullingMask = LightObstaclesReplacementShaderLayer;
LightCamera.RenderWithShader(_lightBlockerReplacementShader, "RenderType");
//LightCamera.Render();
//LightCamera.ResetReplacementShader();
LightCamera.targetTexture = null;
LightCamera.cullingMask = 0;
LightCamera.backgroundColor = oldColor;
LightCamera.clearFlags = oldClearFlags;
_obstaclesTexture.DiscardContents();
Graphics.Blit(_obstaclesUpsampledTexture, _obstaclesTexture);
}
private void SetupShaders()
{
var lightPixelsPerUnityMeter = LightPixelsPerUnityMeter;
if (HDR) Shader.EnableKeyword("HDR");
else Shader.DisableKeyword("HDR");
if (_camera.orthographic) Shader.DisableKeyword("PERSPECTIVE_CAMERA");
else Shader.EnableKeyword("PERSPECTIVE_CAMERA");
Shader.SetGlobalTexture("_ObstacleTex", _obstaclesTexture);
Shader.SetGlobalFloat("_PixelsPerBlock", lightPixelsPerUnityMeter);
Shader.SetGlobalVector("_ExtendedToSmallTextureScale", new Vector2(
_smallLightTextureSize.x / (float)_extendedLightTextureSize.x,
_smallLightTextureSize.y / (float)_extendedLightTextureSize.y));
Shader.SetGlobalVector("_PosOffset", LightObstaclesAntialiasing
? (EnableNormalMapping ? _obstaclesUpsampledTexture.texelSize * 0.75f : _obstaclesUpsampledTexture.texelSize * 0.25f)
: (EnableNormalMapping ? _obstaclesTexture.texelSize : _obstaclesTexture.texelSize * 0.5f));
}
private void RenderNormalBuffer()
{
if (!EnableNormalMapping)
return;
if (_normalMapBuffer == null)
{
_normalMapBuffer = new RenderTexture(
(int) _camera.pixelWidth, (int) _camera.pixelHeight, 0, RenderTextureFormat.ARGB32);
_normalMapBuffer.filterMode = FilterMode.Point;
}
if (_normalMapRenderShader == null)
_normalMapRenderShader = Shader.Find("Light2D/Internal/Normal Map Drawer");
if (_normalMapCamera == null)
{
var camObj = new GameObject();
camObj.name = "Normals Camera";
camObj.transform.parent = _camera.transform;
camObj.transform.localScale = Vector3.one;
camObj.transform.localPosition = Vector3.zero;
camObj.transform.localRotation = Quaternion.identity;
_normalMapCamera = camObj.AddComponent<Camera>();
_normalMapCamera.enabled = false;
}
_normalMapBuffer.DiscardContents();
_normalMapCamera.CopyFrom(_camera);
_normalMapCamera.transform.position = LightCamera.transform.position;
_normalMapCamera.clearFlags = CameraClearFlags.SolidColor;
_normalMapCamera.targetTexture = _normalMapBuffer;
_normalMapCamera.cullingMask = int.MaxValue;
_normalMapCamera.backgroundColor = new Color(0.5f, 0.5f, 0, 1);
_normalMapCamera.RenderWithShader(_normalMapRenderShader, "LightObstacle");
Shader.SetGlobalTexture("_NormalsBuffer", _normalMapBuffer);
Shader.EnableKeyword("NORMAL_MAPPED_LIGHTS");
}
private void RenderLightSources()
{
ConfigLightCamera(false);
if (EnableNormalMapping)
{
if(_singleLightSourceTexture == null)
{
_singleLightSourceTexture = new RenderTexture(
_smallLightTextureSize.x, _smallLightTextureSize.y, 0, _texFormat);
_singleLightSourceTexture.filterMode = LightTexturesFilterMode;
}
if (_normalMappedLightMaterial == null)
{
_normalMappedLightMaterial = new Material(Shader.Find("Light2D/Internal/Normal Mapped Light"));
_normalMappedLightMaterial.SetTexture("_MainTex", _singleLightSourceTexture);
}
if (_lightCombiningMaterial == null)
{
_lightCombiningMaterial = new Material(Shader.Find("Light2D/Internal/Light Blender"));
_lightCombiningMaterial.SetTexture("_MainTex", _singleLightSourceTexture);
}
var cameraPlanes = GeometryUtility.CalculateFrustumPlanes(_camera);
_lightSourcesTexture.DiscardContents();
var oldBackgroundColor = LightCamera.backgroundColor;
var oldRt = RenderTexture.active;
Graphics.SetRenderTarget(_lightSourcesTexture);
GL.Clear(false, true, oldBackgroundColor);
Graphics.SetRenderTarget(oldRt);
_lightSpritesCache.Clear();
foreach (var lightSprite in LightSprite.AllLightSprites)
{
if (lightSprite.RendererEnabled &&
GeometryUtility.TestPlanesAABB(cameraPlanes, lightSprite.Renderer.bounds))
{
_lightSpritesCache.Add(lightSprite);
}
}
var lightCamLocPos = LightCamera.transform.localPosition;
LightCamera.targetTexture = _singleLightSourceTexture;
LightCamera.cullingMask = 0;
LightCamera.backgroundColor = new Color(0, 0, 0, 0);
foreach (var lightSprite in _lightSpritesCache)
{
// HACK: won't work for unknown reason without that line
LightCamera.RenderWithShader(_normalMapRenderShader, "f84j");
Graphics.SetRenderTarget(_singleLightSourceTexture);
lightSprite.DrawLightingNow(lightCamLocPos);
Graphics.SetRenderTarget(_lightSourcesTexture);
lightSprite.DrawLightNormalsNow(_normalMappedLightMaterial);
}
Graphics.SetRenderTarget(oldRt);
LightCamera.cullingMask = 1 << LightSourcesLayer;
LightCamera.Render();
Graphics.Blit(_singleLightSourceTexture, _lightSourcesTexture, _lightCombiningMaterial);
LightCamera.targetTexture = null;
LightCamera.cullingMask = 0;
LightCamera.backgroundColor = oldBackgroundColor;
}
else
{
LightCamera.targetTexture = _lightSourcesTexture;
LightCamera.cullingMask = 1 << LightSourcesLayer;
//LightCamera.backgroundColor = new Color(0, 0, 0, 0);
LightCamera.Render();
LightCamera.targetTexture = null;
LightCamera.cullingMask = 0;
}
}
private void RenderLightSourcesBlur()
{
if (BlurLightSources && LightSourcesBlurMaterial != null)
{
UnityEngine.Profiling.Profiler.BeginSample("LightingSystem.OnRenderImage Bluring Light Sources");
if (_bluredLightTexture == null)
{
var w = _lightSourcesTexture.width == _smallLightTextureSize.x
? _lightSourcesTexture.width*2
: _lightSourcesTexture.width;
var h = _lightSourcesTexture.height == _smallLightTextureSize.y
? _lightSourcesTexture.height*2
: _lightSourcesTexture.height;
_bluredLightTexture = new RenderTexture(w, h, 0, _texFormat);
}
_bluredLightTexture.DiscardContents();
_lightSourcesTexture.filterMode = FilterMode.Bilinear;
LightSourcesBlurMaterial.mainTexture = _lightSourcesTexture;
Graphics.Blit(null, _bluredLightTexture, LightSourcesBlurMaterial);
if (LightTexturesFilterMode == FilterMode.Point)
{
_lightSourcesTexture.filterMode = FilterMode.Point;
_lightSourcesTexture.DiscardContents();
Graphics.Blit(_bluredLightTexture, _lightSourcesTexture);
}
UnityEngine.Profiling.Profiler.EndSample();
}
}
private void RenderAmbientLight()
{
if (!EnableAmbientLight || AmbientLightComputeMaterial == null)
return;
UnityEngine.Profiling.Profiler.BeginSample("LightingSystem.OnRenderImage Ambient Light");
ConfigLightCamera(true);
if (_ambientTexture == null)
{
_ambientTexture =
new RenderTexture(_extendedLightTextureSize.x, _extendedLightTextureSize.y, 0, _texFormat);
}
if (_prevAmbientTexture == null)
{
_prevAmbientTexture =
new RenderTexture(_extendedLightTextureSize.x, _extendedLightTextureSize.y, 0, _texFormat);
}
if (_ambientEmissionTexture == null)
{
_ambientEmissionTexture =
new RenderTexture(_extendedLightTextureSize.x, _extendedLightTextureSize.y, 0, _texFormat);
}
if (EnableAmbientLight)
{
var oldBackgroundColor = LightCamera.backgroundColor;
LightCamera.targetTexture = _ambientEmissionTexture;
LightCamera.cullingMask = 1 << AmbientLightLayer;
LightCamera.backgroundColor = new Color(0, 0, 0, 0);
LightCamera.Render();
LightCamera.targetTexture = null;
LightCamera.cullingMask = 0;
LightCamera.backgroundColor = oldBackgroundColor;
}
for (int i = 0; i < _aditionalAmbientLightCycles + 1; i++)
{
var tmp = _prevAmbientTexture;
_prevAmbientTexture = _ambientTexture;
_ambientTexture = tmp;
var texSize = new Vector2(_ambientTexture.width, _ambientTexture.height);
var posShift = ((Vector2) (_currPos - _oldPos)/LightPixelSize).Div(texSize);
_oldPos = _currPos;
AmbientLightComputeMaterial.SetTexture("_LightSourcesTex", _ambientEmissionTexture);
AmbientLightComputeMaterial.SetTexture("_MainTex", _prevAmbientTexture);
AmbientLightComputeMaterial.SetVector("_Shift", posShift);
_ambientTexture.DiscardContents();
Graphics.Blit(null, _ambientTexture, AmbientLightComputeMaterial);
if (BlurAmbientLight && AmbientLightBlurMaterial != null)
{
UnityEngine.Profiling.Profiler.BeginSample("LightingSystem.OnRenderImage Bluring Ambient Light");
_prevAmbientTexture.DiscardContents();
AmbientLightBlurMaterial.mainTexture = _ambientTexture;
Graphics.Blit(null, _prevAmbientTexture, AmbientLightBlurMaterial);
var tmpblur = _prevAmbientTexture;
_prevAmbientTexture = _ambientTexture;
_ambientTexture = tmpblur;
UnityEngine.Profiling.Profiler.EndSample();
}
}
_aditionalAmbientLightCycles = 0;
UnityEngine.Profiling.Profiler.EndSample();
}
private void RenderLightOverlay(RenderTexture src, RenderTexture dest)
{
UnityEngine.Profiling.Profiler.BeginSample("LightingSystem.OnRenderImage Light Overlay");
ConfigLightCamera(false);
Vector2 lightTexelSize = new Vector2(1f / _smallLightTextureSize.x, 1f / _smallLightTextureSize.y);
float lightPixelsPerUnityMeter = LightPixelsPerUnityMeter;
Vector2 worldOffset = Quaternion.Inverse(_camera.transform.rotation)*(LightCamera.transform.position - _camera.transform.position);
Vector2 offset = Vector2.Scale(lightTexelSize, -worldOffset*lightPixelsPerUnityMeter);
var lightSourcesTex = BlurLightSources && LightSourcesBlurMaterial != null && LightTexturesFilterMode != FilterMode.Point
? _bluredLightTexture
: _lightSourcesTexture;
float xDiff = _camera.aspect/LightCamera.aspect;
if (!_camera.orthographic)
{
var gameCamHalfFov = _camera.fieldOfView * Mathf.Deg2Rad / 2f;
var gameCamSize = Mathf.Tan(gameCamHalfFov) * LightObstaclesDistance * 2;
_camera.orthographicSize = gameCamSize / 2f;
}
float scaleY = _camera.orthographicSize/LightCamera.orthographicSize;
var scale = new Vector2(scaleY*xDiff, scaleY);
var oldAmbientFilterMode = _ambientTexture == null ? FilterMode.Point : _ambientTexture.filterMode;
LightOverlayMaterial.SetTexture("_AmbientLightTex", EnableAmbientLight ? _ambientTexture : null);
LightOverlayMaterial.SetTexture("_LightSourcesTex", lightSourcesTex);
LightOverlayMaterial.SetTexture("_GameTex", src);
LightOverlayMaterial.SetVector("_Offset", offset);
LightOverlayMaterial.SetVector("_Scale", scale);
if (_screenBlitTempTex == null || _screenBlitTempTex.width != src.width ||
_screenBlitTempTex.height != src.height)
{
if (_screenBlitTempTex != null)
_screenBlitTempTex.Release();
_screenBlitTempTex = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.ARGB32);
_screenBlitTempTex.filterMode = FilterMode.Point;
}
_screenBlitTempTex.DiscardContents();
Graphics.Blit(null, _screenBlitTempTex, LightOverlayMaterial);
if (_ambientTexture != null)
_ambientTexture.filterMode = oldAmbientFilterMode;
if (!AffectOnlyThisCamera)
Graphics.Blit(_screenBlitTempTex, dest);
UnityEngine.Profiling.Profiler.EndSample();
}
private void UpdateCamera()
{
LightPixelSize = _camera.orthographicSize*2f/_smallLightTextureSize.y;
var lightPixelsPerUnityMeter = LightPixelsPerUnityMeter;
var mainPos = _camera.transform.position;
var camRot = _camera.transform.rotation;
var unrotMainPos = Quaternion.Inverse(camRot) * mainPos;
var gridPos = new Vector2(
Mathf.Round(unrotMainPos.x * lightPixelsPerUnityMeter) / lightPixelsPerUnityMeter,
Mathf.Round(unrotMainPos.y * lightPixelsPerUnityMeter) / lightPixelsPerUnityMeter);
Vector2 posDiff = gridPos - (Vector2)unrotMainPos;
var pos = camRot*posDiff + mainPos;
LightCamera.transform.position = pos;
_currPos = pos;
}
public void LoopAmbientLight(int cycles)
{
_aditionalAmbientLightCycles += cycles;
}
void ConfigLightCamera(bool extended)
{
if (extended)
{
LightCamera.orthographicSize =
_camera.orthographicSize*(_extendedLightTextureSize.y/(float)_smallLightTextureSize.y);// _extendedLightTextureSize.y/(2f*LightPixelsPerUnityMeter);
LightCamera.fieldOfView = _camera.fieldOfView + LightCameraFovAdd;
LightCamera.aspect = _extendedLightTextureSize.x/(float) _extendedLightTextureSize.y;
}
else
{
LightCamera.orthographicSize = _camera.orthographicSize;// _smallLightTextureSize.y / (2f * LightPixelsPerUnityMeter);
LightCamera.fieldOfView = _camera.fieldOfView;
LightCamera.aspect = _smallLightTextureSize.x / (float)_smallLightTextureSize.y;
}
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2d9b6871d3cdbba478825c4b041a7f90
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Some configuration for LightingSystem. Containd in lighting system prefab, destroyed after ininial setup.
/// </summary>
public class LightingSystemPrefabConfig : MonoBehaviour
{
public Material AmbientLightComputeMaterial;
public Material LightOverlayMaterial;
public Material BlurMaterial;
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6a84973fb2c2f0449bd5d1b1fbb5b86d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,94 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// This class apply post processing effect to light obstacles texture.
/// It is drawing one pixel wide white border on light obstacles texture.
/// Whithout it light sources with off screen origin may not work.
/// </summary>
public class ObstacleCameraPostPorcessor
{
private Mesh _mesh;
private Material _material;
private Point2 _oldCameraSize;
private List<Color32> _colors32 = new List<Color32>();
private List<Vector3> _vertices = new List<Vector3>();
private List<int> _indices = new List<int>();
public ObstacleCameraPostPorcessor()
{
if (_material == null)
{
_material = new Material(Shader.Find("Light2D/Obstacle Texture Post Porcessor"));
}
}
public void DrawMesh(Camera camera, float pixelWidth)
{
var camSize = new Point2(Mathf.RoundToInt(camera.pixelWidth), Mathf.RoundToInt(camera.pixelHeight));
if (_oldCameraSize != camSize || _mesh == null)
{
_oldCameraSize = camSize;
CreateMesh(camera, pixelWidth);
}
Graphics.DrawMesh(_mesh, camera.transform.position, camera.transform.rotation, _material,
LightingSystem.Instance.LightObstaclesLayer, camera);
}
/// <summary>
/// Generating mesh with one pixel wide white border.
/// </summary>
private void CreateMesh(Camera camera, float pixelWidth)
{
var pixelSize = new Vector2(1f/camera.pixelWidth, 1f/camera.pixelHeight)*pixelWidth;
_vertices.Clear();
_colors32.Clear();
_indices.Clear();
CreateQuad(new Color32(0, 0, 0, 0), pixelSize, Vector2.one - pixelSize); // central
CreateQuad(Color.white, Vector2.zero, new Vector2(pixelSize.x, 1)); // left
CreateQuad(Color.white, new Vector2(1 - pixelSize.x, 0), Vector2.one); // right
CreateQuad(Color.white, Vector2.zero, new Vector2(1, pixelSize.y)); // bottom
CreateQuad(Color.white, new Vector2(0, 1 - pixelSize.y), Vector2.one); // top
if (_mesh == null)
_mesh = new Mesh();
_mesh.Clear();
_mesh.vertices = _vertices.ToArray();
_mesh.triangles = _indices.ToArray();
_mesh.colors32 = _colors32.ToArray();
}
private void CreateQuad(Color32 color, Vector2 min, Vector2 max)
{
min = min*2 - Vector2.one;
max = max*2 - Vector2.one;
int startVertex = _vertices.Count;
_indices.Add(0 + startVertex);
_indices.Add(1 + startVertex);
_indices.Add(3 + startVertex);
_indices.Add(3 + startVertex);
_indices.Add(1 + startVertex);
_indices.Add(2 + startVertex);
_vertices.Add(new Vector3(min.x, min.y, 1));
_vertices.Add(new Vector3(min.x, max.y, 1));
_vertices.Add(new Vector3(max.x, max.y, 1));
_vertices.Add(new Vector3(max.x, min.y, 1));
for (int i = 0; i < 4; i++)
_colors32.Add(color);
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dc802f1bc103f85408e82da1905c7e32
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Light2D
{
/// <summary>
/// Class is almost same as Vector2, but using int data type instead of float.
/// </summary>
[Serializable]
public struct Point2 : IEquatable<Point2>
{
public int x, y;
public Point2(int x, int y)
{
this.x = x;
this.y = y;
}
public bool Equals(Point2 other)
{
return x == other.x && y == other.y;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is Point2 && Equals((Point2)obj);
}
public override int GetHashCode()
{
unchecked
{
return (x * 397) ^ y;
}
}
public static bool operator ==(Point2 left, Point2 right)
{
return left.Equals(right);
}
public static bool operator !=(Point2 left, Point2 right)
{
return !left.Equals(right);
}
public static implicit operator Vector2(Point2 p)
{
return new Vector2(p.x, p.y);
}
public static implicit operator Vector3(Point2 p)
{
return new Vector2(p.x, p.y);
}
public static Point2 Floor(Vector2 v)
{
return new Point2((int)v.x, (int)v.y);
}
public static Point2 Round(Vector2 v)
{
return new Point2(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y));
}
public static Point2 Floor(float x, float y)
{
return new Point2((int)x, (int)y);
}
public static Point2 Round(float x, float y)
{
return new Point2(Mathf.RoundToInt(x), Mathf.RoundToInt(y));
}
public static Point2 operator +(Point2 first, Point2 second)
{
return new Point2(first.x + second.x, first.y + second.y);
}
public static Point2 operator -(Point2 first, Point2 second)
{
return new Point2(first.x - second.x, first.y - second.y);
}
public static Vector2 operator +(Point2 first, Vector2 second)
{
return new Vector2(first.x + second.x, first.y + second.y);
}
public static Vector2 operator -(Point2 first, Vector2 second)
{
return new Vector2(first.x - second.x, first.y - second.y);
}
public static Point2 operator *(Point2 p, int mul)
{
return new Point2(p.x * mul, p.y * mul);
}
public static Point2 operator /(Point2 p, int div)
{
return new Point2(p.x / div, p.y / div);
}
public static Vector2 operator *(Point2 p, float mul)
{
return new Vector2(p.x * mul, p.y * mul);
}
public static Vector2 operator /(Point2 p, float div)
{
return new Vector2(p.x / div, p.y / div);
}
public static Point2 one
{
get { return new Point2(1, 1); }
}
public static Point2 zero
{
get { return new Point2(0, 0); }
}
public override string ToString()
{
return "(" + x + ", " + y + ")";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 569a4c0c227854345b2c9db3728899bc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,633 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace Light2D
{
/// <summary>
/// Bunch of utility functions that could be userful sometimes.
/// </summary>
public static class Util
{
#if UNITY_METRO && !UNITY_EDITOR
static Windows.Devices.Input.TouchCapabilities touchCaps = new Windows.Devices.Input.TouchCapabilities();
#endif
public static bool isTouchscreen
{
get
{
return
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER
false;
#elif UNITY_METRO
touchCaps.TouchPresent != 0;
#else
true;
#endif
}
}
public static void SafeIterateBackward<T>(this IList<T> list, Action<T> action)
{
for (int i = list.Count - 1; i >= 0; i--)
{
action(list[i]);
}
}
public static void SafeIterateBackward<T>(this IList<T> list, Action<T, int> action)
{
for (int i = list.Count - 1; i >= 0; i--)
{
if (i >= list.Count) continue;
action(list[i], i);
}
}
public static void SafeIterateBackward<T>(this IEnumerable<T> enumerable, Action<T> action)
{
var list = enumerable.ToArray();
for (int i = list.Length - 1; i >= 0; i--)
{
action(list[i]);
}
}
public static void SafeIterateBackward<T>(this IEnumerable<T> enumerable, Action<T, int> action)
{
var list = enumerable.ToArray();
for (int i = list.Length - 1; i >= 0; i--)
{
action(list[i], i);
}
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var obj in enumerable)
{
action(obj);
yield return obj;
}
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
{
var i = 0;
foreach (var obj in enumerable)
{
action(obj, i);
yield return obj;
i++;
}
}
public static IEnumerable<T> GetComponentsInChildRecursive<T>(this GameObject root) where T : Component
{
return root.GetChildRecursive().SelectMany(go => go.GetComponents<T>());
}
public static IEnumerable<GameObject> GetChildRecursive(this GameObject root)
{
foreach (Transform child in root.transform)
{
var cgo = child.gameObject;
yield return cgo;
foreach (var gameObject in cgo.GetChildRecursive())
yield return gameObject;
}
}
public static Rigidbody2D GetRigidbodyUnderCursor()
{
var mousePos = GetMousePosInUnits();
var click = Physics2D.OverlapPoint(mousePos);
return click != null ? click.attachedRigidbody : null;
}
public static Vector2 GetMousePosInUnits()
{
var mouse = Input.mousePosition;
var camera = Camera.main;
var mouseWorld = camera.ScreenToWorldPoint(
new Vector3(mouse.x, mouse.y, -camera.transform.position.z));
return mouseWorld;
}
public static Vector2 ScreenToWorld(Vector2 screen)
{
var camera = Camera.main;
var mouseWorld = camera.ScreenToWorldPoint(
new Vector3(screen.x, screen.y, -camera.transform.position.z));
return mouseWorld;
}
public static Vector2 WorldToScreen(Vector2 screen)
{
var camera = Camera.main;
var mouseWorld = camera.WorldToScreenPoint(
new Vector3(screen.x, screen.y, -camera.transform.position.z));
return mouseWorld;
}
public static GameObject Instantiate(GameObject prefab)
{
return (GameObject) Object.Instantiate(prefab);
}
public static T Instantiate<T>(GameObject prefab) where T : Component
{
return (Instantiate(prefab)).GetComponent<T>();
}
public static T Instantiate<T>(GameObject prefab, Vector3 position, Quaternion rotation)
where T : Component
{
return ((GameObject) Object.Instantiate(prefab, position, rotation)).GetComponent<T>();
}
public static float ClampAngle(float angle)
{
angle = Mathf.Repeat(angle, 360);
if (angle > 180) angle -= 360;
return angle;
}
public static float AngleZ(this Vector2 angle)
{
if (angle == Vector2.zero) return 0;
return Vector2.Angle(Vector2.up, angle)*Mathf.Sign(-angle.x);
}
public static float AngleZ(this Vector3 angle)
{
if (angle == Vector3.zero) return 0;
return Vector2.Angle(Vector2.up, angle)*Mathf.Sign(-angle.x);
}
public static float Proj(Vector2 vector, Vector2 onNormal)
{
return Vector2.Dot(vector, onNormal)*onNormal.magnitude;
}
public static float Cross(Vector2 lhs, Vector2 rhs)
{
return lhs.x*rhs.y - lhs.y*rhs.x;
}
public static void Destroy(UnityEngine.Object obj)
{
if (obj == null) return;
if (obj is GameObject)
((GameObject) obj).transform.parent = null;
#if UNITY_EDITOR
if (!Application.isPlaying)
GameObject.DestroyImmediate(obj);
else GameObject.Destroy(obj);
#else
GameObject.Destroy(obj);
#endif
}
public static T RandomElement<T>(this IList<T> coll)
{
var index = Random.Range(0, coll.Count);
return coll[index];
}
public static T RandomElement<T>(this T[] coll)
{
var index = Random.Range(0, coll.Length);
return coll[index];
}
public static Vector2 RotateZ(this Vector2 v, float angle)
{
float sin = Mathf.Sin(angle*Mathf.Deg2Rad);
float cos = Mathf.Cos(angle*Mathf.Deg2Rad);
float tx = v.x;
float ty = v.y;
return new Vector2((cos*tx) - (sin*ty), (cos*ty) + (sin*tx));
}
public static Vector3 RotateZ(this Vector3 v, float angle)
{
float sin = Mathf.Sin(angle);
float cos = Mathf.Cos(angle);
float tx = v.x;
float ty = v.y;
return new Vector3((cos*tx) - (sin*ty), (cos*ty) + (sin*tx), v.z);
}
public static Vector2 Rotate90(this Vector2 v)
{
return new Vector2(-v.y, v.x);
}
public static void Log(params object[] vals)
{
var sb = new StringBuilder();
for (int i = 0; i < vals.Length; i++)
{
if (i != 0) sb.Append(", ");
sb.Append(vals[i]);
}
Debug.Log(sb.ToString());
}
public static void Log(UnityEngine.Object context, params object[] vals)
{
var sb = new StringBuilder();
for (int i = 0; i < vals.Length; i++)
{
if (i != 0) sb.Append(", ");
sb.Append(vals[i]);
}
Debug.Log(sb.ToString(), context);
}
public static void LogArray<T>(IEnumerable<T> enumerable)
{
var sb = new StringBuilder();
var vals = enumerable.ToArray();
for (int i = 0; i < vals.Length; i++)
{
sb.Append(i);
sb.Append(": ");
sb.Append(vals[i]);
sb.AppendLine(";");
}
Debug.Log(sb.ToString());
}
public static Color Set(this Color color, int channel, float value)
{
color[channel] = value;
return color;
}
public static Color WithAlpha(this Color color, float value)
{
color.a = value;
return color;
}
public static Vector3 WithX(this Vector3 vec, float value)
{
vec.x = value;
return vec;
}
public static Vector3 WithXY(this Vector3 vec, Vector2 xy)
{
vec.x = xy.x;
vec.y = xy.y;
return vec;
}
public static Vector3 WithXY(this Vector3 vec, float x, float y)
{
vec.x = x;
vec.y = y;
return vec;
}
public static Vector3 WithY(this Vector3 vec, float value)
{
vec.y = value;
return vec;
}
public static Vector3 WithZ(this Vector3 vec, float value)
{
vec.z = value;
return vec;
}
#if !UNITY_WINRT
public static void Serialize<T>(string path, T obj) where T : class
{
using (var stream = File.Create(path))
{
var serializer = new XmlSerializer(typeof (T));
var xmlWriter = new XmlTextWriter(stream, Encoding.UTF8);
serializer.Serialize(xmlWriter, obj);
}
}
public static byte[] Serialize<T>(T obj)
{
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof (T));
var xmlWriter = new XmlTextWriter(stream, Encoding.UTF8);
serializer.Serialize(xmlWriter, obj);
return stream.ToArray();
}
}
public static T Deserialize<T>(string path) where T : class
{
using (var stream = File.OpenRead(path))
{
var serializer = new XmlSerializer(typeof (T));
var fromFile = serializer.Deserialize(stream) as T;
return fromFile;
}
}
public static T Deserialize<T>(byte[] data)
{
try
{
using (var stream = new MemoryStream(data))
{
var serializer = new XmlSerializer(typeof (T));
var fromFile = (T) serializer.Deserialize(stream);
return fromFile;
}
}
catch (Exception ex)
{
Debug.LogError(ex);
return default(T);
}
}
#endif
public static int IndexOfMin<T>(this List<T> list, Func<T, float> pred)
{
int minId = -1;
float minVal = float.MaxValue;
for (int i = 0; i < list.Count; i++)
{
var obj = list[i];
var val = pred(obj);
if (val < minVal)
{
minId = i;
minVal = val;
}
}
return minId;
}
public static T MinBy<T>(this IEnumerable<T> list, Func<T, float> pred)
{
T minObj = default(T);
float minVal = float.MaxValue;
bool isEmpty = true;
foreach (var obj in list)
{
var val = pred(obj);
if (val < minVal)
{
minObj = obj;
minVal = val;
}
isEmpty = false;
}
if (isEmpty) throw new ArgumentException();
return minObj;
}
public static T MinByOrDefault<T>(this IEnumerable<T> list, Func<T, float> pred)
{
T minObj = default(T);
float minVal = float.MaxValue;
bool isEmpty = true;
foreach (var obj in list)
{
var val = pred(obj);
if (val < minVal)
{
minObj = obj;
minVal = val;
}
isEmpty = false;
}
if (isEmpty) return default (T);
return minObj;
}
public static Vector2 NearestPointOnLine(this Vector2 c, Vector2 a, Vector2 b)
{
var v = (a - b).normalized;
return b + v*Vector2.Dot(v, c - b);
}
public static float DistToLine(this Vector2 c, Vector2 a, Vector2 b)
{
var n = new Vector2(b.y - a.y, a.x - b.x).normalized;
var v = c - a;
return Vector2.Dot(n, v);
}
public static bool GetTouchByFingerId(int fingerId, out Touch resultTouch)
{
if (fingerId == -1)
{
resultTouch = new Touch();
return false;
}
for (int i = 0; i < Input.touchCount; i++)
{
var touch = Input.GetTouch(i);
if (touch.fingerId == fingerId)
{
resultTouch = touch;
return true;
}
}
resultTouch = new Touch();
return false;
}
public static float ClampAngle(float angle, float min, float max)
{
angle = Mathf.Repeat(angle, 360);
min = Mathf.Repeat(min, 360);
max = Mathf.Repeat(max, 360);
if (min > max)
{
if (angle > min || angle < max) return angle;
return angle > (min + max)/2f ? min : max;
}
if (angle > min && angle < max) return angle;
return angle < min ? min : max;
}
public static int Hash<T>(T v1, T v2, T v3, T v4)
{
int hash = 23;
hash = hash*31 + v1.GetHashCode();
hash = hash*31 + v2.GetHashCode();
hash = hash*31 + v3.GetHashCode();
hash = hash*31 + v4.GetHashCode();
return hash;
}
public static int Hash<T>(T v1, T v2, T v3)
{
int hash = 23;
hash = hash*31 + v1.GetHashCode();
hash = hash*31 + v2.GetHashCode();
hash = hash*31 + v3.GetHashCode();
return hash;
}
public static int Hash<T>(T v1, T v2)
{
int hash = 23;
hash = hash*31 + v1.GetHashCode();
hash = hash*31 + v2.GetHashCode();
return hash;
}
public static int Hash<T>(params T[] els)
{
int hash = 23;
for (int i = 0; i < els.Length; i++)
hash = hash*31 + els[i].GetHashCode();
return hash;
}
public static Vector4 Div(this Vector4 vec, Vector4 div)
{
return new Vector4(vec.x/div.x, vec.y/div.y, vec.z/div.z, vec.w/div.w);
}
public static Vector3 Div(this Vector3 vec, Vector3 div)
{
return new Vector3(vec.x/div.x, vec.y/div.y, vec.z/div.z);
}
public static Vector2 Div(this Vector2 vec, Vector2 div)
{
return new Vector2(vec.x/div.x, vec.y/div.y);
}
public static Vector4 Mul(this Vector4 v1, Vector4 v2)
{
return Vector4.Scale(v1, v2);
}
public static Vector3 Mul(this Vector3 v1, Vector3 v2)
{
return Vector3.Scale(v1, v2);
}
public static Vector2 Mul(this Vector2 v1, Vector2 v2)
{
return Vector2.Scale(v1, v2);
}
public static float DecodeFloatRGBA(Vector3 enc)
{
enc = new Vector3((byte) (enc.x*254f), (byte) (enc.y*254f), (byte) (enc.z*254f))/255f;
var kDecodeDot = new Vector4(1f, 1/255f, 1/65025f);
var result = Vector3.Dot(enc, kDecodeDot);
return result;
}
public static Vector4 EncodeFloatRGBA(float v)
{
var kEncodeMul = new Vector3(1.0f, 255.0f, 65025.0f);
var enc = kEncodeMul*v;
enc = new Vector3(
enc.x - Mathf.Floor(enc.x), enc.y - Mathf.Floor(enc.y),
enc.z - Mathf.Floor(enc.z));
return enc;
}
#if UNITY_EDITOR
public static bool IsSceneViewFocused
{
get
{
return SceneView.currentDrawingSceneView != null &&
SceneView.currentDrawingSceneView == EditorWindow.focusedWindow;
}
}
#endif
public static bool FastEquals(this Matrix4x4 m1, Matrix4x4 m2)
{
return m1.m00 == m2.m00 && m1.m01 == m2.m01 && m1.m02 == m2.m02 && m1.m03 == m2.m03 &&
m1.m10 == m2.m10 && m1.m11 == m2.m11 && m1.m12 == m2.m12 && m1.m13 == m2.m13 &&
m1.m20 == m2.m20 && m1.m21 == m2.m21 && m1.m22 == m2.m22 && m1.m23 == m2.m23 &&
m1.m30 == m2.m30 && m1.m31 == m2.m31 && m1.m32 == m2.m32 && m1.m33 == m2.m33;
}
public static bool Equals(this Color32 col1, Color32 col2)
{
return col1.r == col2.r && col1.g == col2.g && col1.b == col2.b && col1.a == col2.a;
}
}
internal class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> distinct;
private Func<T, int> hash;
public GenericEqualityComparer(Func<T, T, bool> distinct, Func<T, int> hash)
{
this.distinct = distinct;
this.hash = hash;
}
public bool Equals(T x, T y)
{
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
if (System.Object.ReferenceEquals(x, null) ||
System.Object.ReferenceEquals(y, null))
{
return false;
}
return distinct(x, y);
}
public int GetHashCode(T obj)
{
return hash(obj);
}
}
internal class GenericComparer<T> : IComparer<T>
{
private Func<T, T, int> comparer;
public GenericComparer(Func<T, T, int> comparer)
{
this.comparer = comparer;
}
public int Compare(T x, T y)
{
return comparer(x, y);
}
}
public class ReadOnlyAttribute : PropertyAttribute
{
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5cec7f403e884314d99b1cd248d10843
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData: