using System; using System.Collections.Generic; using UnityEngine; using FairyGUI.Utils; using Object = UnityEngine.Object; namespace FairyGUI { /// /// /// public class NGraphics : IMeshFactory { /// /// /// public GameObject gameObject { get; private set; } /// /// /// public MeshFilter meshFilter { get; private set; } /// /// /// public MeshRenderer meshRenderer { get; private set; } /// /// /// public Mesh mesh { get; private set; } /// /// /// public BlendMode blendMode; /// /// 不参与剪裁 /// public bool dontClip; /// /// 当Mesh更新时触发 /// public event Action meshModifier; NTexture _texture; string _shader; Material _material; int _customMatarial; //0-none, 1-common, 2-support internal mask, 128-owns material MaterialManager _manager; string[] _shaderKeywords; int _materialFlags; IMeshFactory _meshFactory; float _alpha; Color _color; bool _meshDirty; Rect _contentRect; FlipType _flip; public class VertexMatrix { public Vector3 cameraPos; public Matrix4x4 matrix; } VertexMatrix _vertexMatrix; bool hasAlphaBackup; List _alphaBackup; //透明度改变需要通过修改顶点颜色实现,但顶点颜色本身可能就带有透明度,所以这里要有一个备份 internal int _maskFlag; StencilEraser _stencilEraser; #if !UNITY_5_6_OR_NEWER Color32[] _colors; #endif MaterialPropertyBlock _propertyBlock; bool _blockUpdated; /// /// /// /// public NGraphics(GameObject gameObject) { this.gameObject = gameObject; _alpha = 1f; _shader = ShaderConfig.imageShader; _color = Color.white; _meshFactory = this; meshFilter = gameObject.AddComponent(); meshRenderer = gameObject.AddComponent(); meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; meshRenderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; meshRenderer.receiveShadows = false; mesh = new Mesh(); mesh.name = gameObject.name; mesh.MarkDynamic(); meshFilter.mesh = mesh; meshFilter.hideFlags = DisplayObject.hideFlags; meshRenderer.hideFlags = DisplayObject.hideFlags; mesh.hideFlags = DisplayObject.hideFlags; Stats.LatestGraphicsCreation++; } /// /// /// public IMeshFactory meshFactory { get { return _meshFactory; } set { if (_meshFactory != value) { _meshFactory = value; _meshDirty = true; } } } /// /// /// /// /// public T GetMeshFactory() where T : IMeshFactory, new() { if (!(_meshFactory is T)) { _meshFactory = new T(); _meshDirty = true; } return (T)_meshFactory; } /// /// /// public Rect contentRect { get { return _contentRect; } set { _contentRect = value; _meshDirty = true; } } /// /// /// public FlipType flip { get { return _flip; } set { if (_flip != value) { _flip = value; _meshDirty = true; } } } /// /// /// public NTexture texture { get { return _texture; } set { if (_texture != value) { if (value != null) value.AddRef(); if (_texture != null) _texture.ReleaseRef(); _texture = value; if (_customMatarial != 0 && _material != null) _material.mainTexture = _texture != null ? _texture.nativeTexture : null; _meshDirty = true; UpdateManager(); } } } /// /// /// public string shader { get { return _shader; } set { _shader = value; UpdateManager(); } } /// /// /// /// /// public void SetShaderAndTexture(string shader, NTexture texture) { _shader = shader; if (_texture != texture) this.texture = texture; else UpdateManager(); } /// /// /// public Material material { get { if (_customMatarial == 0 && _material == null && _manager != null) _material = _manager.GetMaterial(_materialFlags, blendMode, 0); return _material; } set { if ((_customMatarial & 128) != 0 && _material != null) Object.DestroyImmediate(_material); _material = value; if (_material != null) { _customMatarial = 1; if (_material.HasProperty(ShaderConfig.ID_Stencil) || _material.HasProperty(ShaderConfig.ID_ClipBox)) _customMatarial |= 2; meshRenderer.sharedMaterial = _material; if (_texture != null) _material.mainTexture = _texture.nativeTexture; } else { _customMatarial = 0; meshRenderer.sharedMaterial = null; } } } /// /// Same as material property except that ownership is transferred to this object. /// /// public void SetMaterial(Material material) { this.material = material; _customMatarial |= 128; } /// /// /// public string[] materialKeywords { get { return _shaderKeywords; } set { _shaderKeywords = value; UpdateMaterialFlags(); } } /// /// /// /// /// public void ToggleKeyword(string keyword, bool enabled) { if (enabled) { if (_shaderKeywords == null) { _shaderKeywords = new string[] { keyword }; UpdateMaterialFlags(); } else if (Array.IndexOf(_shaderKeywords, keyword) == -1) { Array.Resize(ref _shaderKeywords, _shaderKeywords.Length + 1); _shaderKeywords[_shaderKeywords.Length - 1] = keyword; UpdateMaterialFlags(); } } else { if (_shaderKeywords != null) { int i = Array.IndexOf(_shaderKeywords, keyword); if (i != -1) { _shaderKeywords[i] = null; UpdateMaterialFlags(); } } } } void UpdateManager() { if (_texture != null) _manager = _texture.GetMaterialManager(_shader); else _manager = null; UpdateMaterialFlags(); } void UpdateMaterialFlags() { if (_customMatarial != 0) { if (material != null) material.shaderKeywords = _shaderKeywords; } else if (_shaderKeywords != null && _manager != null) _materialFlags = _manager.GetFlagsByKeywords(_shaderKeywords); else _materialFlags = 0; } /// /// /// public bool enabled { get { return meshRenderer.enabled; } set { meshRenderer.enabled = value; } } /// /// /// public int sortingOrder { get { return meshRenderer.sortingOrder; } set { meshRenderer.sortingOrder = value; } } /// /// /// /// internal void _SetStencilEraserOrder(int value) { _stencilEraser.meshRenderer.sortingOrder = value; } /// /// /// /// public Color color { get { return _color; } set { _color = value; } } /// /// /// public void Tint() { if (_meshDirty) return; int vertCount = mesh.vertexCount; if (vertCount == 0) return; #if !UNITY_5_6_OR_NEWER Color32[] colors = _colors; if (colors == null) colors = mesh.colors32; #else VertexBuffer vb = VertexBuffer.Begin(); mesh.GetColors(vb.colors); List colors = vb.colors; #endif for (int i = 0; i < vertCount; i++) { Color32 col = _color; col.a = (byte)(_alpha * (hasAlphaBackup ? _alphaBackup[i] : (byte)255)); colors[i] = col; } #if !UNITY_5_6_OR_NEWER mesh.colors32 = colors; #else mesh.SetColors(vb.colors); vb.End(); #endif } void ChangeAlpha(float value) { _alpha = value; int vertCount = mesh.vertexCount; if (vertCount == 0) return; #if !UNITY_5_6_OR_NEWER Color32[] colors = _colors; if (colors == null) colors = mesh.colors32; #else VertexBuffer vb = VertexBuffer.Begin(); mesh.GetColors(vb.colors); List colors = vb.colors; #endif for (int i = 0; i < vertCount; i++) { Color32 col = colors[i]; col.a = (byte)(_alpha * (hasAlphaBackup ? _alphaBackup[i] : (byte)255)); colors[i] = col; } #if !UNITY_5_6_OR_NEWER mesh.colors32 = colors; #else mesh.SetColors(vb.colors); vb.End(); #endif } /// /// /// public VertexMatrix vertexMatrix { get { return _vertexMatrix; } set { _vertexMatrix = value; _meshDirty = true; } } /// /// /// /// public MaterialPropertyBlock materialPropertyBlock { get { if (_propertyBlock == null) _propertyBlock = new MaterialPropertyBlock(); _blockUpdated = true; return _propertyBlock; } } /// /// /// public void SetMeshDirty() { _meshDirty = true; } /// /// /// /// public bool UpdateMesh() { if (_meshDirty) { UpdateMeshNow(); return true; } else return false; } /// /// /// public void Dispose() { if (mesh != null) { if (Application.isPlaying) Object.Destroy(mesh); else Object.DestroyImmediate(mesh); mesh = null; } if ((_customMatarial & 128) != 0 && _material != null) Object.DestroyImmediate(_material); if (_texture != null) { _texture.ReleaseRef(); _texture = null; } _manager = null; _material = null; meshRenderer = null; meshFilter = null; _stencilEraser = null; meshModifier = null; } /// /// /// /// /// /// public void Update(UpdateContext context, float alpha, bool grayed) { Stats.GraphicsCount++; if (_meshDirty) { _alpha = alpha; UpdateMeshNow(); } else if (_alpha != alpha) ChangeAlpha(alpha); if (_propertyBlock != null && _blockUpdated) { meshRenderer.SetPropertyBlock(_propertyBlock); _blockUpdated = false; } if (_customMatarial != 0) { if ((_customMatarial & 2) != 0 && _material != null) context.ApplyClippingProperties(_material, false); } else { if (_manager != null) { if (_maskFlag == 1) { _material = _manager.GetMaterial((int)MaterialFlags.AlphaMask | _materialFlags, BlendMode.Normal, context.clipInfo.clipId); context.ApplyAlphaMaskProperties(_material, false); } else { int matFlags = _materialFlags; if (grayed) matFlags |= (int)MaterialFlags.Grayed; if (context.clipped) { if (context.stencilReferenceValue > 0) matFlags |= (int)MaterialFlags.StencilTest; if (context.rectMaskDepth > 0) { if (context.clipInfo.soft) matFlags |= (int)MaterialFlags.SoftClipped; else matFlags |= (int)MaterialFlags.Clipped; } _material = _manager.GetMaterial(matFlags, blendMode, context.clipInfo.clipId); if (_manager.firstMaterialInFrame) context.ApplyClippingProperties(_material, true); } else _material = _manager.GetMaterial(matFlags, blendMode, 0); } } else _material = null; if (!Material.ReferenceEquals(_material, meshRenderer.sharedMaterial)) meshRenderer.sharedMaterial = _material; } if (_maskFlag != 0) { if (_maskFlag == 1) _maskFlag = 2; else { if (_stencilEraser != null) _stencilEraser.enabled = false; _maskFlag = 0; } } } internal void _PreUpdateMask(UpdateContext context, uint maskId) { //_maskFlag: 0-new mask, 1-active mask, 2-mask complete if (_maskFlag == 0) { if (_stencilEraser == null) { _stencilEraser = new StencilEraser(gameObject.transform); _stencilEraser.meshFilter.mesh = mesh; } else _stencilEraser.enabled = true; } _maskFlag = 1; if (_manager != null) { //这里使用maskId而不是clipInfo.clipId,是因为遮罩有两个用途,一个是写入遮罩,一个是擦除,两个不能用同一个材质 Material mat = _manager.GetMaterial((int)MaterialFlags.AlphaMask | _materialFlags, BlendMode.Normal, maskId); if (!Material.ReferenceEquals(mat, _stencilEraser.meshRenderer.sharedMaterial)) _stencilEraser.meshRenderer.sharedMaterial = mat; context.ApplyAlphaMaskProperties(mat, true); } } void UpdateMeshNow() { _meshDirty = false; if (_texture == null || _meshFactory == null) { if (mesh.vertexCount > 0) { mesh.Clear(); if (meshModifier != null) meshModifier(); } return; } VertexBuffer vb = VertexBuffer.Begin(); vb.contentRect = _contentRect; vb.uvRect = _texture.uvRect; if (_texture != null) vb.textureSize = new Vector2(_texture.width, _texture.height); else vb.textureSize = new Vector2(0, 0); if (_flip != FlipType.None) { if (_flip == FlipType.Horizontal || _flip == FlipType.Both) { float tmp = vb.uvRect.xMin; vb.uvRect.xMin = vb.uvRect.xMax; vb.uvRect.xMax = tmp; } if (_flip == FlipType.Vertical || _flip == FlipType.Both) { float tmp = vb.uvRect.yMin; vb.uvRect.yMin = vb.uvRect.yMax; vb.uvRect.yMax = tmp; } } vb.vertexColor = _color; _meshFactory.OnPopulateMesh(vb); int vertCount = vb.currentVertCount; if (vertCount == 0) { if (mesh.vertexCount > 0) { mesh.Clear(); if (meshModifier != null) meshModifier(); } vb.End(); return; } if (_texture.rotated) { float xMin = _texture.uvRect.xMin; float yMin = _texture.uvRect.yMin; float yMax = _texture.uvRect.yMax; float tmp; for (int i = 0; i < vertCount; i++) { Vector2 vec = vb.uvs[i]; tmp = vec.y; vec.y = yMin + vec.x - xMin; vec.x = xMin + yMax - tmp; vb.uvs[i] = vec; } } hasAlphaBackup = vb._alphaInVertexColor; if (hasAlphaBackup) { if (_alphaBackup == null) _alphaBackup = new List(); else _alphaBackup.Clear(); for (int i = 0; i < vertCount; i++) { Color32 col = vb.colors[i]; _alphaBackup.Add(col.a); col.a = (byte)(col.a * _alpha); vb.colors[i] = col; } } else if (_alpha != 1) { for (int i = 0; i < vertCount; i++) { Color32 col = vb.colors[i]; col.a = (byte)(col.a * _alpha); vb.colors[i] = col; } } if (_vertexMatrix != null) { Vector3 camPos = _vertexMatrix.cameraPos; Vector3 center = new Vector3(camPos.x, camPos.y, 0); center -= _vertexMatrix.matrix.MultiplyPoint(center); for (int i = 0; i < vertCount; i++) { Vector3 pt = vb.vertices[i]; pt = _vertexMatrix.matrix.MultiplyPoint(pt); pt += center; Vector3 vec = pt - camPos; float lambda = -camPos.z / vec.z; pt.x = camPos.x + lambda * vec.x; pt.y = camPos.y + lambda * vec.y; pt.z = 0; vb.vertices[i] = pt; } } mesh.Clear(); #if UNITY_5_2 || UNITY_5_3_OR_NEWER mesh.SetVertices(vb.vertices); if (vb._isArbitraryQuad) mesh.SetUVs(0, vb.FixUVForArbitraryQuad()); else mesh.SetUVs(0, vb.uvs); mesh.SetColors(vb.colors); mesh.SetTriangles(vb.triangles, 0); if (vb.uvs2.Count == vb.uvs.Count) mesh.SetUVs(1, vb.uvs2); #if !UNITY_5_6_OR_NEWER _colors = null; #endif #else Vector3[] vertices = new Vector3[vertCount]; Vector2[] uv = new Vector2[vertCount]; _colors = new Color32[vertCount]; int[] triangles = new int[vb.triangles.Count]; vb.vertices.CopyTo(vertices); vb.uvs.CopyTo(uv); vb.colors.CopyTo(_colors); vb.triangles.CopyTo(triangles); mesh.vertices = vertices; mesh.uv = uv; mesh.triangles = triangles; mesh.colors32 = _colors; if(vb.uvs2.Count==uv.Length) { uv = new Vector2[vertCount]; vb.uvs2.CopyTo(uv); mesh.uv2 = uv; } #endif vb.End(); if (meshModifier != null) meshModifier(); } public void OnPopulateMesh(VertexBuffer vb) { Rect rect = texture.GetDrawRect(vb.contentRect); vb.AddQuad(rect, vb.vertexColor, vb.uvRect); vb.AddTriangles(); vb._isArbitraryQuad = _vertexMatrix != null; } class StencilEraser { public GameObject gameObject; public MeshFilter meshFilter; public MeshRenderer meshRenderer; public StencilEraser(Transform parent) { gameObject = new GameObject("StencilEraser"); gameObject.transform.SetParent(parent, false); meshFilter = gameObject.AddComponent(); meshRenderer = gameObject.AddComponent(); meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; meshRenderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; meshRenderer.receiveShadows = false; gameObject.layer = parent.gameObject.layer; gameObject.hideFlags = parent.gameObject.hideFlags; meshFilter.hideFlags = parent.gameObject.hideFlags; meshRenderer.hideFlags = parent.gameObject.hideFlags; } public bool enabled { get { return meshRenderer.enabled; } set { meshRenderer.enabled = value; } } } } }