using UnityEngine;
using LightGlue.Unity.Game;
using LightGlue.Unity.Networking;
using LightGlue.Unity.Roma;
using LightGlue.Unity.Runtime;
using LightGlue.Unity.Config;
namespace LightGlue.Unity.UI
{
///
/// 专用于游戏内的 LightGlue 相机朝向控制器
/// - 从 HardwareToPythonUdpBridge 读取算法返回的相机坐标(参考图像坐标系)
/// - 将参考图坐标映射到游戏实际屏幕坐标(Screen.width / Screen.height)
/// - 使用 ScreenPointToRay 计算射线方向,并更新 CameraToLook.ins.localRotation
/// - 不依赖 UI Image,不会与 SimpleCameraPositionMarker 的 UI 逻辑耦合或冲突
///
public class LightGlueCameraAimController : MonoBehaviour
{
[Header("事件驱动(推荐插件模式)")]
[Tooltip("为 true 时,仅使用 LightGlueRuntimeFacade.OnPositionUpdate 作为结果来源,不再依赖 LightGlueManager/RomaManager。")]
public bool useOnPositionUpdate = true;
[Header("结果来源")]
[Tooltip("优先使用 LightGlueManager(旧场景);若为空则回退到 RomaManager(新 Roma 场景)。禁止直接从 Bridge 获取结果。")]
public LightGlueManager manager;
[Tooltip("Roma 场景下的结果来源。通常留空,自动使用 RomaManager.Instance。")]
public RomaManager romaManager;
[Header("参考图像尺寸(必须与 Python --resize 一致)")]
[Tooltip("参考图像宽度(像素)")]
public int referenceImageWidth = 640;
[Tooltip("参考图像高度(像素)")]
public int referenceImageHeight = 480;
[Header("平滑参数来源(唯一来源,无本地参数)")]
[Tooltip("留空则使用 LightGlueCursorSettings.Instance;所有平滑参数均由此引用读取,UI 修改后即时生效")]
public LightGlueCursorSettings cursorSettings;
private Quaternion _currentRotation;
private bool _hasCurrentRotation;
private Quaternion _smoothStartRot;
private Quaternion _smoothTargetRot;
private float _smoothProgress;
private LightGluePositionUpdate _latestUpdate;
private bool _hasLatestUpdate;
private LightGlueCursorSettings GetCursorSettings()
{
if (cursorSettings != null) return cursorSettings;
return LightGlueCursorSettings.Instance;
}
private void OnEnable()
{
ReferenceImageResizeService.EnsureInitialized();
SetReferenceImageSize(ReferenceImageResizeService.Width, ReferenceImageResizeService.Height);
ReferenceImageResizeService.OnResizeChanged += SetReferenceImageSize;
if (!useOnPositionUpdate) return;
LightGlueRuntimeFacade.Ensure();
LightGlueRuntimeFacade.OnPositionUpdate += HandlePositionUpdate;
}
private void OnDisable()
{
ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize;
if (!useOnPositionUpdate) return;
LightGlueRuntimeFacade.OnPositionUpdate -= HandlePositionUpdate;
}
private void HandlePositionUpdate(LightGluePositionUpdate update)
{
_latestUpdate = update;
_hasLatestUpdate = true;
}
private void Start()
{
if (cursorSettings == null)
cursorSettings = LightGlueCursorSettings.Instance;
if (GetCursorSettings() == null)
Debug.LogWarning("[LightGlueCameraAimController] 未找到 LightGlueCursorSettings,相机朝向平滑参数将不可用,请确保 GameManager 已生成设置预制。");
if (!useOnPositionUpdate)
{
if (manager == null) manager = LightGlueManager.Instance;
if (manager == null && romaManager == null) romaManager = RomaManager.Instance;
if (manager == null && romaManager == null)
Debug.LogError("[LightGlueCameraAimController] 未找到 LightGlueManager / RomaManager,无法获取算法结果。请确认场景已生成对应 System 预制。");
}
}
private void Update()
{
LightGlueResult result;
if (useOnPositionUpdate)
{
if (!_hasLatestUpdate) return;
if (!_latestUpdate.IsValid) return;
// Position/CameraLocation 均为参考图坐标(左上原点、Y向下)
Vector2 p = _latestUpdate.CameraLocation != default ? _latestUpdate.CameraLocation : _latestUpdate.Position;
result = new LightGlueResult(
isValid: _latestUpdate.IsValid,
numMatches: _latestUpdate.NumMatches,
inliersRatio: _latestUpdate.InliersRatio,
cameraX: p.x,
cameraY: p.y);
}
else
{
if (manager != null)
{
if (!manager.HasLatestResult) return;
result = manager.LatestResult;
}
else
{
if (romaManager == null) romaManager = RomaManager.Instance;
if (romaManager == null || !romaManager.HasLatestResult) return;
result = romaManager.LatestResult;
}
}
LightGlueCursorSettings settings = GetCursorSettings();
if (settings == null)
return;
if (!result.IsValid) return;
if (Camera.main == null || CameraToLook.ins == null)
return;
Vector2 gameScreenPos = result.GetMappedPosition(
referenceWidth: referenceImageWidth,
referenceHeight: referenceImageHeight,
targetWidth: Screen.width,
targetHeight: Screen.height,
type: LightGlueResult.MappedCoordinateType.GameScreen);
Vector3 screenPoint = new Vector3(gameScreenPos.x, gameScreenPos.y, 0f);
Ray ray = Camera.main.ScreenPointToRay(screenPoint);
Vector3 rayEndPoint = ray.GetPoint(200f);
Quaternion targetRotation = Quaternion.LookRotation(rayEndPoint - Camera.main.transform.position);
targetRotation = Quaternion.AngleAxis(-180f, Vector3.up) * targetRotation;
bool useSmooth = settings.cameraAimEnableSmoothRotation;
float lerpSpeed = settings.cameraAimRotationLerpSpeed;
float jitterDeg = settings.cameraAimRotationJitterThresholdDeg;
AnimationCurve curve = settings.cameraAimSmoothCurve;
if (!useSmooth || !_hasCurrentRotation)
{
_currentRotation = targetRotation;
_hasCurrentRotation = true;
_smoothStartRot = targetRotation;
_smoothTargetRot = targetRotation;
_smoothProgress = 1f;
}
else
{
if (jitterDeg > 0f)
{
float angle = Quaternion.Angle(_currentRotation, targetRotation);
if (angle < jitterDeg)
{
CameraToLook.ins.localRotation = _currentRotation;
return;
}
}
_smoothTargetRot = targetRotation;
if (_smoothProgress >= 1f)
{
_smoothStartRot = _currentRotation;
_smoothProgress = 0f;
}
_smoothProgress += Time.deltaTime * lerpSpeed;
_smoothProgress = Mathf.Clamp01(_smoothProgress);
float t = (curve != null && curve.keys.Length > 0) ? curve.Evaluate(_smoothProgress) : _smoothProgress;
t = Mathf.Clamp01(t);
_currentRotation = Quaternion.Slerp(_smoothStartRot, _smoothTargetRot, t);
}
CameraToLook.ins.localRotation = _currentRotation;
}
///
/// 允许在运行时从外部调整参考图像尺寸(例如根据 Python 参数动态设置)
///
public void SetReferenceImageSize(int width, int height)
{
referenceImageWidth = width;
referenceImageHeight = height;
}
}
}