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