LightGlueCameraAimController.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using UnityEngine;
  2. using LightGlue.Unity.Game;
  3. using LightGlue.Unity.Networking;
  4. using LightGlue.Unity.Roma;
  5. using LightGlue.Unity.Runtime;
  6. using LightGlue.Unity.Config;
  7. namespace LightGlue.Unity.UI
  8. {
  9. /// <summary>
  10. /// 专用于游戏内的 LightGlue 相机朝向控制器
  11. /// - 从 HardwareToPythonUdpBridge 读取算法返回的相机坐标(参考图像坐标系)
  12. /// - 将参考图坐标映射到游戏实际屏幕坐标(Screen.width / Screen.height)
  13. /// - 使用 ScreenPointToRay 计算射线方向,并更新 CameraToLook.ins.localRotation
  14. /// - 不依赖 UI Image,不会与 SimpleCameraPositionMarker 的 UI 逻辑耦合或冲突
  15. /// </summary>
  16. public class LightGlueCameraAimController : MonoBehaviour
  17. {
  18. [Header("事件驱动(推荐插件模式)")]
  19. [Tooltip("为 true 时,仅使用 LightGlueRuntimeFacade.OnPositionUpdate 作为结果来源,不再依赖 LightGlueManager/RomaManager。")]
  20. public bool useOnPositionUpdate = true;
  21. [Header("结果来源")]
  22. [Tooltip("优先使用 LightGlueManager(旧场景);若为空则回退到 RomaManager(新 Roma 场景)。禁止直接从 Bridge 获取结果。")]
  23. public LightGlueManager manager;
  24. [Tooltip("Roma 场景下的结果来源。通常留空,自动使用 RomaManager.Instance。")]
  25. public RomaManager romaManager;
  26. [Header("参考图像尺寸(必须与 Python --resize 一致)")]
  27. [Tooltip("参考图像宽度(像素)")]
  28. public int referenceImageWidth = 640;
  29. [Tooltip("参考图像高度(像素)")]
  30. public int referenceImageHeight = 480;
  31. [Header("平滑参数来源(唯一来源,无本地参数)")]
  32. [Tooltip("留空则使用 LightGlueCursorSettings.Instance;所有平滑参数均由此引用读取,UI 修改后即时生效")]
  33. public LightGlueCursorSettings cursorSettings;
  34. private Quaternion _currentRotation;
  35. private bool _hasCurrentRotation;
  36. private Quaternion _smoothStartRot;
  37. private Quaternion _smoothTargetRot;
  38. private float _smoothProgress;
  39. private LightGluePositionUpdate _latestUpdate;
  40. private bool _hasLatestUpdate;
  41. private LightGlueCursorSettings GetCursorSettings()
  42. {
  43. if (cursorSettings != null) return cursorSettings;
  44. return LightGlueCursorSettings.Instance;
  45. }
  46. private void OnEnable()
  47. {
  48. ReferenceImageResizeService.EnsureInitialized();
  49. SetReferenceImageSize(ReferenceImageResizeService.Width, ReferenceImageResizeService.Height);
  50. ReferenceImageResizeService.OnResizeChanged += SetReferenceImageSize;
  51. if (!useOnPositionUpdate) return;
  52. LightGlueRuntimeFacade.Ensure();
  53. LightGlueRuntimeFacade.OnPositionUpdate += HandlePositionUpdate;
  54. }
  55. private void OnDisable()
  56. {
  57. ReferenceImageResizeService.OnResizeChanged -= SetReferenceImageSize;
  58. if (!useOnPositionUpdate) return;
  59. LightGlueRuntimeFacade.OnPositionUpdate -= HandlePositionUpdate;
  60. }
  61. private void HandlePositionUpdate(LightGluePositionUpdate update)
  62. {
  63. _latestUpdate = update;
  64. _hasLatestUpdate = true;
  65. }
  66. private void Start()
  67. {
  68. if (cursorSettings == null)
  69. cursorSettings = LightGlueCursorSettings.Instance;
  70. if (GetCursorSettings() == null)
  71. Debug.LogWarning("[LightGlueCameraAimController] 未找到 LightGlueCursorSettings,相机朝向平滑参数将不可用,请确保 GameManager 已生成设置预制。");
  72. if (!useOnPositionUpdate)
  73. {
  74. if (manager == null) manager = LightGlueManager.Instance;
  75. if (manager == null && romaManager == null) romaManager = RomaManager.Instance;
  76. if (manager == null && romaManager == null)
  77. Debug.LogError("[LightGlueCameraAimController] 未找到 LightGlueManager / RomaManager,无法获取算法结果。请确认场景已生成对应 System 预制。");
  78. }
  79. }
  80. private void Update()
  81. {
  82. LightGlueResult result;
  83. if (useOnPositionUpdate)
  84. {
  85. if (!_hasLatestUpdate) return;
  86. if (!_latestUpdate.IsValid) return;
  87. // Position/CameraLocation 均为参考图坐标(左上原点、Y向下)
  88. Vector2 p = _latestUpdate.CameraLocation != default ? _latestUpdate.CameraLocation : _latestUpdate.Position;
  89. result = new LightGlueResult(
  90. isValid: _latestUpdate.IsValid,
  91. numMatches: _latestUpdate.NumMatches,
  92. inliersRatio: _latestUpdate.InliersRatio,
  93. cameraX: p.x,
  94. cameraY: p.y);
  95. }
  96. else
  97. {
  98. if (manager != null)
  99. {
  100. if (!manager.HasLatestResult) return;
  101. result = manager.LatestResult;
  102. }
  103. else
  104. {
  105. if (romaManager == null) romaManager = RomaManager.Instance;
  106. if (romaManager == null || !romaManager.HasLatestResult) return;
  107. result = romaManager.LatestResult;
  108. }
  109. }
  110. LightGlueCursorSettings settings = GetCursorSettings();
  111. if (settings == null)
  112. return;
  113. if (!result.IsValid) return;
  114. if (Camera.main == null || CameraToLook.ins == null)
  115. return;
  116. Vector2 gameScreenPos = result.GetMappedPosition(
  117. referenceWidth: referenceImageWidth,
  118. referenceHeight: referenceImageHeight,
  119. targetWidth: Screen.width,
  120. targetHeight: Screen.height,
  121. type: LightGlueResult.MappedCoordinateType.GameScreen);
  122. Vector3 screenPoint = new Vector3(gameScreenPos.x, gameScreenPos.y, 0f);
  123. Ray ray = Camera.main.ScreenPointToRay(screenPoint);
  124. Vector3 rayEndPoint = ray.GetPoint(200f);
  125. Quaternion targetRotation = Quaternion.LookRotation(rayEndPoint - Camera.main.transform.position);
  126. targetRotation = Quaternion.AngleAxis(-180f, Vector3.up) * targetRotation;
  127. bool useSmooth = settings.cameraAimEnableSmoothRotation;
  128. float lerpSpeed = settings.cameraAimRotationLerpSpeed;
  129. float jitterDeg = settings.cameraAimRotationJitterThresholdDeg;
  130. AnimationCurve curve = settings.cameraAimSmoothCurve;
  131. if (!useSmooth || !_hasCurrentRotation)
  132. {
  133. _currentRotation = targetRotation;
  134. _hasCurrentRotation = true;
  135. _smoothStartRot = targetRotation;
  136. _smoothTargetRot = targetRotation;
  137. _smoothProgress = 1f;
  138. }
  139. else
  140. {
  141. if (jitterDeg > 0f)
  142. {
  143. float angle = Quaternion.Angle(_currentRotation, targetRotation);
  144. if (angle < jitterDeg)
  145. {
  146. CameraToLook.ins.localRotation = _currentRotation;
  147. return;
  148. }
  149. }
  150. _smoothTargetRot = targetRotation;
  151. if (_smoothProgress >= 1f)
  152. {
  153. _smoothStartRot = _currentRotation;
  154. _smoothProgress = 0f;
  155. }
  156. _smoothProgress += Time.deltaTime * lerpSpeed;
  157. _smoothProgress = Mathf.Clamp01(_smoothProgress);
  158. float t = (curve != null && curve.keys.Length > 0) ? curve.Evaluate(_smoothProgress) : _smoothProgress;
  159. t = Mathf.Clamp01(t);
  160. _currentRotation = Quaternion.Slerp(_smoothStartRot, _smoothTargetRot, t);
  161. }
  162. CameraToLook.ins.localRotation = _currentRotation;
  163. }
  164. /// <summary>
  165. /// 允许在运行时从外部调整参考图像尺寸(例如根据 Python 参数动态设置)
  166. /// </summary>
  167. public void SetReferenceImageSize(int width, int height)
  168. {
  169. referenceImageWidth = width;
  170. referenceImageHeight = height;
  171. }
  172. }
  173. }