Initial Commit
This commit is contained in:
commit
ee5c2f922d
2255 changed files with 547750 additions and 0 deletions
449
Assets/Plugins/Light2D/Scripts/CustomSprite.cs
Normal file
449
Assets/Plugins/Light2D/Scripts/CustomSprite.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue