AndroidWebViewPlayer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace NRKernal.Experimental.NRExamples
  5. {
  6. public interface IDrawListener
  7. {
  8. void onDrawBegin(Int64 nanos);
  9. void onDrawEnd(Int64 nanos);
  10. }
  11. public interface IWebEventListener
  12. {
  13. void onImmersiveSessionStart();
  14. void onImmersiveSessionEnd();
  15. }
  16. public class WebXRController
  17. {
  18. public bool isTracked;
  19. public bool isLeft;
  20. public float[] position = new float[3];
  21. public float[] orientation = new float[4];
  22. public float[] axes = new float[2];
  23. public float[] buttons = new float[5];
  24. private byte[] rawdata = new byte[58];
  25. public void UpdateData(bool tracked, bool isleft, Vector3 pos, Quaternion rotation, Vector2 touch, SystemInputState buttons)
  26. {
  27. this.isTracked = tracked;
  28. this.isLeft = isleft;
  29. this.position[0] = pos.x;
  30. this.position[1] = pos.y;
  31. this.position[2] = pos.z;
  32. var rot = ConversionUtility.ConvertOrientation(rotation);
  33. this.orientation[0] = rot.x;
  34. this.orientation[1] = rot.y;
  35. this.orientation[2] = rot.z;
  36. this.orientation[3] = rot.w;
  37. this.axes[0] = touch.x;
  38. this.axes[1] = touch.y;
  39. this.buttons[0] = buttons.buttons[0] ? 1 : 0;
  40. this.buttons[1] = buttons.buttons[1] ? 1 : 0;
  41. this.buttons[2] = buttons.buttons[2] ? 1 : 0;
  42. }
  43. public byte[] Serialize()
  44. {
  45. Array.Copy(BitConverter.GetBytes(isTracked), 0, rawdata, 0, 1);
  46. Array.Copy(BitConverter.GetBytes(isLeft), 0, rawdata, 1, 1);
  47. int index = 2;
  48. for (int i = 0; i < position.Length; i++)
  49. {
  50. Array.Copy(BitConverter.GetBytes(position[i]), 0, rawdata, index, sizeof(float));
  51. index += sizeof(float);
  52. }
  53. for (int i = 0; i < orientation.Length; i++)
  54. {
  55. Array.Copy(BitConverter.GetBytes(orientation[i]), 0, rawdata, index, sizeof(float));
  56. index += sizeof(float);
  57. }
  58. for (int i = 0; i < axes.Length; i++)
  59. {
  60. Array.Copy(BitConverter.GetBytes(axes[i]), 0, rawdata, index, sizeof(float));
  61. index += sizeof(float);
  62. }
  63. for (int i = 0; i < buttons.Length; i++)
  64. {
  65. Array.Copy(BitConverter.GetBytes(buttons[i]), 0, rawdata, index, sizeof(float));
  66. index += sizeof(float);
  67. }
  68. return rawdata;
  69. }
  70. public void DeSerialize(byte[] data)
  71. {
  72. isTracked = Convert.ToBoolean(data[0]);
  73. isLeft = Convert.ToBoolean(data[1]);
  74. int index = 2;
  75. for (int i = 0; i < position.Length; i++)
  76. {
  77. position[i] = ConvertUtility.IntBitsToFloat(BitConverter.ToInt32(data, index));
  78. index += sizeof(float);
  79. }
  80. for (int i = 0; i < orientation.Length; i++)
  81. {
  82. orientation[i] = ConvertUtility.IntBitsToFloat(BitConverter.ToInt32(data, index));
  83. index += sizeof(float);
  84. }
  85. for (int i = 0; i < axes.Length; i++)
  86. {
  87. axes[i] = ConvertUtility.IntBitsToFloat(BitConverter.ToInt32(data, index));
  88. index += sizeof(float);
  89. }
  90. for (int i = 0; i < buttons.Length; i++)
  91. {
  92. buttons[i] = ConvertUtility.IntBitsToFloat(BitConverter.ToInt32(data, index));
  93. index += sizeof(float);
  94. }
  95. }
  96. public override bool Equals(object obj)
  97. {
  98. if (obj is WebXRController)
  99. {
  100. WebXRController new_info = (WebXRController)obj;
  101. if (new_info.isLeft != this.isLeft || new_info.isTracked != this.isTracked)
  102. {
  103. return false;
  104. }
  105. for (int i = 0; i < position.Length; i++)
  106. {
  107. if (new_info.position[i] != position[i])
  108. {
  109. return false;
  110. }
  111. }
  112. for (int i = 0; i < orientation.Length; i++)
  113. {
  114. if (new_info.orientation[i] != orientation[i])
  115. {
  116. return false;
  117. }
  118. }
  119. for (int i = 0; i < axes.Length; i++)
  120. {
  121. if (new_info.axes[i] != axes[i])
  122. {
  123. return false;
  124. }
  125. }
  126. for (int i = 0; i < buttons.Length; i++)
  127. {
  128. if (new_info.buttons[i] != buttons[i])
  129. {
  130. return false;
  131. }
  132. }
  133. return true;
  134. }
  135. else
  136. {
  137. return false;
  138. }
  139. }
  140. public override int GetHashCode()
  141. {
  142. return base.GetHashCode();
  143. }
  144. public override string ToString()
  145. {
  146. return string.Format("isTracked:{0} isLeft:{1} position:[{2}] orientation:[{3}] axes:[{4}] buttons:[{5}]",
  147. isTracked, isLeft, position.ToString(), orientation.ToString(), axes.ToString(), buttons.ToString());
  148. }
  149. }
  150. public class AndroidWebViewPlayer
  151. {
  152. public delegate void OnWebEvent();
  153. public delegate void OnWebDrawEvent(Int64 timestamp, int index);
  154. public class AndroidPoseProviderProxy : AndroidJavaProxy, IPoseProvider
  155. {
  156. private IPoseProvider PoseProvider { get; set; }
  157. public AndroidPoseProviderProxy(IPoseProvider provider) : base("ai.nreal.webframe.IDataSource")
  158. {
  159. PoseProvider = provider;
  160. }
  161. public Int64 getRecommendedTimeNanos()
  162. {
  163. return PoseProvider.getRecommendedTimeNanos();
  164. }
  165. public float[] getHeadPose(Int64 nanos)
  166. {
  167. return PoseProvider.getHeadPose(nanos);
  168. }
  169. public float[] getEyePoseFromHead(int eye)
  170. {
  171. return PoseProvider.getEyePoseFromHead(eye);
  172. }
  173. public float[] getEyeFov(int eye)
  174. {
  175. return PoseProvider.getEyeFov(eye);
  176. }
  177. public byte[] getControllerData()
  178. {
  179. return PoseProvider.getControllerData();
  180. }
  181. }
  182. public class AndroidDrawListenerProxy : AndroidJavaProxy, IDrawListener
  183. {
  184. public event OnWebDrawEvent onDrawBeginEvent;
  185. public event OnWebDrawEvent onDrawEndEvent;
  186. public Queue<TimeStampInfo> m_CacheTimestamp;
  187. public struct TimeStampInfo
  188. {
  189. public Int64 timeStamp;
  190. public int frameIndex;
  191. }
  192. private const int MaxCacheCount = 10;
  193. public AndroidDrawListenerProxy(OnWebDrawEvent onDrawBegin, OnWebDrawEvent onDrawEnd) : base("ai.nreal.webframe.IDrawListener")
  194. {
  195. onDrawBeginEvent += onDrawBegin;
  196. onDrawEndEvent += onDrawEnd;
  197. m_CacheTimestamp = new Queue<TimeStampInfo>();
  198. }
  199. public void onDrawBegin(Int64 nanos)
  200. {
  201. onDrawBeginEvent?.Invoke(nanos, 0);
  202. }
  203. public void onDrawEnd(Int64 nanos)
  204. {
  205. int index = 0;
  206. if (GetCacheFrameIndexByTime(nanos, ref index))
  207. {
  208. onDrawEndEvent?.Invoke(nanos, index);
  209. }
  210. else
  211. {
  212. NRDebugger.Warning("Can not find the frameIndex by time:" + nanos);
  213. }
  214. }
  215. void onPreparePose(long nanos, int index)
  216. {
  217. if (m_CacheTimestamp.Count >= MaxCacheCount)
  218. {
  219. m_CacheTimestamp.Dequeue();
  220. }
  221. m_CacheTimestamp.Enqueue(new TimeStampInfo()
  222. {
  223. timeStamp = nanos,
  224. frameIndex = index
  225. });
  226. }
  227. private bool GetCacheFrameIndexByTime(Int64 timestamp, ref int index)
  228. {
  229. while (m_CacheTimestamp.Count != 0)
  230. {
  231. var item = m_CacheTimestamp.Dequeue();
  232. if (item.timeStamp == timestamp)
  233. {
  234. index = item.frameIndex;
  235. return true;
  236. }
  237. }
  238. NRDebugger.Info("[NRDataSourceProvider] Missing cache pose:" + timestamp);
  239. return false;
  240. }
  241. }
  242. public class AndroidWebEventListenerProxy : AndroidJavaProxy, IWebEventListener
  243. {
  244. private event OnWebEvent onImmersiveSessionStartEvent;
  245. private event OnWebEvent onImmersiveSessionEndEvent;
  246. public AndroidWebEventListenerProxy(OnWebEvent onImmersiveStart, OnWebEvent onImmersiveEnd) : base("ai.nreal.webframe.IWebEventListener")
  247. {
  248. onImmersiveSessionStartEvent += onImmersiveStart;
  249. onImmersiveSessionEndEvent += onImmersiveEnd;
  250. }
  251. public void onImmersiveSessionStart()
  252. {
  253. onImmersiveSessionStartEvent?.Invoke();
  254. }
  255. public void onImmersiveSessionEnd()
  256. {
  257. onImmersiveSessionEndEvent?.Invoke();
  258. }
  259. }
  260. AndroidJavaObject m_UnityActivity;
  261. AndroidJavaObject UnityActivity
  262. {
  263. get
  264. {
  265. if (m_UnityActivity == null)
  266. {
  267. AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
  268. m_UnityActivity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
  269. }
  270. return m_UnityActivity;
  271. }
  272. }
  273. AndroidJavaObject m_NRWebView;
  274. AndroidJavaObject NRWebView
  275. {
  276. get
  277. {
  278. if (m_NRWebView == null)
  279. {
  280. m_NRWebView = new AndroidJavaObject("ai.nreal.webframe.NRWebView", UnityActivity);
  281. }
  282. return m_NRWebView;
  283. }
  284. }
  285. public void Initialize(IntPtr surface, IPoseProvider poseProvider, OnWebDrawEvent onDrawBegin,
  286. OnWebDrawEvent onDrawEnd, OnWebEvent onImmersiveStart, OnWebEvent onImmersiveEnd)
  287. {
  288. try
  289. {
  290. AndroidJNI.AttachCurrentThread();
  291. var datasource = new AndroidPoseProviderProxy(poseProvider);
  292. var listener = new AndroidDrawListenerProxy(onDrawBegin, onDrawEnd);
  293. var webevent = new AndroidWebEventListenerProxy(onImmersiveStart, onImmersiveEnd);
  294. SetSurface(surface);
  295. NRWebView.Call("setDataSource", datasource);
  296. NRWebView.Call("setListeners", listener, webevent);
  297. NRWebView.Call<bool>("show");
  298. }
  299. catch (Exception e)
  300. {
  301. NRDebugger.Error("[AndroidWebViewPlayer] Init error:" + e.ToString());
  302. throw;
  303. }
  304. }
  305. public void SetSurface(IntPtr surface, bool useoverlay = false)
  306. {
  307. AndroidJNI.AttachCurrentThread();
  308. string funcName = useoverlay ? "setOverlaySurface" : "setDefaultSurface";
  309. var methodID = AndroidJNI.GetMethodID(NRWebView.GetRawClass(), funcName, "(Landroid/view/Surface;)V");
  310. jvalue[] jniArgs = new jvalue[1];
  311. jniArgs[0].l = surface;
  312. AndroidJNI.CallVoidMethod(NRWebView.GetRawObject(), methodID, jniArgs);
  313. }
  314. public void LoadURL(string url)
  315. {
  316. try
  317. {
  318. NRWebView.Call("loadUrl", url);
  319. }
  320. catch (Exception e)
  321. {
  322. NRDebugger.Error("[AndroidWebViewPlayer] LoadURL error:" + e.ToString());
  323. throw;
  324. }
  325. }
  326. public void UpdateTouchState(Vector2 point, Int64 timestamp, bool istouching)
  327. {
  328. // Must use Int64 but not UInt64.
  329. NRWebView.Call("TouchEvent", point.x, point.y, timestamp, istouching ? 1 : 0);
  330. }
  331. public void Close()
  332. {
  333. try
  334. {
  335. NRWebView.Call("close");
  336. }
  337. catch (Exception e)
  338. {
  339. NRDebugger.Error("[AndroidWebViewPlayer] Close error:" + e.ToString());
  340. throw;
  341. }
  342. }
  343. }
  344. }