using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace XericLibrary.Runtime.UIGraph
{
///
/// 图元缓存渲染器基类
/// 管理 PrimitiveCacheEntry 列表 + 脏标记范围更新
/// LateUpdate 中只重建脏标记范围内的图元
///
[RequireComponent(typeof(CanvasRenderer))]
public abstract class PrimitiveCacheRendererBase : MaskableGraphic
{
#region 默认材质
private static Material s_DefaultMaterial;
private const string DefaultShaderPath = "XericLibrary/UIGraph/UIPattern";
///
/// 获取图元渲染器的默认材质(基于 UIPattern.shader)
///
public override Material defaultMaterial
{
get
{
if (s_DefaultMaterial == null)
{
var shader = Shader.Find(DefaultShaderPath);
if (shader != null)
s_DefaultMaterial = new Material(shader);
else
s_DefaultMaterial = base.defaultMaterial;
}
return s_DefaultMaterial;
}
}
#endregion
#region 数据缓存
///
/// 图元列表
///
protected List m_Primitives = new List();
///
/// 模型顶点缓存
///
protected List m_MeshVertexCache = new List();
///
/// 三角形索引缓存
///
protected List m_MeshIndexCache = new List();
///
/// 脏标记范围起点(图元索引),-1 表示无脏标记
///
protected int m_DirtyStartIndex = -1;
///
/// 脏标记范围终点(图元索引)
///
protected int m_DirtyEndIndex = -1;
#endregion
#region API
///
/// 添加一个图元
///
/// 图元索引
public int AddPrimitive(PrimitiveCacheEntry entry)
{
int index = m_Primitives.Count;
m_Primitives.Add(entry);
MarkDirty(index);
return index;
}
///
/// 移除指定图元
///
public void RemovePrimitive(int index)
{
if (index < 0 || index >= m_Primitives.Count) return;
m_Primitives.RemoveAt(index);
RebuildAll();
}
///
/// 标记指定图元为脏
///
public void SetPrimitiveDirty(int index)
{
MarkDirty(index);
}
///
/// 清空所有图元
///
public void ClearAll()
{
m_Primitives.Clear();
m_MeshVertexCache.Clear();
m_MeshIndexCache.Clear();
m_DirtyStartIndex = -1;
m_DirtyEndIndex = -1;
SetVerticesDirty();
}
#endregion
#region 脏标记
protected void MarkDirty(int index)
{
if (m_DirtyStartIndex < 0 || index < m_DirtyStartIndex)
m_DirtyStartIndex = index;
if (m_DirtyEndIndex < 0 || index > m_DirtyEndIndex)
m_DirtyEndIndex = index;
}
///
/// 标记所有图元为脏(下次 LateUpdate 全量重绘)
/// 适用于:更新顶点坐标后,不改变元素数量的重绘
///
public void RebuildAll()
{
m_DirtyStartIndex = 0;
m_DirtyEndIndex = m_Primitives.Count - 1;
if (m_DirtyEndIndex < 0)
m_DirtyStartIndex = -1;
}
#endregion
#region LateUpdate
protected virtual void LateUpdate()
{
if (m_DirtyStartIndex < 0) return;
// 全量重建策略:从脏范围起点开始,重绘该条目及后续所有条目
// 保证所有条目 startVertexIndex/startIndexIndex 始终正确
int start = m_DirtyStartIndex;
// 统计脏范围前的顶点/索引数(这些条目无需重建)
int preVertexCount = 0;
int preIndexCount = 0;
for (int i = 0; i < start; i++)
{
preVertexCount += m_Primitives[i].vertexCount;
preIndexCount += m_Primitives[i].triangleCount * 3;
}
// 从 start 开始重建所有条目
var tempVerts = new List();
var tempIndices = new List();
int vAccum = preVertexCount;
int iAccum = preIndexCount;
for (int i = start; i < m_Primitives.Count; i++)
{
var entry = m_Primitives[i];
int vertBefore = tempVerts.Count;
int idxBefore = tempIndices.Count;
PrimitiveMeshGenerator.GeneratePrimitive(entry, tempVerts, tempIndices);
var updated = entry;
updated.startVertexIndex = vAccum;
updated.vertexCount = tempVerts.Count - vertBefore;
updated.startIndexIndex = iAccum;
updated.triangleCount = (tempIndices.Count - idxBefore) / 3;
m_Primitives[i] = updated;
vAccum += updated.vertexCount;
iAccum += updated.triangleCount * 3;
}
// 合并到主缓存
int totalVertices = vAccum;
int totalIndices = iAccum;
while (m_MeshVertexCache.Count < totalVertices)
m_MeshVertexCache.Add(new UIVertex());
while (m_MeshIndexCache.Count < totalIndices)
m_MeshIndexCache.Add(0);
for (int i = 0; i < tempVerts.Count; i++)
m_MeshVertexCache[preVertexCount + i] = tempVerts[i];
for (int i = 0; i < tempIndices.Count; i++)
m_MeshIndexCache[preIndexCount + i] = tempIndices[i] + preVertexCount;
SetVerticesDirty();
m_DirtyStartIndex = -1;
m_DirtyEndIndex = -1;
}
#endregion
#region OnPopulateMesh
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if (m_MeshVertexCache.Count == 0 || m_MeshIndexCache.Count == 0) return;
vh.AddUIVertexStream(m_MeshVertexCache, m_MeshIndexCache);
}
#endregion
#region 生命周期
protected override void OnEnable()
{
base.OnEnable();
RebuildAll();
}
protected override void OnDestroy()
{
base.OnDestroy();
}
#endregion
}
}