Files
XericLibrary-WebGL-Publish/Runtime/Blueprint/Render/WireCanvasRenderTool.cs
T
2026-07-27 08:34:20 +08:00

136 lines
4.3 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using XericLibrary.Runtime.UIGraph;
namespace XericLibrary.Runtime.Blueprint.Render
{
/// <summary>
/// 连线渲染工具 — 按网格区块管理 BlueprintCurveRenderer,支持 Viewport Transform。
/// <para>连线跟随其源节点(SourcePort.OwnerNode)进入对应 Chunk 的 CurveRenderer。</para>
/// </summary>
[BlueprintTool(phase: ToolPhase.Render, order: 110)]
[BlueprintTheme("QuickGraph")]
public class WireCanvasRenderTool : BlueprintCanvasRenderTool<BlueprintCurveRenderer>
{
private QuickGraphWireRenderConfig _defaultConfig;
private QuickGraphWireRenderConfig Config
{
get
{
if (ToolConfig is QuickGraphWireRenderConfig external) return external;
if (_defaultConfig == null)
_defaultConfig = ScriptableObject.CreateInstance<QuickGraphWireRenderConfig>();
return _defaultConfig;
}
}
// ====================================================================
// 抽象实现
// ====================================================================
protected override BlueprintCurveRenderer BuildChunkRenderer(GameObject chunkGo)
{
return chunkGo.AddComponent<BlueprintCurveRenderer>();
}
protected override void AppendElementData(BlueprintCurveRenderer renderer, IBlueprintElement element)
{
if (!(element is BlueprintWire wire)) return;
if (wire.SourcePort == null || wire.TargetPort == null) return;
if (wire.SourcePort.OwnerNode == null || wire.TargetPort.OwnerNode == null) return;
var cfg = Config;
// 端点:画布空间坐标(由 Viewport Transform 统一变换)
Vector2 startPos = wire.SourcePort.GetWorldPosition();
Vector2 endPos = wire.TargetPort.GetWorldPosition();
// 手柄方向 = 端口法向
float srcDirX = Mathf.Sign(wire.SourcePort.RelativePosition.x);
float tgtDirX = Mathf.Sign(wire.TargetPort.RelativePosition.x);
if (srcDirX == tgtDirX) srcDirX *= -1f;
// 手柄长度(原始值,由 Viewport 统一缩放)
Vector2 handle1 = startPos + new Vector2(srcDirX * cfg.SourceHandleLength, 0f);
Vector2 handle2 = endPos + new Vector2(tgtDirX * cfg.TargetHandleLength, 0f);
float ww = cfg.WireWidth;
var ctrlPts = new Vector3[]
{
new Vector3(startPos.x, startPos.y, ww),
new Vector3(handle1.x, handle1.y, ww),
new Vector3(handle2.x, handle2.y, ww),
new Vector3(endPos.x, endPos.y, ww),
};
Color32 srcColor = wire.SourcePort.OwnerNode.NodeColor;
Color32 tgtColor = wire.TargetPort.OwnerNode.NodeColor;
var arrow = new ArrowHeadData
{
shape = cfg.ArrowShape,
reversed = cfg.ArrowReversed,
progress = cfg.ArrowProgress,
width = cfg.ArrowWidth,
height = cfg.ArrowHeight,
depthCompensation = cfg.ArrowDepthCompensation,
color = tgtColor,
};
int count = ctrlPts.Length;
var ctrlPts2 = new Vector2[count];
var widths = new float[count];
for (int i = 0; i < count; i++)
{
ctrlPts2[i] = ctrlPts[i];
widths[i] = ctrlPts[i].z;
}
var curveEntry = new CurveCacheEntry
{
startColor = srcColor,
endColor = tgtColor,
tessellationSegments = cfg.TessellationSegments,
};
renderer.AddCurve(curveEntry, ctrlPts2, widths, new[] { arrow });
}
protected override void ClearChunkData(BlueprintCurveRenderer renderer)
{
renderer.ClearAll();
}
protected override void SetChunkDirty(BlueprintCurveRenderer renderer)
{
renderer.RebuildAll();
}
protected override Vector2 GetElementPosition(IBlueprintElement element)
{
if (!(element is BlueprintWire wire)) return Vector2.zero;
// 以源节点的位置作为 Chunk 分组依据
return wire.SourcePort?.OwnerNode?.Position ?? Vector2.zero;
}
protected override bool IsElementCulled(IBlueprintElement element)
{
if (!(element is BlueprintWire wire)) return true;
if (wire.SourcePort?.OwnerNode == null || wire.TargetPort?.OwnerNode == null)
return true;
var viewport = Graph?.Canvas?.GetViewportRect() ?? new Rect();
float margin = Config.WireWidth * 20f;
viewport.xMin -= margin;
viewport.xMax += margin;
viewport.yMin -= margin;
viewport.yMax += margin;
// 两端都不在视口内则跳过
return !viewport.Contains(wire.SourcePort.OwnerNode.Position)
&& !viewport.Contains(wire.TargetPort.OwnerNode.Position);
}
}
}