ARCore.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Threading.Tasks;
  3. using Android;
  4. using Urho;
  5. using Urho.Urho2D;
  6. using Urho.IO;
  7. using Android.App;
  8. using Android.Content.PM;
  9. using Android.Runtime;
  10. using Android.Views;
  11. using Com.Google.AR.Core;
  12. namespace Urho.Droid
  13. {
  14. public class ARCoreComponent : Component
  15. {
  16. const uint GL_TEXTURE_EXTERNAL_OES = 36197;
  17. bool paused;
  18. public Texture2D CameraTexture { get; private set; }
  19. public Viewport Viewport { get; private set; }
  20. public Session Session { get; private set; }
  21. public Config Config { get; private set; }
  22. public Camera Camera { get; private set; }
  23. public string ARCoreShader { get; set; } = "ARCore";
  24. public event Action<Frame> ARFrameUpdated;
  25. public event Action<Config> ConfigRequested;
  26. [Preserve]
  27. public ARCoreComponent() { ReceiveSceneUpdates = true; }
  28. [Preserve]
  29. public ARCoreComponent(IntPtr handle) : base(handle) { ReceiveSceneUpdates = true; }
  30. public override unsafe void OnAttachedToNode(Node node)
  31. {
  32. Application.Paused += OnPause;
  33. Application.Resumed += OnResume;
  34. }
  35. public Task Run(Camera camera = null)
  36. {
  37. if (CameraTexture != null)
  38. throw new InvalidOperationException("ARCore component is already initialized, if you want to re-configure ARCore session - use Session property.");
  39. Camera = camera;
  40. if (Camera == null)
  41. Camera = base.Scene.GetComponent<Camera>(true);
  42. if (Camera == null)
  43. throw new InvalidOperationException("Camera component was not found.");
  44. CameraTexture = new Texture2D();
  45. CameraTexture.SetCustomTarget(GL_TEXTURE_EXTERNAL_OES);
  46. CameraTexture.SetNumLevels(1);
  47. CameraTexture.FilterMode = TextureFilterMode.Bilinear;
  48. CameraTexture.SetAddressMode(TextureCoordinate.U, TextureAddressMode.Clamp);
  49. CameraTexture.SetAddressMode(TextureCoordinate.V, TextureAddressMode.Clamp);
  50. CameraTexture.SetSize(Application.Graphics.Width, Application.Graphics.Height, Graphics.Float32Format, TextureUsage.Dynamic);
  51. CameraTexture.Name = nameof(CameraTexture);
  52. Application.ResourceCache.AddManualResource(CameraTexture);
  53. Viewport = Application.Renderer.GetViewport(0);
  54. if (base.Application is SimpleApplication simpleApp)
  55. {
  56. simpleApp.MoveCamera = false;
  57. }
  58. var videoRp = new RenderPathCommand(RenderCommandType.Quad);
  59. videoRp.PixelShaderName = (UrhoString)ARCoreShader; //see CoreData/Shaders/GLSL/ARCore.glsl
  60. videoRp.VertexShaderName = (UrhoString)ARCoreShader;
  61. videoRp.SetOutput(0, "viewport");
  62. videoRp.SetTextureName(TextureUnit.Diffuse, CameraTexture.Name);
  63. Viewport.RenderPath.InsertCommand(1, videoRp);
  64. var tcs = new TaskCompletionSource<bool>();
  65. var activity = (Activity)Urho.Application.CurrentWindow.Target;
  66. activity.RunOnUiThread(() =>
  67. {
  68. var cameraAllowed = activity.CheckSelfPermission(Manifest.Permission.Camera);
  69. if (cameraAllowed != Permission.Granted)
  70. throw new InvalidOperationException("Camera permission: Denied");
  71. var session = new Session(activity);
  72. session.SetCameraTextureName((int)CameraTexture.AsGPUObject().GPUObjectName);
  73. session.SetDisplayGeometry((int)SurfaceOrientation.Rotation0 /*windowManager.DefaultDisplay.Rotation*/,
  74. Application.Graphics.Width, Application.Graphics.Height);
  75. Config = new Config(session);
  76. Config.SetLightEstimationMode(Config.LightEstimationMode.AmbientIntensity);
  77. Config.SetUpdateMode(Config.UpdateMode.LatestCameraImage);
  78. Config.SetPlaneFindingMode(Config.PlaneFindingMode.Horizontal);
  79. ConfigRequested?.Invoke(Config);
  80. if (!session.IsSupported(Config))
  81. {
  82. throw new Exception("AR is not supported on this device with given config");
  83. }
  84. session.Configure(Config);
  85. paused = false;
  86. session.Resume();
  87. Session = session;
  88. Urho.Application.InvokeOnMain(() => tcs.TrySetResult(true));
  89. });
  90. return tcs.Task;
  91. }
  92. void OnPause()
  93. {
  94. paused = true;
  95. Session?.Pause();
  96. }
  97. void OnResume()
  98. {
  99. paused = false;
  100. Session?.Resume();
  101. }
  102. protected override void OnDeleted()
  103. {
  104. Application.Paused -= OnPause;
  105. Application.Resumed -= OnResume;
  106. base.OnDeleted();
  107. try
  108. {
  109. Session?.Pause();
  110. }
  111. catch (Exception exc)
  112. {
  113. Log.Write(LogLevel.Warning, "ARCore pause error: " + exc);
  114. }
  115. }
  116. protected override void OnUpdate(float timeStep)
  117. {
  118. if (paused)
  119. return;
  120. if (Camera == null)
  121. throw new Exception("ARCore.Camera property was not set");
  122. try
  123. {
  124. if (Session == null)
  125. return;
  126. var frame = Session.Update();
  127. if (paused) //in case if Config.UpdateMode.LatestCameraImage is not used
  128. return;
  129. var camera = frame.Camera;
  130. if (camera.TrackingState != TrackingState.Tracking)
  131. return;
  132. var far = 100f;
  133. var near = 0.01f;
  134. float[] projmx = new float[16];
  135. camera.GetProjectionMatrix(projmx, 0, near, far);
  136. var prj = new Urho.Matrix4(
  137. projmx[0], projmx[4], projmx[8], projmx[12],
  138. projmx[1], projmx[5], projmx[9], projmx[13],
  139. projmx[2], projmx[6], projmx[10], projmx[14],
  140. projmx[3], projmx[7], projmx[11], projmx[15]
  141. );
  142. //some OGL -> DX conversion (Urho accepts DX format on all platforms)
  143. prj.M34 /= 2f;
  144. prj.M33 = far / (far - near);
  145. prj.M43 *= -1;
  146. //prj.M13 = 0; //center of projection
  147. //prj.M23 = 0;
  148. Camera.SetProjection(prj);
  149. TransformByPose(Camera.Node, camera.DisplayOrientedPose);
  150. ARFrameUpdated?.Invoke(frame);
  151. }
  152. catch (Exception exc)
  153. {
  154. Log.Write(LogLevel.Warning, "ARCore error: " + exc);
  155. }
  156. }
  157. public static void TransformByPose(Node node, Pose pose)
  158. {
  159. // Right-Handed coordinate system to Left-Handed
  160. node.Rotation = new Quaternion(pose.Qx(), pose.Qy(), -pose.Qz(), -pose.Qw());
  161. node.Position = new Vector3(pose.Tx(), pose.Ty(), -pose.Tz());
  162. }
  163. }
  164. }