SpatialMappingManager.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.InteropServices.WindowsRuntime;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Windows.Graphics.DirectX;
  9. using Windows.Perception.Spatial;
  10. using Windows.Perception.Spatial.Surfaces;
  11. using Urho.SharpReality;
  12. namespace Urho
  13. {
  14. public class SpatialMappingManager
  15. {
  16. SpatialSurfaceObserver observer;
  17. SpatialCoordinateSystem currentCoordinateSystem;
  18. readonly SemaphoreSlim spatialObserverSemaphore = new SemaphoreSlim(1);
  19. static readonly Dictionary<Guid, DateTimeOffset> UpdateCache = new Dictionary<Guid, DateTimeOffset>();
  20. StereoApplication currentHoloApp;
  21. int trianglesPerCubicMeter;
  22. public Color DefaultColor { get; set; } = Color.Transparent;
  23. public bool ConvertToLeftHanded { get; set; }
  24. public bool OnlyAdd { get; set; }
  25. SpatialSurfaceMeshOptions options = new SpatialSurfaceMeshOptions
  26. {
  27. //TODO: check if supported?
  28. VertexPositionFormat = DirectXPixelFormat.R32G32B32A32Float,
  29. VertexNormalFormat = DirectXPixelFormat.R32G32B32A32Float,
  30. TriangleIndexFormat = DirectXPixelFormat.R16UInt,
  31. IncludeVertexNormals = true,
  32. };
  33. public async Task<bool> Register(StereoApplication app, SpatialCoordinateSystem coordinateSystem, System.Numerics.Vector3 extents, int trianglesPerCubicMeter = 1000, bool onlyAdd = false, bool convertToLeftHanded = true)
  34. {
  35. this.currentHoloApp = app;
  36. this.trianglesPerCubicMeter = trianglesPerCubicMeter;
  37. this.currentCoordinateSystem = coordinateSystem;
  38. ConvertToLeftHanded = convertToLeftHanded;
  39. OnlyAdd = onlyAdd;
  40. var result = await SpatialSurfaceObserver.RequestAccessAsync();
  41. if (result != SpatialPerceptionAccessStatus.Allowed)
  42. return false;
  43. observer = new SpatialSurfaceObserver();
  44. observer.SetBoundingVolume(SpatialBoundingVolume.FromBox(coordinateSystem, new SpatialBoundingBox { Extents = extents }));
  45. foreach (var item in observer.GetObservedSurfaces())
  46. {
  47. lock (UpdateCache)
  48. {
  49. UpdateCache[item.Key] = item.Value.UpdateTime.ToUniversalTime();
  50. }
  51. ProcessSurface(item.Value);
  52. }
  53. observer.ObservedSurfacesChanged += Observer_ObservedSurfacesChanged;
  54. return true;
  55. }
  56. public void Stop()
  57. {
  58. if (observer != null)
  59. observer.ObservedSurfacesChanged -= Observer_ObservedSurfacesChanged;
  60. observer = null;
  61. }
  62. async void Observer_ObservedSurfacesChanged(SpatialSurfaceObserver sender, object args)
  63. {
  64. foreach (var item in sender.GetObservedSurfaces())
  65. {
  66. lock (UpdateCache)
  67. {
  68. DateTimeOffset updateTime;
  69. var time = item.Value.UpdateTime.ToUniversalTime();
  70. if (UpdateCache.TryGetValue(item.Key, out updateTime) && (updateTime >= time || OnlyAdd))
  71. continue;
  72. UpdateCache[item.Key] = time;
  73. }
  74. if (observer == null)
  75. return;
  76. await ProcessSurface(item.Value).ConfigureAwait(false);
  77. }
  78. }
  79. void RemoveSurfaceFromCache(Guid surfaceId)
  80. {
  81. lock (UpdateCache)
  82. UpdateCache.Remove(surfaceId);
  83. }
  84. async Task ProcessSurface(SpatialSurfaceInfo surface)
  85. {
  86. var mesh = await surface.TryComputeLatestMeshAsync(trianglesPerCubicMeter, options).AsTask().ConfigureAwait(false);
  87. if (observer == null || mesh == null)
  88. {
  89. RemoveSurfaceFromCache(surface.Id);
  90. return;
  91. }
  92. var bounds = mesh.SurfaceInfo.TryGetBounds(currentCoordinateSystem);
  93. if (bounds == null)
  94. {
  95. RemoveSurfaceFromCache(surface.Id);
  96. return;
  97. }
  98. var transform = mesh.CoordinateSystem.TryGetTransformTo(currentCoordinateSystem);
  99. if (transform == null)
  100. {
  101. RemoveSurfaceFromCache(surface.Id);
  102. return;
  103. }
  104. //1. TriangleIndices
  105. var trianglesBytes = mesh.TriangleIndices.Data.ToArray();
  106. var indeces = new short[mesh.TriangleIndices.ElementCount];
  107. int indexOffset = 0;
  108. for (int i = 0; i < mesh.TriangleIndices.ElementCount; i++)
  109. {
  110. //DirectXPixelFormat.R16UInt
  111. var index = BitConverter.ToInt16(trianglesBytes, indexOffset);
  112. indexOffset += 2;
  113. indeces[i] = index;
  114. }
  115. var vertexCount = mesh.VertexPositions.ElementCount;
  116. var vertexRawData = mesh.VertexPositions.Data.ToArray();
  117. var vertexScale = mesh.VertexPositionScale;
  118. var normalsRawData = mesh.VertexNormals.Data.ToArray();
  119. var vertexColor = DefaultColor.ToUInt();
  120. var vertexData = new SpatialVertex[vertexCount];
  121. var boundsRotation = new Quaternion(-bounds.Value.Orientation.X, -bounds.Value.Orientation.Y, bounds.Value.Orientation.Z, bounds.Value.Orientation.W);
  122. var boundsCenter = new Vector3(bounds.Value.Center.X, bounds.Value.Center.Y, -bounds.Value.Center.Z);
  123. var boundsExtents = new Vector3(bounds.Value.Extents.X, bounds.Value.Extents.Y, bounds.Value.Extents.Z);
  124. var transformValue = transform.Value;
  125. Matrix4 transformUrhoMatrix;
  126. unsafe { transformUrhoMatrix = *(Matrix4*)(void*)&transformValue; }
  127. //these values won't change, let's declare them as consts
  128. const int vertexStride = 16; // (int) mesh.VertexPositions.Stride;
  129. const int normalStride = 16; // (int) mesh.VertexNormals.Stride;
  130. //2,3 - VertexPositions and Normals
  131. for (int i = 0; i < vertexCount; i++)
  132. {
  133. var positionX = BitConverter.ToSingle(vertexRawData, i * vertexStride + 0);
  134. var positionY = BitConverter.ToSingle(vertexRawData, i * vertexStride + 4); //4 per X,Y,Z,W (stride is 16)
  135. var positionZ = BitConverter.ToSingle(vertexRawData, i * vertexStride + 8); //also, we don't need the W component.
  136. var normalX = BitConverter.ToSingle(normalsRawData, i * normalStride + 0);
  137. var normalY = BitConverter.ToSingle(normalsRawData, i * normalStride + 4);
  138. var normalZ = BitConverter.ToSingle(normalsRawData, i * normalStride + 8);
  139. //merge vertex+normals for Urho3D (also, change RH to LH coordinate systems)
  140. vertexData[i].PositionX = positionX * vertexScale.X;
  141. vertexData[i].PositionY = positionY * vertexScale.Y;
  142. vertexData[i].PositionZ = - positionZ * vertexScale.Z;
  143. vertexData[i].NormalX = normalX;
  144. vertexData[i].NormalY = normalY;
  145. vertexData[i].NormalZ = - normalZ;
  146. //Vertex color (for VCol techniques)
  147. vertexData[i].ColorUint = vertexColor;
  148. }
  149. var surfaceInfo = new SharpReality.SpatialMeshInfo
  150. {
  151. SurfaceId = surface.Id.ToString(),
  152. Date = surface.UpdateTime,
  153. VertexData = vertexData,
  154. IndexData = indeces,
  155. BoundsCenter = boundsCenter,
  156. BoundsRotation = boundsRotation,
  157. Extents = boundsExtents,
  158. Transform = transformUrhoMatrix,
  159. };
  160. currentHoloApp.HandleSurfaceUpdated(surfaceInfo);
  161. }
  162. }
  163. }