forked from microsoft/MixedRealityToolkit-Unity
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSpatialUnderstandingDll.cs
More file actions
495 lines (455 loc) · 24.3 KB
/
SpatialUnderstandingDll.cs
File metadata and controls
495 lines (455 loc) · 24.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
namespace HoloToolkit.Unity
{
/// <summary>
/// Encapsulates the primary dll functions, including marshalling helper functions.
/// The dll functions are organized into four parts - in this behavior,
/// SpatialUnderstandingDllTopology, SpatialUnderstandingDllShapes, and
/// SpatialUnderstandingDllObjectPlacement. The scan flow, raycast, and alignment
/// functions are included in this class.
/// </summary>
public class SpatialUnderstandingDll
{
/// <summary>
/// Representation of the mesh data to be passed to the understanding dll.
/// Used by SpatialUnderstandingSourceMesh to store local copies of the mesh data.
/// </summary>
public struct MeshData
{
public int MeshID;
public int LastUpdateID;
public Matrix4x4 Transform;
public Vector3[] Verts;
public Vector3[] Normals;
public Int32[] Indices;
public MeshData(MeshFilter meshFilter)
{
MeshID = 0;
LastUpdateID = 0;
Transform = meshFilter.transform.localToWorldMatrix;
Verts = meshFilter.sharedMesh.vertices;
Normals = meshFilter.sharedMesh.normals;
Indices = meshFilter.sharedMesh.triangles;
}
public void CopyFrom(MeshFilter meshFilter, int meshID = 0, int lastUpdateID = 0)
{
MeshID = meshID;
LastUpdateID = lastUpdateID;
if (meshFilter != null)
{
Transform = meshFilter.transform.localToWorldMatrix;
// Note that we are assuming that Unity's getters for vertices/normals/triangles make
// copies of the array. As of unity 5.4 this assumption is correct.
Verts = meshFilter.sharedMesh.vertices;
Normals = meshFilter.sharedMesh.normals;
Indices = meshFilter.sharedMesh.triangles;
}
}
}
// Privates
private Imports.MeshData[] reusedMeshesForMarshalling = null;
private List<GCHandle> reusedPinnedMemoryHandles = new List<GCHandle>();
private Imports.RaycastResult reusedRaycastResult = new Imports.RaycastResult();
private IntPtr reusedRaycastResultPtr;
private Imports.PlayspaceStats reusedPlayspaceStats = new Imports.PlayspaceStats();
private IntPtr reusedPlayspaceStatsPtr;
private Imports.PlayspaceAlignment reusedPlayspaceAlignment = new Imports.PlayspaceAlignment();
private IntPtr reusedPlayspaceAlignmentPtr;
private SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult reusedObjectPlacementResult = new SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult();
private IntPtr reusedObjectPlacementResultPtr;
/// <summary>
/// Pins the specified object so that the backing memory can not be relocated, adds the pinned
/// memory handle to the tracking list, and then returns that address of the pinned memory so
/// that it can be passed into the DLL to be access directly from native code.
/// </summary>
public IntPtr PinObject(System.Object obj)
{
GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
reusedPinnedMemoryHandles.Add(h);
return h.AddrOfPinnedObject();
}
/// <summary>
/// Pins the string, converting to the format expected by the dll. See PinObject for
/// additional details.
/// </summary>
public IntPtr PinString(string str)
{
byte[] obj = System.Text.Encoding.ASCII.GetBytes(str);
GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
reusedPinnedMemoryHandles.Add(h);
return h.AddrOfPinnedObject();
}
/// <summary>
/// Unpins all of the memory previously pinned by calls to PinObject().
/// </summary>
public void UnpinAllObjects()
{
for (int i = 0; i < reusedPinnedMemoryHandles.Count; ++i)
{
reusedPinnedMemoryHandles[i].Free();
}
reusedPinnedMemoryHandles.Clear();
}
/// <summary>
/// Copies the supplied mesh data into the reusedMeshesForMarhsalling array. All managed arrays
/// are pinned so that the marshalling only needs to pass a pointer and the native code can
/// reference the memory in place without needing the marshaller to create a complete copy of
/// the data.
/// </summary>
public IntPtr PinMeshDataForMarshalling(List<MeshData> meshes)
{
// if we have a big enough array reuse it, otherwise create new
if (reusedMeshesForMarshalling == null || reusedMeshesForMarshalling.Length < meshes.Count)
{
reusedMeshesForMarshalling = new Imports.MeshData[meshes.Count];
}
for (int i = 0; i < meshes.Count; ++i)
{
IntPtr pinnedVerts = (meshes[i].Verts != null) && (meshes[i].Verts.Length > 0) ? PinObject(meshes[i].Verts) : IntPtr.Zero;
IntPtr pinnedNormals = (meshes[i].Verts != null) && (meshes[i].Verts.Length > 0) ? PinObject(meshes[i].Normals) : IntPtr.Zero;
IntPtr pinnedIndices = (meshes[i].Indices != null) && (meshes[i].Indices.Length > 0) ? PinObject(meshes[i].Indices) : IntPtr.Zero;
reusedMeshesForMarshalling[i] = new Imports.MeshData()
{
meshID = meshes[i].MeshID,
lastUpdateID = meshes[i].LastUpdateID,
transform = meshes[i].Transform,
vertCount = (meshes[i].Verts != null) ? meshes[i].Verts.Length : 0,
indexCount = (meshes[i].Indices != null) ? meshes[i].Indices.Length : 0,
verts = pinnedVerts,
normals = pinnedNormals,
indices = pinnedIndices,
};
}
return PinObject(reusedMeshesForMarshalling);
}
/// <summary>
/// Reusable raycast result object pointer. Can be used for inline raycast calls.
/// </summary>
/// <returns>Raycast result pointer</returns>
public IntPtr GetStaticRaycastResultPtr()
{
if (reusedRaycastResultPtr == IntPtr.Zero)
{
GCHandle h = GCHandle.Alloc(reusedRaycastResult, GCHandleType.Pinned);
reusedRaycastResultPtr = h.AddrOfPinnedObject();
}
return reusedRaycastResultPtr;
}
/// <summary>
/// Resuable raycast result object. Can be used for inline raycast calls.
/// </summary>
/// <returns>Raycast result structure</returns>
public Imports.RaycastResult GetStaticRaycastResult()
{
return reusedRaycastResult;
}
/// <summary>
/// Resuable playspace statistics pointer. Can be used for inline playspace statistics calls.
/// </summary>
/// <returns>playspace statistics pointer</returns>
public IntPtr GetStaticPlayspaceStatsPtr()
{
if (reusedPlayspaceStatsPtr == IntPtr.Zero)
{
GCHandle h = GCHandle.Alloc(reusedPlayspaceStats, GCHandleType.Pinned);
reusedPlayspaceStatsPtr = h.AddrOfPinnedObject();
}
return reusedPlayspaceStatsPtr;
}
/// <summary>
/// Resuable playspace statistics. Can be used for inline playspace statistics calls.
/// </summary>
/// <returns>playspace statistics structure</returns>
public Imports.PlayspaceStats GetStaticPlayspaceStats()
{
return reusedPlayspaceStats;
}
/// <summary>
/// Resuable playspace alignment pointer. Can be used for inline playspace alignment query calls.
/// </summary>
/// <returns>playspace alignment pointer</returns>
public IntPtr GetStaticPlayspaceAlignmentPtr()
{
if (reusedPlayspaceAlignmentPtr == IntPtr.Zero)
{
GCHandle h = GCHandle.Alloc(reusedPlayspaceAlignment, GCHandleType.Pinned);
reusedPlayspaceAlignmentPtr = h.AddrOfPinnedObject();
}
return reusedPlayspaceAlignmentPtr;
}
/// <summary>
/// Resuable playspace alignment. Can be used for inline playspace alignment query calls.
/// </summary>
/// <returns>playspace alignment structure</returns>
public Imports.PlayspaceAlignment GetStaticPlayspaceAlignment()
{
return reusedPlayspaceAlignment;
}
/// <summary>
/// Resuable object placement results pointer. Can be used for inline object placement queries.
/// </summary>
/// <returns>Object placement result pointer</returns>
public IntPtr GetStaticObjectPlacementResultPtr()
{
if (reusedObjectPlacementResultPtr == IntPtr.Zero)
{
GCHandle h = GCHandle.Alloc(reusedObjectPlacementResult, GCHandleType.Pinned);
reusedObjectPlacementResultPtr = h.AddrOfPinnedObject();
}
return reusedObjectPlacementResultPtr;
}
/// <summary>
/// Resuable object placement results. Can be used for inline object placement queries.
/// </summary>
/// <returns>Object placement result structure</returns>
public SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult GetStaticObjectPlacementResult()
{
return reusedObjectPlacementResult;
}
/// <summary>
/// Marshals BoundedPlane data returned from a DLL API call into a managed BoundedPlane array
/// and then frees the memory that was allocated within the DLL.
/// </summary>
public T[] MarshalArrayFromIntPtr<T>(IntPtr outArray, int count)
{
T[] resultArray = new T[count];
int structSize =
#if UNITY_EDITOR || !UNITY_WSA
Marshal.SizeOf(typeof(T));
#else
Marshal.SizeOf<T>();
#endif
IntPtr current = outArray;
try
{
for (int i = 0; i < count; i++)
{
resultArray[i] =
#if UNITY_EDITOR || !UNITY_WSA
(T)Marshal.PtrToStructure(current, typeof(T));
#else
Marshal.PtrToStructure<T>(current);
#endif
current = (IntPtr)((long)current + structSize);
}
}
finally
{
Marshal.FreeCoTaskMem(outArray);
}
return resultArray;
}
public class Imports
{
/// <summary>
/// Mesh input data passed to the dll
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MeshData
{
public int meshID;
public int lastUpdateID;
public Matrix4x4 transform;
public Int32 vertCount;
public Int32 indexCount;
public IntPtr verts;
public IntPtr normals;
public IntPtr indices;
};
/// <summary>
/// Playspace statistics for querying scanning progress
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class PlayspaceStats
{
public int IsWorkingOnStats; // 0 if still working on creating the stats
public float HorizSurfaceArea; // In m2 : All horizontal faces UP between Ground – 0.15 and Ground + 1.f (include Ground and convenient horiz surface)
public float TotalSurfaceArea; // In m2 : All !
public float UpSurfaceArea; // In m2 : All horizontal faces UP (no constraint => including ground)
public float DownSurfaceArea; // In m2 : All horizontal faces DOWN (no constraint => including ceiling)
public float WallSurfaceArea; // In m2 : All Vertical faces (not only walls)
public float VirtualCeilingSurfaceArea; // In m2 : estimation of surface of virtual Ceiling.
public float VirtualWallSurfaceArea; // In m2 : estimation of surface of virtual Walls.
public int NumFloor; // List of Area of each Floor surface (contains count)
public int NumCeiling; // List of Area of each Ceiling surface (contains count)
public int NumWall_XNeg; // List of Area of each Wall XNeg surface (contains count)
public int NumWall_XPos; // List of Area of each Wall XPos surface (contains count)
public int NumWall_ZNeg; // List of Area of each Wall ZNeg surface (contains count)
public int NumWall_ZPos; // List of Area of each Wall ZPos surface (contains count)
public int NumPlatform; // List of Area of each Horizontal not Floor surface (contains count)
public int CellCount_IsPaintMode; // Number paint cells (could deduce surface of painted area) => 8cm x 8cm cell
public int CellCount_IsSeenQualtiy_None; // Number of not seen cells => 8cm x 8cm cell
public int CellCount_IsSeenQualtiy_Seen; // Number of seen cells => 8cm x 8cm cell
public int CellCount_IsSeenQualtiy_Good; // Number of seen cells good quality => 8cm x 8cm cell
};
/// <summary>
/// Playspace alignment results. Reports internal alignment of room in Unity space and basic alignment of the room.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class PlayspaceAlignment
{
public Vector3 Center;
public Vector3 HalfDims;
public Vector3 BasisX;
public Vector3 BasisY;
public Vector3 BasisZ;
public float FloorYValue;
public float CeilingYValue;
};
/// <summary>
/// Result structure returns from a raycast call.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class RaycastResult
{
public enum SurfaceTypes
{
Invalid, // If no intersection
Other,
Floor,
FloorLike, // Not part of the floor topology, but close to the floor and looks like the floor
Platform, // Horizontal platform between the ground and the ceiling
Ceiling,
WallExternal,
WallLike, // Not part of the external wall surface
};
public SurfaceTypes SurfaceType;
float SurfaceArea; // Zero if unknown (not part of the topology analysis)
public Vector3 IntersectPoint;
public Vector3 IntersectNormal;
};
// Functions
/// <summary>
/// Initialize the spatial understanding dll. Function must be called
/// before any other dll function.
/// </summary>
/// <returns>Zero if fails, one if success</returns>
[DllImport("SpatialUnderstanding")]
public static extern int SpatialUnderstanding_Init();
/// <summary>
/// Terminate the spatial understanding dll.
/// </summary>
[DllImport("SpatialUnderstanding")]
public static extern void SpatialUnderstanding_Term();
/// <summary>
/// Initialize the scanning process.
/// </summary>
/// <param name="camPos_X">The user's camera/view position, x value</param>
/// <param name="camPos_Y">The user's camera/view position, y value</param>
/// <param name="camPos_Z">The user's camera/view position, z value</param>
/// <param name="camFwd_X">The user's camera/view unit forward vector, x value</param>
/// <param name="camFwd_Y">The user's camera/view unit forward vector, y value</param>
/// <param name="camFwd_Z">The user's camera/view unit forward vector, z value</param>
/// <param name="camUp_X">The user's camera/view unit up vector, x value</param>
/// <param name="camUp_Y">The user's camera/view unit up vector, y value</param>
/// <param name="camUp_Z">The user's camera/view unit up vector, z value</param>
/// <param name="searchDst">Suggested search distance for playspace center</param>
/// <param name="optimalSize">Optimal room size. Used to determind the playspace size</param>
[DllImport("SpatialUnderstanding")]
public static extern void GeneratePlayspace_InitScan(
[In] float camPos_X, [In] float camPos_Y, [In] float camPos_Z,
[In] float camFwd_X, [In] float camFwd_Y, [In] float camFwd_Z,
[In] float camUp_X, [In] float camUp_Y, [In] float camUp_Z,
[In] float searchDst, [In] float optimalSize);
/// <summary>
/// Update the playspace scanning. Should be called once per frame during scanning.
/// </summary>
/// <param name="meshCount">Number of meshes passed in the meshes parameter</param>
/// <param name="meshes">Array of meshes</param>
/// <param name="camPos_X">The user's camera/view position, x value</param>
/// <param name="camPos_Y">The user's camera/view position, y value</param>
/// <param name="camPos_Z">The user's camera/view position, z value</param>
/// <param name="camFwd_X">The user's camera/view unit forward vector, x value</param>
/// <param name="camFwd_Y">The user's camera/view unit forward vector, y value</param>
/// <param name="camFwd_Z">The user's camera/view unit forward vector, z value</param>
/// <param name="camUp_X">The user's camera/view unit up vector, x value</param>
/// <param name="camUp_Y">The user's camera/view unit up vector, y value</param>
/// <param name="camUp_Z">The user's camera/view unit up vector, z value</param>
/// <param name="deltaTime">Time since last update</param>
/// <returns>One if scanning has been finalized, zero if more updates are required.</returns>
[DllImport("SpatialUnderstanding")]
public static extern int GeneratePlayspace_UpdateScan(
[In] int meshCount, [In] IntPtr meshes,
[In] float camPos_X, [In] float camPos_Y, [In] float camPos_Z,
[In] float camFwd_X, [In] float camFwd_Y, [In] float camFwd_Z,
[In] float camUp_X, [In] float camUp_Y, [In] float camUp_Z,
[In] float deltaTime);
/// <summary>
/// Request scanning that the scanning phase be ended and the playspace
/// finalized. This should be called once the user is happy with the currently
/// scanned in playspace.
/// </summary>
[DllImport("SpatialUnderstanding")]
public static extern void GeneratePlayspace_RequestFinish();
/// <summary>
/// Extracting the mesh is a two step process, the first generates the mesh for extraction & saves it off.
/// The caller is able to see vertex counts, etc. so they can allocate the proper amount of memory.
/// The second call, the caller provides buffers of the appropriate size (or larger), passing in the
/// buffer sizes for validation.
/// </summary>
/// <param name="vertexCount">Filled in with the number of vertices to be returned in the subsequent extract call</param>
/// <param name="indexCount">Filled in with the number of indices to be returned in the subsequent extract call</param>
/// <returns>Zero if fails, one if success</returns>
[DllImport("SpatialUnderstanding")]
public static extern int GeneratePlayspace_ExtractMesh_Setup(
[Out] out int vertexCount,
[Out] out int indexCount);
/// <summary>
/// Call to receive the dll's custom generated mesh data. Use GeneratePlayspace_ExtractMesh_Setup to
/// query the minimum size of the vertex positions, normals, and indices.
/// </summary>
/// <param name="bufferVertexCount">Size of vericesPos & verticesNormal, in number Vector3 elements in each array</param>
/// <param name="verticesPos">Array to receive the vertex positions</param>
/// <param name="verticesNormal">Array to receive vertex normals</param>
/// <param name="bufferIndexCount">Size of indices, in number of elements</param>
/// <param name="indices">Array to receive the mesh indices</param>
/// <returns>Zero if fails, one if success</returns>
[DllImport("SpatialUnderstanding")]
public static extern int GeneratePlayspace_ExtractMesh_Extract(
[In] int bufferVertexCount,
[In] IntPtr verticesPos, // (vertexCount) DirectX::XMFLOAT3*
[In] IntPtr verticesNormal, // (vertexCount) DirectX::XMFLOAT3*
[In] int bufferIndexCount,
[In] IntPtr indices); // (indexCount) INT32*
/// <summary>
/// Query the playspace scan statistics.
/// </summary>
/// <param name="playspaceStats">playspace stats structure to receive the statistics data</param>
/// <returns>Zero if fails, one if success</returns>
[DllImport("SpatialUnderstanding")]
public static extern int QueryPlayspaceStats(
[In] IntPtr playspaceStats); // PlayspaceStats
/// <summary>
/// Query the playspace alignment data. This will not be valid until after scanning is finalized.
/// </summary>
/// <param name="playspaceAlignment">playspace alignment structure to receive the alignment data</param>
/// <returns>Zero if fails, one if success</returns>
[DllImport("SpatialUnderstanding")]
public static extern int QueryPlayspaceAlignment(
[In] IntPtr playspaceAlignment); // PlayspaceAlignment
/// <summary>
/// Perform a raycast against the internal world representation of the understanding dll.
/// This will not be valid until after scanning is finalized.
/// </summary>
/// <param name="rayPos_X">Ray origin, x component</param>
/// <param name="rayPos_Y">Ray origin, y component</param>
/// <param name="rayPos_Z">Ray origin, z component</param>
/// <param name="rayVec_X">Ray direction vector, x component. Length of ray indicates the length of the ray cast query.</param>
/// <param name="rayVec_Y">Ray direction vector, y component. Length of ray indicates the length of the ray cast query.</param>
/// <param name="rayVec_Z">Ray direction vector, z component. Length of ray indicates the length of the ray cast query.</param>
/// <param name="result">Structure to receive the results of the raycast</param>
/// <returns>Zero if fails or no intersection, one if an intersection is detected</returns>
[DllImport("SpatialUnderstanding")]
public static extern int PlayspaceRaycast(
[In] float rayPos_X, [In] float rayPos_Y, [In] float rayPos_Z,
[In] float rayVec_X, [In] float rayVec_Y, [In] float rayVec_Z,
[In] IntPtr result); // RaycastResult
}
}
}