using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Video;
using InteractiveVideo.Examples;
namespace InteractiveVideo.Editor
{
///
/// GameObject > Interactive Video > Create Example Setup
/// Builds the whole example hierarchy in the open scene and wires every reference, so the only manual steps
/// are dropping a VideoClip on the VideoPlayer and (optionally) swapping the sample tracking JSON.
///
public static class InteractiveVideoSetupMenu
{
private const string Root = "Assets/InteractiveVideo/";
private const string HighlightMaterialPath = Root + "Materials/InteractiveVideoHighlight.mat";
private const string MaskMaterialPath = Root + "Materials/InteractiveVideoMask.mat";
private const string SampleJsonPath = Root + "Examples/tracking_sample.json";
// The Speech "throw" clip (child throws a red ball) and its tracking data: used when both are present.
private const string ThrowClipPath = Root + "Videos/spaixjbHYK.mp4";
private const string ThrowJsonPath = Root + "Videos/spaixjbHYK.json";
/// The throw clip when it is in the package, else no clip (the sample JSON is used for plumbing tests).
[MenuItem("GameObject/Interactive Video/Create Example Setup", false, 10)]
public static void CreateExampleSetup()
{
var clip = AssetDatabase.LoadAssetAtPath(ThrowClipPath);
CreateSetup(clip, clip != null ? AssetDatabase.LoadAssetAtPath(ThrowJsonPath) : null);
}
/// Pick any clip under Assets; its tracking JSON is the file with the same name next to it.
[MenuItem("GameObject/Interactive Video/Create Setup From Clip...", false, 11)]
public static void CreateSetupFromClipDialog()
{
var start = System.IO.Directory.Exists(Application.dataPath + "/InteractiveVideo/Videos") ? Application.dataPath + "/InteractiveVideo/Videos" : Application.dataPath;
var picked = EditorUtility.OpenFilePanelWithFilters("Pick a video clip inside Assets", start, new[] { "Video", "mp4,webm,mov,m4v", "All files", "*" });
if (string.IsNullOrEmpty(picked)) return;
var full = System.IO.Path.GetFullPath(picked).Replace('\\', '/');
var assets = System.IO.Path.GetFullPath(Application.dataPath).Replace('\\', '/');
if (!full.StartsWith(assets, System.StringComparison.OrdinalIgnoreCase))
{
EditorUtility.DisplayDialog("Interactive Video", "The clip must be inside this project's Assets folder (copy it to Assets/InteractiveVideo/Videos first).", "OK");
return;
}
var assetPath = "Assets" + full.Substring(assets.Length);
var clip = AssetDatabase.LoadAssetAtPath(assetPath);
if (clip == null)
{
EditorUtility.DisplayDialog("Interactive Video", $"{assetPath} has not been imported as a VideoClip yet. Let Unity finish importing, then try again.", "OK");
return;
}
CreateSetup(clip, TrackingJsonFor(assetPath));
}
/// Right-click a VideoClip in the Project window.
[MenuItem("Assets/Interactive Video/Create Setup From Selected Clip", false, 20)]
public static void CreateSetupFromSelectedClip()
{
var clip = Selection.activeObject as VideoClip;
if (clip == null) return;
CreateSetup(clip, TrackingJsonFor(AssetDatabase.GetAssetPath(clip)));
}
[MenuItem("Assets/Interactive Video/Create Setup From Selected Clip", true)]
private static bool CreateSetupFromSelectedClipValidate() => Selection.activeObject is VideoClip;
/// Assets/.../name.mp4 -> Assets/.../name.json, or null with a warning when there is none.
public static TextAsset TrackingJsonFor(string clipAssetPath)
{
var jsonPath = System.IO.Path.ChangeExtension(clipAssetPath, ".json");
var json = AssetDatabase.LoadAssetAtPath(jsonPath);
if (json == null)
Debug.LogWarning($"[InteractiveVideo] No tracking file at {jsonPath}. The scene is built with the sample tracking; put the clip's JSON next to it with the same name and assign it on VideoTrackingManager.");
return json;
}
/// Builds the whole hierarchy for a clip and its tracking (either may be null: no clip = assign one by hand; no tracking = the sample JSON).
public static void CreateSetup(VideoClip clip, TextAsset trackingJson)
{
var highlightMat = AssetDatabase.LoadAssetAtPath(HighlightMaterialPath);
var maskMat = AssetDatabase.LoadAssetAtPath(MaskMaterialPath);
if (trackingJson == null)
trackingJson = AssetDatabase.LoadAssetAtPath(SampleJsonPath);
var firstObject = FirstObjectOf(trackingJson);
if (highlightMat == null || maskMat == null)
Debug.LogWarning($"[InteractiveVideo] Materials not found under {Root}Materials. Assign them by hand on the Highlight Controller / Mask Renderer.");
// ---- EventSystem ----
if (Object.FindAnyObjectByType() == null)
{
var es = new GameObject("EventSystem", typeof(EventSystem));
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
var moduleType = System.Type.GetType("UnityEngine.InputSystem.UI.InputSystemUIInputModule, Unity.InputSystem");
if (moduleType != null) es.AddComponent(moduleType);
else es.AddComponent();
#else
es.AddComponent();
#endif
Undo.RegisterCreatedObjectUndo(es, "Create EventSystem");
}
// ---- Canvas ----
var canvasGo = new GameObject("InteractiveVideoCanvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
Undo.RegisterCreatedObjectUndo(canvasGo, "Create Interactive Video Setup");
var canvas = canvasGo.GetComponent