2025-06-07 17:43:34 +08:00

881 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using UnityEngine;
using FairyGUI.Utils;
using Object = UnityEngine.Object;
namespace FairyGUI
{
/// <summary>
///
/// </summary>
public class NGraphics : IMeshFactory, IBatchable
{
/// <summary>
///
/// </summary>
public GameObject gameObject { get; private set; }
/// <summary>
///
/// </summary>
public MeshFilter meshFilter { get; private set; }
/// <summary>
///
/// </summary>
public MeshRenderer meshRenderer { get; private set; }
/// <summary>
///
/// </summary>
public Mesh mesh { get; private set; }
/// <summary>
///
/// </summary>
public BlendMode blendMode;
/// <summary>
/// 不参与剪裁
/// </summary>
public bool dontClip;
/// <summary>
/// 当Mesh更新时触发
/// </summary>
public event Action meshModifier;
/// <summary>
///
/// </summary>
public List<NGraphics> subInstances;
/// <summary>
///
/// </summary>
public Vector4 userData;
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<byte> _alphaBackup; //透明度改变需要通过修改顶点颜色实现,但顶点颜色本身可能就带有透明度,所以这里要有一个备份
internal int _maskFlag;
StencilEraser _stencilEraser;
MaterialPropertyBlock _propertyBlock;
bool _blockUpdated;
internal BatchElement _batchElement;
/// <summary>
///
/// </summary>
/// <param name="gameObject"></param>
public NGraphics(GameObject gameObject)
{
this.gameObject = gameObject;
_alpha = 1f;
_shader = ShaderConfig.imageShader;
_color = Color.white;
_meshFactory = this;
meshFilter = gameObject.AddComponent<MeshFilter>();
meshRenderer = gameObject.AddComponent<MeshRenderer>();
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++;
}
/// <summary>
///
/// </summary>
public IMeshFactory meshFactory
{
get { return _meshFactory; }
set
{
if (_meshFactory != value)
{
_meshFactory = value;
_meshDirty = true;
}
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T GetMeshFactory<T>() where T : IMeshFactory, new()
{
if (!(_meshFactory is T))
{
_meshFactory = new T();
_meshDirty = true;
}
return (T)_meshFactory;
}
/// <summary>
///
/// </summary>
public Rect contentRect
{
get { return _contentRect; }
set
{
_contentRect = value;
_meshDirty = true;
if (subInstances != null)
{
foreach (var sub in subInstances)
sub.contentRect = value;
}
}
}
/// <summary>
///
/// </summary>
public FlipType flip
{
get { return _flip; }
set
{
if (_flip != value)
{
_flip = value;
_meshDirty = true;
}
}
}
/// <summary>
///
/// </summary>
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();
}
}
}
/// <summary>
///
/// </summary>
public string shader
{
get { return _shader; }
set
{
_shader = value;
UpdateManager();
}
}
/// <summary>
///
/// </summary>
/// <param name="shader"></param>
/// <param name="texture"></param>
public void SetShaderAndTexture(string shader, NTexture texture)
{
_shader = shader;
if (_texture != texture)
this.texture = texture;
else
UpdateManager();
}
/// <summary>
///
/// </summary>
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;
}
}
}
/// <summary>
/// Same as material property except that ownership is transferred to this object.
/// </summary>
/// <param name="material"></param>
public void SetMaterial(Material material)
{
this.material = material;
_customMatarial |= 128;
}
/// <summary>
///
/// </summary>
public string[] materialKeywords
{
get { return _shaderKeywords; }
set
{
_shaderKeywords = value;
UpdateMaterialFlags();
}
}
/// <summary>
///
/// </summary>
/// <param name="keyword"></param>
/// <param name="enabled"></param>
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;
}
/// <summary>
///
/// </summary>
public bool enabled
{
get { return meshRenderer.enabled; }
set { meshRenderer.enabled = value; }
}
/// <summary>
///
/// </summary>
[Obsolete("Use renderingOrder")]
public int sortingOrder
{
get { return meshRenderer.sortingOrder; }
set { meshRenderer.sortingOrder = value; }
}
/// <summary>
///
/// </summary>
public int renderingOrder
{
get { return meshRenderer.sortingOrder; }
set { meshRenderer.sortingOrder = value; }
}
public void SetRenderingOrder(UpdateContext context, bool inBatch)
{
meshRenderer.sortingOrder = context.renderingOrder++;
if (subInstances != null && !inBatch)
{
foreach (var sub in subInstances)
{
sub.meshRenderer.sortingOrder = context.renderingOrder++;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
internal void _SetStencilEraserOrder(int value)
{
_stencilEraser.meshRenderer.sortingOrder = value;
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
public Color color
{
get { return _color; }
set { _color = value; }
}
/// <summary>
///
/// </summary>
public void Tint()
{
if (_meshDirty)
return;
int vertCount = mesh.vertexCount;
if (vertCount == 0)
return;
VertexBuffer vb = VertexBuffer.Begin();
mesh.GetColors(vb.colors);
List<Color32> colors = vb.colors;
for (int i = 0; i < vertCount; i++)
{
Color32 col = _color;
col.a = (byte)(_alpha * (hasAlphaBackup ? _alphaBackup[i] : (byte)255));
colors[i] = col;
}
mesh.SetColors(vb.colors);
vb.End();
}
void ChangeAlpha(float value)
{
_alpha = value;
int vertCount = mesh.vertexCount;
if (vertCount == 0)
return;
VertexBuffer vb = VertexBuffer.Begin();
mesh.GetColors(vb.colors);
List<Color32> colors = vb.colors;
for (int i = 0; i < vertCount; i++)
{
Color32 col = colors[i];
col.a = (byte)(_alpha * (hasAlphaBackup ? _alphaBackup[i] : (byte)255));
colors[i] = col;
}
mesh.SetColors(vb.colors);
vb.End();
}
/// <summary>
///
/// </summary>
public VertexMatrix vertexMatrix
{
get { return _vertexMatrix; }
set
{
_vertexMatrix = value;
_meshDirty = true;
if (subInstances != null)
{
foreach (var sub in subInstances)
sub._vertexMatrix = value;
}
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public MaterialPropertyBlock materialPropertyBlock
{
get
{
if (_propertyBlock == null)
_propertyBlock = new MaterialPropertyBlock();
_blockUpdated = true;
return _propertyBlock;
}
}
/// <summary>
///
/// </summary>
public void SetMeshDirty()
{
_meshDirty = true;
if (subInstances != null)
{
foreach (var g in subInstances)
g._meshDirty = true;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public bool UpdateMesh()
{
bool ret = false;
if (_meshDirty)
{
UpdateMeshNow();
ret = true;
}
if (subInstances != null)
{
foreach (var g in subInstances)
{
if (g.UpdateMesh())
ret = true;
}
}
return ret;
}
/// <summary>
///
/// </summary>
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;
if (subInstances != null)
{
foreach (var sub in subInstances)
sub.Dispose();
subInstances.Clear();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="alpha"></param>
/// <param name="grayed"></param>
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;
}
}
if (subInstances != null)
{
foreach (var sub in subInstances)
sub.Update(context, alpha, grayed);
}
}
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<byte>();
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();
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);
vb.End();
if (meshModifier != null)
meshModifier();
}
public void OnPopulateMesh(VertexBuffer vb)
{
Rect rect = texture.GetDrawRect(vb.contentRect, flip);
vb.AddQuad(rect, vb.vertexColor, vb.uvRect);
vb.AddTriangles();
vb._isArbitraryQuad = _vertexMatrix != null;
}
public NGraphics CreateSubInstance(string name)
{
if (subInstances == null)
subInstances = new List<NGraphics>();
GameObject newGameObject = new GameObject(name);
newGameObject.transform.SetParent(gameObject.transform, false);
newGameObject.layer = gameObject.layer;
newGameObject.hideFlags = gameObject.hideFlags;
var newGraphics = new NGraphics(newGameObject);
newGraphics._vertexMatrix = _vertexMatrix;
return newGraphics;
}
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<MeshFilter>();
meshRenderer = gameObject.AddComponent<MeshRenderer>();
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; }
}
}
}
}