GestureTrackingState.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. namespace Ximmerse.XR.InputSystems.GazeAndGestureInteraction
  6. {
  7. /// <summary>
  8. /// 更新 tracking state 的状态控制器
  9. /// </summary>
  10. public partial class GazeAndHandInteractionSystem
  11. {
  12. /// <summary>
  13. /// Gesture type recoginzed by system
  14. /// </summary>
  15. public enum GestureType : byte
  16. {
  17. /// <summary>
  18. /// 不明手势
  19. /// </summary>
  20. Unkown = 0,
  21. /// <summary>
  22. /// 手指张开并朝向用户前方
  23. /// </summary>
  24. OpenHandAndPointForward,
  25. /// <summary>
  26. /// 手指张开并朝向用户自身方向
  27. /// </summary>
  28. OpenHandAndPointToUser,
  29. /// <summary>
  30. /// 食指拇指捏合
  31. /// </summary>
  32. Pinch,
  33. /// <summary>
  34. /// 握拳并手心朝向用户前方
  35. /// </summary>
  36. CloseHandAndPointForward,
  37. /// <summary>
  38. /// 握拳并手心朝向用户自身
  39. /// </summary>
  40. CloseHandAndPointToUser,
  41. }
  42. /// <summary>
  43. /// Impl of gesture tracking state.
  44. /// </summary>
  45. internal partial class TrackState
  46. {
  47. Camera m_mainCamera;
  48. public Camera mainCamera
  49. {
  50. get
  51. {
  52. if (!m_mainCamera)
  53. m_mainCamera = Camera.main;
  54. return m_mainCamera;
  55. }
  56. }
  57. /// <summary>
  58. /// Current gesture type
  59. /// </summary>
  60. public GestureType gestureType
  61. {
  62. get; private set;
  63. }
  64. /// <summary>
  65. /// Delta position movement in world space
  66. /// </summary>
  67. public Vector3 DeltaPositionWorld
  68. {
  69. get => this.gestureRecords[0].PalmWorldPose.position - gestureRecords[1].PalmWorldPose.position;
  70. }
  71. /// <summary>
  72. /// Delta angular rotation in world space
  73. /// </summary>
  74. public Vector3 DeltaAngluarWorld
  75. {
  76. get => this.gestureRecords[0].PalmWorldPose.rotation.eulerAngles - gestureRecords[1].PalmWorldPose.rotation.eulerAngles;
  77. }
  78. public event Action<GestureType> OnGestureChanged;
  79. float previousGestureChangeTime;
  80. /// <summary>
  81. /// Structure to cache native gesture data.
  82. /// </summary>
  83. struct NativeGestureRecord
  84. {
  85. public bool IsValidFrame;
  86. public float RealFrameTime;
  87. /// <summary>
  88. /// Palm world pose
  89. /// </summary>
  90. public Pose PalmWorldPose;
  91. /// <summary>
  92. /// Palm local pose.
  93. /// </summary>
  94. public Pose PalmLocalPose;
  95. /// <summary>
  96. /// Palm movement velocity.
  97. /// </summary>
  98. public Pose PalmVelocity;
  99. /// <summary>
  100. /// distance to previous frame
  101. /// </summary>
  102. public float PalmDeltaDistance;
  103. /// <summary>
  104. /// Angle diff to previous frame
  105. /// </summary>
  106. public Vector3 DeltaAngle;
  107. public int NativeGesturePluginCode;
  108. }
  109. const int kCacheGestureDataCount = 5;
  110. /// <summary>
  111. /// 缓存的手势输入数据.
  112. /// </summary>
  113. readonly NativeGestureRecord[] gestureRecords = new NativeGestureRecord[kCacheGestureDataCount];
  114. void TickGestureState()
  115. {
  116. UpdateGestureRecordQueue();
  117. //连续三帧判断状态:
  118. if (CheckValidFrameCount(true, 2))
  119. {
  120. trackingState = HandTrackingStatus.Tracking;
  121. }
  122. else if (CheckValidFrameCount(false, 2))
  123. {
  124. trackingState = HandTrackingStatus.Invalid;
  125. }
  126. //don't change gesture type too frequently
  127. if (trackingState == HandTrackingStatus.Tracking && (Time.realtimeSinceStartup - previousGestureChangeTime) >= kMinAllowStateChangeTime)
  128. {
  129. UpdateGestureTypeChange();
  130. }
  131. }
  132. /// <summary>
  133. /// 更新 gesture输入队列
  134. /// </summary>
  135. void UpdateGestureRecordQueue()
  136. {
  137. HandTrackingInfo currentHandTrackInfo = HandTracking.HandTrackingInfo;
  138. if (!currentHandTrackInfo.IsValid)
  139. {
  140. InsertRecord(new NativeGestureRecord());//insert dummy record
  141. }
  142. //添加一条有效数据
  143. else
  144. {
  145. NativeGestureRecord record = new NativeGestureRecord()
  146. {
  147. IsValidFrame = true,
  148. RealFrameTime = Time.realtimeSinceStartup,
  149. NativeGesturePluginCode = currentHandTrackInfo.NativeGestureType,
  150. PalmWorldPose = new Pose(currentHandTrackInfo.PalmPosition, Quaternion.LookRotation(currentHandTrackInfo.PalmNormal)),
  151. PalmLocalPose = new Pose(currentHandTrackInfo.PalmLocalPosition, Quaternion.LookRotation(currentHandTrackInfo.PalmLocalNormal)),
  152. };
  153. //计算delta:
  154. if (GetPreviousValidFrame(out NativeGestureRecord prevValidFrame))
  155. {
  156. float deltaTime = Mathf.Min(Mathf.Epsilon, record.RealFrameTime - prevValidFrame.RealFrameTime);
  157. Vector3 vector2prev = record.PalmWorldPose.position - prevValidFrame.PalmWorldPose.position;
  158. Vector3 deltaAngle = (Quaternion.Inverse(prevValidFrame.PalmWorldPose.rotation) * record.PalmWorldPose.rotation).eulerAngles;
  159. record.PalmVelocity = new Pose(
  160. (vector2prev) /
  161. deltaTime,
  162. Quaternion.Euler(deltaAngle / deltaTime));
  163. record.PalmDeltaDistance = vector2prev.magnitude;
  164. record.DeltaAngle = deltaAngle;
  165. }
  166. InsertRecord(record);
  167. }
  168. }
  169. /// <summary>
  170. /// 更新手势识别的当前手势输入类型
  171. /// </summary>
  172. void UpdateGestureTypeChange()
  173. {
  174. int checkFrame = 2;
  175. //如果连续五帧是Open Hand姿态:
  176. if (CheckNativeGestureCodeMatchContinueFrames((int)TouchlessA3D.GestureType.OPEN_HAND, checkFrame)
  177. || CheckNativeGestureCodeMatchContinueFrames((int)TouchlessA3D.GestureType.HAND, checkFrame))
  178. {
  179. //手心张开,向前
  180. if (CheckForawrdMatchContinueFrames(this.mainCamera.transform.forward, checkFrame))
  181. {
  182. InternalUpdateGestureState(GestureType.OpenHandAndPointForward);
  183. }
  184. //手心张开,向后
  185. else if (CheckForawrdMatchContinueFrames(-this.mainCamera.transform.forward, checkFrame))
  186. {
  187. InternalUpdateGestureState(GestureType.OpenHandAndPointToUser);
  188. }
  189. else
  190. {
  191. InternalUpdateGestureState(GestureType.Unkown);
  192. }
  193. }
  194. if (CheckNativeGestureCodeMatchContinueFrames((int)TouchlessA3D.GestureType.CLOSED_PINCH, checkFrame))
  195. {
  196. InternalUpdateGestureState(GestureType.Pinch);
  197. }
  198. if (CheckNativeGestureCodeMatchContinueFrames((int)TouchlessA3D.GestureType.CLOSED_HAND, checkFrame))
  199. {
  200. //握拳,向前
  201. if (CheckForawrdMatchContinueFrames(this.mainCamera.transform.forward, checkFrame))
  202. {
  203. InternalUpdateGestureState(GestureType.CloseHandAndPointForward);
  204. }
  205. //握拳,向用户
  206. else if (CheckForawrdMatchContinueFrames(-this.mainCamera.transform.forward, checkFrame))
  207. {
  208. InternalUpdateGestureState(GestureType.CloseHandAndPointToUser);
  209. }
  210. else
  211. {
  212. InternalUpdateGestureState(GestureType.Unkown);
  213. }
  214. }
  215. }
  216. void InternalUpdateGestureState(GestureType newGesture)
  217. {
  218. if (gestureType != newGesture)
  219. {
  220. gestureType = newGesture;
  221. previousGestureChangeTime = Time.realtimeSinceStartup;
  222. OnGestureChanged?.Invoke(newGesture);
  223. }
  224. }
  225. void InsertRecord(NativeGestureRecord nativeRecord)
  226. {
  227. for (int i = gestureRecords.Length - 1; i > 0; i--)
  228. {
  229. gestureRecords[i] = gestureRecords[i - 1];
  230. }
  231. gestureRecords[0] = nativeRecord;
  232. }
  233. /// <summary>
  234. /// Get previous valid record.
  235. /// </summary>
  236. /// <param name="prevNativeRecord"></param>
  237. /// <returns></returns>
  238. bool GetPreviousValidFrame(out NativeGestureRecord prevNativeRecord)
  239. {
  240. for (int i = 0; i < gestureRecords.Length; i++)//starts from 1
  241. {
  242. if (gestureRecords[i].IsValidFrame)
  243. {
  244. prevNativeRecord = gestureRecords[i];
  245. return true;
  246. }
  247. }
  248. prevNativeRecord = default(NativeGestureRecord);
  249. return false;
  250. }
  251. /// <summary>
  252. /// 检查连续若干帧数据是有效数据
  253. /// </summary>
  254. /// <param name="frameCount"></param>
  255. /// <returns></returns>
  256. bool CheckValidFrameCount(bool isValid, int frameCount)
  257. {
  258. for (int i = 0; i < frameCount; i++)
  259. {
  260. if (gestureRecords[i].IsValidFrame != isValid)
  261. {
  262. return false;
  263. }
  264. }
  265. return true;
  266. }
  267. /// <summary>
  268. /// Check a certain frames matching forward direction
  269. /// </summary>
  270. /// <param name="forward"></param>
  271. /// <param name="frameCount"></param>
  272. /// <param name="expectDot"></param>
  273. /// <returns></returns>
  274. bool CheckForawrdMatchContinueFrames(Vector3 forward, int frameCount, float expectDot = 0.45f)
  275. {
  276. for (int i = 0; i < frameCount; i++)
  277. {
  278. if (Vector3.Dot(forward, this.gestureRecords[i].PalmWorldPose.forward) < expectDot)
  279. {
  280. return false;
  281. }
  282. }
  283. return true;
  284. }
  285. /// <summary>
  286. /// Check if a certain frames matching target gesture code.
  287. /// </summary>
  288. /// <param name="targetCode"></param>
  289. /// <param name="frameCount"></param>
  290. /// <returns></returns>
  291. bool CheckNativeGestureCodeMatchContinueFrames(int targetCode, int frameCount)
  292. {
  293. for (int i = 0; i < frameCount; i++)
  294. {
  295. if (targetCode != this.gestureRecords[i].NativeGesturePluginCode)
  296. {
  297. return false;
  298. }
  299. }
  300. return true;
  301. }
  302. }
  303. }
  304. }