using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.UI; namespace InteractiveVideo { /// /// Debug drawing for validating coordinate alignment. Place it as a child of the video RawImage, stretched to /// the same rect. When VideoTrackingManager.ShowTrackingDebug is on it draws, for every object in the current /// resolved frame: bounding box, polygon outline, polygon vertices, and (via IMGUI) the track id, name, action, /// plus the current video frame / source tracked frame in the corner. Not a raycast target. /// [RequireComponent(typeof(CanvasRenderer))] [AddComponentMenu("Interactive Video/Tracking Debug Overlay")] public sealed class TrackingDebugOverlay : MaskableGraphic { [SerializeField] private InteractiveVideoPlayer player; [SerializeField] private VideoTrackingManager trackingManager; [Header("Style")] [SerializeField] private float lineWidth = 2f; [SerializeField] private float vertexSize = 6f; [SerializeField] private Color bboxColor = new Color(0f, 1f, 1f, 0.8f); [SerializeField] private Color polygonColor = new Color(0f, 1f, 0f, 0.9f); [SerializeField] private Color selectedColor = new Color(1f, 0.9f, 0.1f, 1f); [SerializeField] private Color hiddenColor = new Color(1f, 0.3f, 0.3f, 0.6f); [SerializeField] private bool drawLabels = true; private int _lastVersion = int.MinValue; private bool _lastEnabled; private readonly StringBuilder _sb = new StringBuilder(128); private GUIStyle _labelStyle; protected override void Awake() { base.Awake(); raycastTarget = false; if (player == null) player = GetComponentInParent(); if (player == null) player = FindAnyObjectByType(); if (trackingManager == null && player != null) trackingManager = player.TrackingManager; if (trackingManager == null) trackingManager = FindAnyObjectByType(); } private bool Active => trackingManager != null && trackingManager.ShowTrackingDebug && player != null; private void Update() { bool active = Active; int version = trackingManager != null ? trackingManager.ResolvedVersion : 0; if (active != _lastEnabled || (active && version != _lastVersion)) { _lastEnabled = active; _lastVersion = version; SetVerticesDirty(); } } protected override void OnPopulateMesh(VertexHelper vh) { vh.Clear(); if (!Active) return; var frame = trackingManager.CurrentTrackingFrame; if (frame == null) return; for (int i = 0; i < frame.Objects.Count; i++) { var o = frame.Objects[i]; bool selected = trackingManager.IsSelected(o.TrackId); Color polyColor = !o.Visible ? hiddenColor : (selected ? selectedColor : polygonColor); // Bounding box (tracking rect: yMin is the top edge). Vector2 tl = ToLocal(new Vector2(o.Bounds.xMin, o.Bounds.yMin)); Vector2 tr = ToLocal(new Vector2(o.Bounds.xMax, o.Bounds.yMin)); Vector2 br = ToLocal(new Vector2(o.Bounds.xMax, o.Bounds.yMax)); Vector2 bl = ToLocal(new Vector2(o.Bounds.xMin, o.Bounds.yMax)); AddLine(vh, tl, tr, bboxColor); AddLine(vh, tr, br, bboxColor); AddLine(vh, br, bl, bboxColor); AddLine(vh, bl, tl, bboxColor); // Polygon edges and vertices. var poly = o.Polygon; for (int p = 0; p < poly.Count; p++) { Vector2 a = ToLocal(poly[p]); Vector2 b = ToLocal(poly[(p + 1) % poly.Count]); if (poly.Count >= 2) AddLine(vh, a, b, polyColor); AddQuad(vh, a, vertexSize, polyColor); } } } private Vector2 ToLocal(Vector2 tracking) { // Tracking -> RawImage local (player) -> world -> this overlay's local. Works even if the overlay // is not exactly the same rect as the RawImage. Vector2 rawLocal = player.TrackingToLocalPoint(tracking); Vector3 world = player.Display.rectTransform.TransformPoint(rawLocal); return rectTransform.InverseTransformPoint(world); } private void AddLine(VertexHelper vh, Vector2 a, Vector2 b, Color color) { Vector2 dir = b - a; if (dir.sqrMagnitude < 1e-6f) return; Vector2 n = new Vector2(-dir.y, dir.x).normalized * (lineWidth * 0.5f); int i = vh.currentVertCount; vh.AddVert(a - n, color, Vector4.zero); vh.AddVert(a + n, color, Vector4.zero); vh.AddVert(b + n, color, Vector4.zero); vh.AddVert(b - n, color, Vector4.zero); vh.AddTriangle(i, i + 1, i + 2); vh.AddTriangle(i, i + 2, i + 3); } private void AddQuad(VertexHelper vh, Vector2 center, float size, Color color) { float h = size * 0.5f; int i = vh.currentVertCount; vh.AddVert(center + new Vector2(-h, -h), color, Vector4.zero); vh.AddVert(center + new Vector2(-h, h), color, Vector4.zero); vh.AddVert(center + new Vector2(h, h), color, Vector4.zero); vh.AddVert(center + new Vector2(h, -h), color, Vector4.zero); vh.AddTriangle(i, i + 1, i + 2); vh.AddTriangle(i, i + 2, i + 3); } // ---- labels (IMGUI: debug only, works on every platform) ---- private void OnGUI() { if (!drawLabels || !Active) return; if (_labelStyle == null) { _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 14, richText = false }; _labelStyle.normal.textColor = Color.white; } Canvas canvas = GetComponentInParent(); Camera cam = canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay ? canvas.worldCamera : null; _sb.Clear(); _sb.Append("video frame ").Append(trackingManager.CurrentFrame); _sb.Append(" tracked frame ").Append(trackingManager.CurrentSourceFrame); if (trackingManager.CurrentIsInterpolated) _sb.Append(" (interpolated)"); if (player != null && player.IsPrepared) _sb.Append(" ").Append(player.FrameRate.ToString("0.#")).Append(" fps"); DrawLabel(new Vector2(10f, Screen.height - 10f), _sb.ToString(), true); var frame = trackingManager.CurrentTrackingFrame; if (frame == null) return; for (int i = 0; i < frame.Objects.Count; i++) { var o = frame.Objects[i]; Vector3 world = player.Display.rectTransform.TransformPoint(player.TrackingToLocalPoint(new Vector2(o.Bounds.xMin, o.Bounds.yMin))); Vector2 screen = RectTransformUtility.WorldToScreenPoint(cam, world); _sb.Clear(); _sb.Append(o.TrackId); if (o.Definition != null && o.Definition.Name != o.TrackId) _sb.Append(" · ").Append(o.Definition.Name); if (!string.IsNullOrEmpty(o.Action)) _sb.Append(" · ").Append(o.Action); if (!o.Visible) _sb.Append(" · hidden"); DrawLabel(screen, _sb.ToString(), false); } } private void DrawLabel(Vector2 screenPos, string text, bool background) { // IMGUI has Y down; screen points have Y up. var content = new GUIContent(text); Vector2 size = _labelStyle.CalcSize(content); var rect = new Rect(screenPos.x, Screen.height - screenPos.y - size.y, size.x + 6f, size.y); GUI.color = new Color(0f, 0f, 0f, 0.6f); GUI.DrawTexture(rect, Texture2D.whiteTexture); GUI.color = Color.white; GUI.Label(new Rect(rect.x + 3f, rect.y, size.x, size.y), content, _labelStyle); } } }