AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
LevelSolver.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
4 using UnityEngine;
5 using System.Collections;
6 using HoloToolkit.Unity;
7 using System.Collections.Generic;
8 using System.Runtime.InteropServices;
9 using System;
10 
11 namespace HoloToolkit.Examples.SpatialUnderstandingFeatureOverview
12 {
13  public class LevelSolver : LineDrawer
14  {
15  // Singleton
16  public static LevelSolver Instance;
17 
18  // Enums
19  public enum QueryStates
20  {
21  None,
22  Processing,
23  Finished
24  }
25 
26  // Structs
27  private struct QueryStatus
28  {
29  public void Reset()
30  {
31  State = QueryStates.None;
32  Name = "";
33  CountFail = 0;
34  CountSuccess = 0;
36  }
37 
38  public QueryStates State;
39  public string Name;
40  public int CountFail;
41  public int CountSuccess;
43  }
44 
45  private struct PlacementQuery
46  {
47  public PlacementQuery(
49  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
50  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null)
51  {
52  PlacementDefinition = placementDefinition;
53  PlacementRules = placementRules;
54  PlacementConstraints = placementConstraints;
55  }
56 
59  public List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> PlacementConstraints;
60  }
61 
62  private class PlacementResult
63  {
64  public PlacementResult(float timeDelay, SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult result)
65  {
66  Box = new AnimatedBox(timeDelay, result.Position, Quaternion.LookRotation(result.Forward, result.Up), Color.blue, result.HalfDims);
67  Result = result;
68  }
69 
70  public LineDrawer.AnimatedBox Box;
72  }
73 
74  // Properties
75  public bool IsSolverInitialized { get; private set; }
76 
77  // Privates
78  private List<PlacementResult> placementResults = new List<PlacementResult>();
79  private QueryStatus queryStatus = new QueryStatus();
80 
81  // Functions
82  private void Awake()
83  {
84  Instance = this;
85  }
86 
87  public void ClearGeometry(bool clearAll = true)
88  {
89  placementResults.Clear();
90  if (SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
91  {
93  }
94  AppState.Instance.ObjectPlacementDescription = "";
95 
96  if (clearAll && (SpaceVisualizer.Instance != null))
97  {
99  }
100  }
101 
102  private bool Draw_PlacementResults()
103  {
104  bool needsUpdate = false;
105 
106  for (int i = 0; i < placementResults.Count; ++i)
107  {
108  needsUpdate |= Draw_AnimatedBox(placementResults[i].Box);
109  }
110 
111  return needsUpdate;
112  }
113 
114  private bool PlaceObjectAsync(
115  string placementName,
117  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
118  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null,
119  bool clearObjectsFirst = true)
120  {
121  return PlaceObjectAsync(
122  placementName,
123  new List<PlacementQuery>() { new PlacementQuery(placementDefinition, placementRules, placementConstraints) },
124  clearObjectsFirst);
125  }
126 
127  private bool PlaceObjectAsync(
128  string placementName,
129  List<PlacementQuery> placementList,
130  bool clearObjectsFirst = true)
131  {
132  // If we already mid-query, reject the request
133  if (queryStatus.State != QueryStates.None)
134  {
135  return false;
136  }
137 
138  // Clear geo
139  if (clearObjectsFirst)
140  {
141  ClearGeometry();
142  }
143 
144  // Mark it
145  queryStatus.Reset();
146  queryStatus.State = QueryStates.Processing;
147  queryStatus.Name = placementName;
148 
149  // Tell user we are processing
150  AppState.Instance.ObjectPlacementDescription = placementName + " (processing)";
151 
152  // Kick off a thread to do process the queries
153 #if UNITY_EDITOR || !UNITY_WSA
154  new System.Threading.Thread
155 #else
156  System.Threading.Tasks.Task.Run
157 #endif
158  (() =>
159  {
160  // Go through the queries in the list
161  for (int i = 0; i < placementList.Count; ++i)
162  {
163  // Do the query
164  bool success = PlaceObject(
165  placementName,
166  placementList[i].PlacementDefinition,
167  placementList[i].PlacementRules,
168  placementList[i].PlacementConstraints,
169  clearObjectsFirst,
170  true);
171 
172  // Mark the result
173  queryStatus.CountSuccess = success ? (queryStatus.CountSuccess + 1) : queryStatus.CountSuccess;
174  queryStatus.CountFail = !success ? (queryStatus.CountFail + 1) : queryStatus.CountFail;
175  }
176 
177  // Done
178  queryStatus.State = QueryStates.Finished;
179  }
180  )
181 #if UNITY_EDITOR || !UNITY_WSA
182  .Start()
183 #endif
184  ;
185 
186  return true;
187  }
188 
189  private bool PlaceObject(
190  string placementName,
192  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null,
193  List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null,
194  bool clearObjectsFirst = true,
195  bool isASync = false)
196  {
197  // Clear objects (if requested)
198  if (!isASync && clearObjectsFirst)
199  {
200  ClearGeometry();
201  }
202  if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
203  {
204  return false;
205  }
206 
207  // New query
209  placementName,
210  SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementDefinition),
211  (placementRules != null) ? placementRules.Count : 0,
212  ((placementRules != null) && (placementRules.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementRules.ToArray()) : IntPtr.Zero,
213  (placementConstraints != null) ? placementConstraints.Count : 0,
214  ((placementConstraints != null) && (placementConstraints.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementConstraints.ToArray()) : IntPtr.Zero,
215  SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResultPtr()) > 0)
216  {
217  SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult placementResult = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResult();
218  if (!isASync)
219  {
220  // If not running async, we can just add the results to the draw list right now
221  AppState.Instance.ObjectPlacementDescription = placementName + " (1)";
222  float timeDelay = (float)placementResults.Count * AnimatedBox.DelayPerItem;
223  placementResults.Add(new PlacementResult(timeDelay, placementResult.Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult));
224  }
225  else
226  {
227  queryStatus.QueryResult.Add(placementResult.Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult);
228  }
229  return true;
230  }
231  if (!isASync)
232  {
233  AppState.Instance.ObjectPlacementDescription = placementName + " (0)";
234  }
235  return false;
236  }
237 
238  private void ProcessPlacementResults()
239  {
240  // Check it
241  if (queryStatus.State != QueryStates.Finished)
242  {
243  return;
244  }
245  if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
246  {
247  return;
248  }
249 
250  // Clear results
251  ClearGeometry();
252 
253  // We will reject any above or below the ceiling/floor
254  SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
255  SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();
256 
257  // Copy over the results
258  for (int i = 0; i < queryStatus.QueryResult.Count; ++i)
259  {
260  if ((queryStatus.QueryResult[i].Position.y < alignment.CeilingYValue) &&
261  (queryStatus.QueryResult[i].Position.y > alignment.FloorYValue))
262  {
263  float timeDelay = (float)placementResults.Count * AnimatedBox.DelayPerItem;
264  placementResults.Add(new PlacementResult(timeDelay, queryStatus.QueryResult[i].Clone() as SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult));
265  }
266  }
267 
268  // Text
269  AppState.Instance.ObjectPlacementDescription = queryStatus.Name + " (" + placementResults.Count + "/" + (queryStatus.CountSuccess + queryStatus.CountFail) + ")";
270 
271  // Mark done
272  queryStatus.Reset();
273  }
274 
275  public void Query_OnFloor()
276  {
277  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
278  for (int i = 0; i < 4; ++i)
279  {
280  float halfDimSize = UnityEngine.Random.Range(0.15f, 0.35f);
281  placementQuery.Add(
282  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize * 2.0f)),
285  }));
286  }
287  PlaceObjectAsync("OnFloor", placementQuery);
288  }
289 
290  public void Query_OnWall()
291  {
292  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
293  for (int i = 0; i < 6; ++i)
294  {
295  float halfDimSize = UnityEngine.Random.Range(0.3f, 0.6f);
296  placementQuery.Add(
297  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnWall(new Vector3(halfDimSize, halfDimSize * 0.5f, 0.05f), 0.5f, 3.0f),
300  }));
301  }
302  PlaceObjectAsync("OnWall", placementQuery);
303  }
304 
305  public void Query_OnCeiling()
306  {
307  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
308  for (int i = 0; i < 2; ++i)
309  {
310  float halfDimSize = UnityEngine.Random.Range(0.3f, 0.4f);
311  placementQuery.Add(
312  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnCeiling(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
315  }));
316  }
317  PlaceObjectAsync("OnCeiling", placementQuery);
318  }
319 
320  public void Query_OnEdge()
321  {
322  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
323  for (int i = 0; i < 8; ++i)
324  {
325  float halfDimSize = UnityEngine.Random.Range(0.05f, 0.1f);
326  placementQuery.Add(
327  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnEdge(new Vector3(halfDimSize, halfDimSize, halfDimSize),
328  new Vector3(halfDimSize, halfDimSize, halfDimSize)),
331  }));
332  }
333  PlaceObjectAsync("OnEdge", placementQuery);
334  }
335 
337  {
338  SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
339  SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();
340  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
341  for (int i = 0; i < 4; ++i)
342  {
343  float halfDimSize = UnityEngine.Random.Range(0.1f, 0.2f);
344  placementQuery.Add(
345  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloorAndCeiling(new Vector3(halfDimSize, (alignment.CeilingYValue - alignment.FloorYValue) * 0.5f, halfDimSize),
346  new Vector3(halfDimSize, (alignment.CeilingYValue - alignment.FloorYValue) * 0.5f, halfDimSize)),
349  }));
350  }
351  PlaceObjectAsync("OnFloorAndCeiling", placementQuery);
352  }
353 
355  {
356  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
357  for (int i = 0; i < 8; ++i)
358  {
359  float halfDimSize = UnityEngine.Random.Range(0.1f, 0.2f);
360  placementQuery.Add(
361  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_RandomInAir(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
365  }));
366  }
367  PlaceObjectAsync("RandomInAir - AwayFromMe", placementQuery);
368  }
369 
371  {
372  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
373  for (int i = 0; i < 4; ++i)
374  {
375  float halfDimSize = UnityEngine.Random.Range(0.05f, 0.1f);
376  placementQuery.Add(
377  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnEdge(new Vector3(halfDimSize, halfDimSize, halfDimSize), new Vector3(halfDimSize, halfDimSize, halfDimSize)),
380  },
383  }));
384  }
385  PlaceObjectAsync("OnEdge - NearCenter", placementQuery);
386 
387  }
388 
390  {
391  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
392  for (int i = 0; i < 4; ++i)
393  {
394  float halfDimSize = UnityEngine.Random.Range(0.05f, 0.15f);
395  placementQuery.Add(
396  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
400  }));
401  }
402  PlaceObjectAsync("OnFloor - AwayFromMe", placementQuery);
403  }
404 
405  public void Query_OnFloor_NearMe()
406  {
407  List<PlacementQuery> placementQuery = new List<PlacementQuery>();
408  for (int i = 0; i < 4; ++i)
409  {
410  float halfDimSize = UnityEngine.Random.Range(0.05f, 0.2f);
411  placementQuery.Add(
412  new PlacementQuery(SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize, halfDimSize, halfDimSize)),
415  },
418  }));
419  }
420  PlaceObjectAsync("OnFloor - NearMe", placementQuery);
421  }
422 
423  private void Update_Queries()
424  {
425  if (Input.GetKeyDown(KeyCode.R))
426  {
427  Query_OnFloor();
428  }
429  if (Input.GetKeyDown(KeyCode.T))
430  {
431  Query_OnWall();
432  }
433  if (Input.GetKeyDown(KeyCode.Y))
434  {
435  Query_OnCeiling();
436  }
437  if (Input.GetKeyDown(KeyCode.U))
438  {
439  Query_OnEdge();
440  }
441  if (Input.GetKeyDown(KeyCode.I))
442  {
443  Query_OnFloorAndCeiling();
444  }
445  if (Input.GetKeyDown(KeyCode.O))
446  {
447  Query_RandomInAir_AwayFromMe();
448  }
449  if (Input.GetKeyDown(KeyCode.P))
450  {
451  Query_OnEdge_NearCenter();
452  }
453  if (Input.GetKeyDown(KeyCode.LeftBracket))
454  {
455  Query_OnFloor_AwayFromMe();
456  }
457  if (Input.GetKeyDown(KeyCode.RightBracket))
458  {
459  Query_OnFloor_NearMe();
460  }
461  }
462 
463  public bool InitializeSolver()
464  {
465  if (IsSolverInitialized ||
466  !SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
467  {
468  return IsSolverInitialized;
469  }
470 
472  {
473  IsSolverInitialized = true;
474  }
475  return IsSolverInitialized;
476  }
477 
478  private void Update()
479  {
480  // Can't do any of this till we're done with the scanning phase
482  {
483  return;
484  }
485 
486  // Make sure the solver has been initialized
487  if (!IsSolverInitialized &&
488  SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
489  {
490  InitializeSolver();
491  }
492 
493  // Constraint queries
495  {
496  Update_Queries();
497  }
498 
499  // Handle async query results
500  ProcessPlacementResults();
501 
502  // Lines: Begin
503  LineDraw_Begin();
504 
505  // Drawers
506  bool needsUpdate = false;
507  needsUpdate |= Draw_PlacementResults();
508 
509  // Lines: Finish up
510  LineDraw_End(needsUpdate);
511  }
512  }
513 }
static void Solver_RemoveAllObjects()
Removed all solved object placements.
static ObjectPlacementConstraint Create_NearPoint(Vector3 position, float minDistance=0.0f, float maxDistance=0.0f)
Constructs an object placement constraint requesting that the placement volume be placed no closer th...
static ObjectPlacementRule Create_AwayFromOtherObjects(float minDistance)
Constructs an object placement rule requiring the placement volume to be placed a minimum distance aw...
static ObjectPlacementDefinition Create_RandomInAir(Vector3 halfDims)
Constructs an object placement query definition requiring the object to be placed floating in space...
static ObjectPlacementDefinition Create_OnEdge(Vector3 halfDims, Vector3 halfDimsBottom)
Constructs an object placement query definition requiring the object to be placed on the edge of a pl...
static int Solver_Init()
Initialized the object placement solver. This should be called after the scanning phase has finish an...
Encapsulates the primary DLL functions, including marshalling helper functions. The DLL functions are...
static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims)
Constructs an object placement query definition requiring the object to be placed on the floor...
Object placement result. Defines an oriented bounding box result for the object placement query...
static ObjectPlacementDefinition Create_OnCeiling(Vector3 halfDims)
Constructs an object placement query definition requiring the object to be place on the ceiling...
Playspace alignment results. Reports internal alignment of room in Unity space and basic alignment of...
Encapsulates the object placement queries of the understanding DLL. These queries will not be valid u...
The purpose of this class is to provide a cached reference to the main camera. Calling Camera...
Definition: CameraCache.cs:12
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
Defines an object placement rule. Rules are one part of an object placement definition. Rules may not be violated by the returned query.
static ObjectPlacementRule Create_AwayFromPosition(Vector3 position, float minDistance)
Constructs an object placement rule requiring the placement volume to be placed a minimum distance aw...
Defines an object placement constraint. Constraints are one part of an object placement definition...
static Camera Main
Returns a cached reference to the main camera and uses Camera.main if it hasn&#39;t been cached yet...
Definition: CameraCache.cs:20
Defines an object placement query. A query consists of a type a name, type, set of rules...
static ObjectPlacementConstraint Create_NearCenter(float minDistance=0.0f, float maxDistance=0.0f)
Constructs an object placement constraint requesting that the placement volume be placed near the cen...
static ObjectPlacementDefinition Create_OnFloorAndCeiling(Vector3 halfDims, Vector3 halfDimsBottom)
Constructs an object placement query definition requiring the object to be have space on the floor an...
The SpatialUnderstanding class controls the state and flow of the scanning process used in the unders...
static int Solver_PlaceObject([In, MarshalAs(UnmanagedType.LPStr)] string objectName, [In] IntPtr placementDefinition, [In] int placementRuleCount, [In] IntPtr placementRules, [In] int constraintCount, [In] IntPtr placementConstraints, [Out] IntPtr placementResult)
Executes an object placement query.
static int QueryPlayspaceAlignment([In] IntPtr playspaceAlignment)
Query the playspace alignment data. This will not be valid until after scanning is finalized...
static ObjectPlacementDefinition Create_OnWall(Vector3 halfDims, float heightMin, float heightMax, WallTypeFlags wallTypes=WallTypeFlags.External|WallTypeFlags.Normal, float marginLeft=0.0f, float marginRight=0.0f)
Constructs an object placement query definition requiring the object to be placed on a wall...