using UnityEngine; using UnityEngine.UI; using TMPro; using LightGlue.Unity.Game; using LightGlue.Unity.Networking; namespace LightGlue.Unity.Roma.UI { /// /// Roma 场景结果显示 UI(兼容旧 LightGlueManager / 新 RomaManager)。 /// - 优先绑定 LightGlueManager(旧场景) /// - 若不存在则绑定 RomaManager(新场景) /// public sealed class RomaResultUIController : MonoBehaviour { [Header("结果来源(可留空自动查找)")] public LightGlueManager lightGlueManager; public RomaManager romaManager; [Header("UI显示组件(可选)")] [Tooltip("综合状态文本(推荐绑定一个 Text 或 TMP_Text 之一)")] public Text statusText; public TMP_Text statusTmpText; [Tooltip("结果有效性状态文本(显示Valid/Invalid)")] public Text isValidText; [Tooltip("匹配点数量文本")] public Text numMatchesText; [Tooltip("内点比例文本")] public Text inliersRatioText; [Tooltip("相机位置X坐标文本")] public Text cameraXText; [Tooltip("相机位置Y坐标文本")] public Text cameraYText; [Header("显示设置")] [Range(1, 60)] public int updateRate = 10; public bool showInvalidResults = true; public string invalidResultText = "等待有效结果..."; [Header("格式")] [Tooltip("是否按 RomaBridge 日志格式输出到 statusText/statusTmpText")] public bool useRomaBridgeLogFormat = true; private float _updateInterval; private float _lastUpdateTime; private LightGlueResult _latestResult; private bool _hasResult; private enum SourceType { None, LightGlueManager, RomaManager } private SourceType _source; private void Start() { _updateInterval = 1.0f / Mathf.Max(1, updateRate); _lastUpdateTime = Time.time; BindSource(); } private void BindSource() { if (lightGlueManager == null) lightGlueManager = LightGlueManager.Instance; if (lightGlueManager != null) { _source = SourceType.LightGlueManager; lightGlueManager.OnResultUpdated += OnResultUpdated; if (lightGlueManager.HasLatestResult) OnResultUpdated(lightGlueManager.LatestResult); return; } if (romaManager == null) romaManager = RomaManager.Instance; if (romaManager != null) { _source = SourceType.RomaManager; romaManager.OnResultUpdated += OnResultUpdated; if (romaManager.HasLatestResult) OnResultUpdated(romaManager.LatestResult); return; } _source = SourceType.None; Debug.LogError("[RomaResultUI] 未找到 LightGlueManager / RomaManager,无法获取算法结果。"); } private void Update() { if (Time.time - _lastUpdateTime < _updateInterval) return; _lastUpdateTime = Time.time; if (!_hasResult) { if (!showInvalidResults) ClearUI(); return; } UpdateUI(_latestResult); } private void UpdateUI(LightGlueResult result) { if (isValidText != null) { if (result.IsValid) { isValidText.text = "状态: 有效"; isValidText.color = Color.green; } else { isValidText.text = "状态: 无效"; isValidText.color = Color.red; } } if (numMatchesText != null) numMatchesText.text = $"匹配点: {result.NumMatches}"; if (inliersRatioText != null) inliersRatioText.text = $"内点比例: {result.InliersRatio:P1}"; if (cameraXText != null) cameraXText.text = result.IsValid ? $"相机X: {result.CameraPosition.x:F1}" : "相机X: --"; if (cameraYText != null) cameraYText.text = result.IsValid ? $"相机Y: {result.CameraPosition.y:F1}" : "相机Y: --"; string msg = BuildStatusText(result); if (statusText != null) { statusText.text = msg; statusText.color = result.IsValid ? Color.white : Color.yellow; } if (statusTmpText != null) { statusTmpText.text = msg; statusTmpText.color = result.IsValid ? Color.white : Color.yellow; } } private string BuildStatusText(LightGlueResult result) { if (!result.IsValid && showInvalidResults && !useRomaBridgeLogFormat) return $"无效 | 匹配:{result.NumMatches} | {invalidResultText}"; if (!useRomaBridgeLogFormat) { if (result.IsValid) return $"有效 | 匹配:{result.NumMatches} | 内点:{result.InliersRatio:P1} | 位置:({result.CameraPosition.x:F1}, {result.CameraPosition.y:F1})"; return invalidResultText; } // 对齐你在 Console 里看到的格式:[RomaBridge] Result: valid=0 matches=500 inliers=0.000 x=0.0 y=0.0 string prefix = _source == SourceType.LightGlueManager ? "[LightGlue]" : "[RomaBridge]"; int valid = result.IsValid ? 1 : 0; return $"{prefix} Result: valid={valid} matches={result.NumMatches} inliers={result.InliersRatio:F3} x={result.CameraPosition.x:F1} y={result.CameraPosition.y:F1}"; } private void ClearUI() { if (isValidText != null) isValidText.text = "状态: --"; if (numMatchesText != null) numMatchesText.text = "匹配点: --"; if (inliersRatioText != null) inliersRatioText.text = "内点比例: --"; if (cameraXText != null) cameraXText.text = "相机X: --"; if (cameraYText != null) cameraYText.text = "相机Y: --"; if (statusText != null) { statusText.text = invalidResultText; statusText.color = Color.gray; } if (statusTmpText != null) { statusTmpText.text = invalidResultText; statusTmpText.color = Color.gray; } } private void OnResultUpdated(LightGlueResult result) { _latestResult = result; _hasResult = true; } private void OnDestroy() { if (lightGlueManager != null) lightGlueManager.OnResultUpdated -= OnResultUpdated; if (romaManager != null) romaManager.OnResultUpdated -= OnResultUpdated; } } }