AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
LineDrawer.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 System.Collections.Generic;
7 using HoloToolkit.Unity;
8 using System;
9 
10 namespace HoloToolkit.Examples.SpatialUnderstandingFeatureOverview
11 {
12  public class LineDrawer : MonoBehaviour
13  {
14  // Consts
15  public const float DefaultLineWidth = 0.001f;
16  public const float DefaultBasisLength = 0.2f;
17 
18  // Structs
19  public class Line
20  {
21  // Functions
22  public Line()
23  {
24  }
25  public Line(Vector3 _p0, Vector3 _p1, Color _c0, Color _c1, float _lineWidth = DefaultLineWidth)
26  {
27  p0 = _p0;
28  p1 = _p1;
29  c0 = _c0;
30  c1 = _c1;
31  lineWidth = _lineWidth;
32  isValid = true;
33  }
34  public bool Set_IfDifferent(Vector3 _p0, Vector3 _p1, Color _c0, Color _c1, float _lineWidth)
35  {
36  isValid = true;
37  if ((p0 != _p0) || (p1 != _p1) || (c0 != _c0) || (c1 != _c1) || (lineWidth != _lineWidth))
38  {
39  p0 = _p0;
40  p1 = _p1;
41  c0 = _c0;
42  c1 = _c1;
43  lineWidth = _lineWidth;
44  return true;
45  }
46  return false;
47  }
48 
49  // Data
50  public Vector3 p0;
51  public Vector3 p1;
52  public Color c0;
53  public Color c1;
54  public float lineWidth;
55  public bool isValid;
56  }
57  public class LineData
58  {
59  public int LineIndex;
60  public List<Line> Lines = new List<Line>();
61  public MeshRenderer Renderer;
62  public MeshFilter Filter;
63  }
64  public class AnimationCurve3
65  {
66  public void AddKey(float time, Vector3 pos)
67  {
68  CurveX.AddKey(time, pos.x);
69  CurveY.AddKey(time, pos.y);
70  CurveZ.AddKey(time, pos.z);
71  }
72  public Vector3 Evaluate(float time)
73  {
74  return new Vector3(CurveX.Evaluate(time), CurveY.Evaluate(time), CurveZ.Evaluate(time));
75  }
76 
77  public AnimationCurve CurveX = new AnimationCurve();
78  public AnimationCurve CurveY = new AnimationCurve();
79  public AnimationCurve CurveZ = new AnimationCurve();
80  }
81  public class AnimatedBox
82  {
83  public const float InitialPositionForwardMaxDistance = 2.0f;
84  public const float AnimationTime = 2.5f;
85  public const float DelayPerItem = 0.35f;
86 
87  public AnimatedBox(
88  float timeDelay,
89  Vector3 center,
90  Quaternion rotation,
91  Color color,
92  Vector3 halfSize,
93  float lineWidth = DefaultLineWidth * 3.0f)
94  {
95  TimeDelay = timeDelay;
96  Center = center;
97  Rotation = rotation;
98  Color = color;
99  HalfSize = halfSize;
100  LineWidth = lineWidth;
101 
102  // If no time delay, go ahead and lock the animation now
103  if (TimeDelay <= 0.0f)
104  {
105  SetupAnimation();
106  }
107  }
108 
109  public bool Update(float deltaTime)
110  {
111  Time += deltaTime;
112 
113  // Delay animation setup until after the time delay
114  if (!IsAnimationSetup &&
115  (Time >= TimeDelay))
116  {
117  SetupAnimation();
118  }
119 
120  return (Time >= TimeDelay);
121  }
122 
123  private void SetupAnimation()
124  {
125  if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
126  {
127  return;
128  }
129 
130  // Calculate the forward distance for the animation start point
131  Vector3 rayPos = CameraCache.Main.transform.position;
132  Vector3 rayVec = CameraCache.Main.transform.forward * InitialPositionForwardMaxDistance;
133  IntPtr raycastResultPtr = HoloToolkit.Unity.SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticRaycastResultPtr();
135  rayPos.x, rayPos.y, rayPos.z, rayVec.x, rayVec.y, rayVec.z,
136  raycastResultPtr);
137  SpatialUnderstandingDll.Imports.RaycastResult rayCastResult = HoloToolkit.Unity.SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticRaycastResult();
138  Vector3 animOrigin = (rayCastResult.SurfaceType != HoloToolkit.Unity.SpatialUnderstandingDll.Imports.RaycastResult.SurfaceTypes.Invalid) ?
139  rayPos + rayVec.normalized * Mathf.Max((rayCastResult.IntersectPoint - rayPos).magnitude - 0.3f, 0.0f) :
140  rayPos + rayVec * InitialPositionForwardMaxDistance;
141 
142  // Create the animation (starting it on the ground in front of the camera
143  SpatialUnderstandingDll.Imports.QueryPlayspaceAlignment(SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignmentPtr());
144  SpatialUnderstandingDll.Imports.PlayspaceAlignment alignment = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticPlayspaceAlignment();
145  AnimPosition.AddKey(TimeDelay + 0.0f, new Vector3(animOrigin.x, alignment.FloorYValue, animOrigin.z));
146  AnimPosition.AddKey(TimeDelay + AnimationTime * 0.5f, new Vector3(animOrigin.x, alignment.FloorYValue + 1.25f, animOrigin.z));
147  AnimPosition.AddKey(TimeDelay + AnimationTime * 0.6f, new Vector3(animOrigin.x, alignment.FloorYValue + 1.0f, animOrigin.z));
148  AnimPosition.AddKey(TimeDelay + AnimationTime * 0.95f, Center);
149  AnimPosition.AddKey(TimeDelay + AnimationTime * 1.0f, Center);
150 
151  AnimScale.AddKey(TimeDelay + 0.0f, 0.0f);
152  AnimScale.AddKey(TimeDelay + AnimationTime * 0.5f, 0.5f);
153  AnimScale.AddKey(TimeDelay + AnimationTime * 0.8f, 1.0f);
154  AnimScale.AddKey(TimeDelay + AnimationTime * 1.0f, 1.0f);
155 
156  AnimRotation.AddKey(TimeDelay + 0.0f, -1.5f);
157  AnimRotation.AddKey(TimeDelay + AnimationTime * 0.2f, -0.5f);
158  AnimRotation.AddKey(TimeDelay + AnimationTime * 0.9f, 0.0f);
159  AnimRotation.AddKey(TimeDelay + AnimationTime * 1.0f, 0.0f);
160 
161  IsAnimationSetup = true;
162  }
163  public bool IsAnimationComplete { get { return IsAnimationSetup && (Time >= (AnimatedBox.AnimationTime + TimeDelay)); } }
164 
165  public Vector3 Center;
166  public Quaternion Rotation;
167  public Color Color;
168  public Vector3 HalfSize;
169  public float LineWidth;
170 
171  public bool IsAnimationSetup;
172  public float Time;
173  public float TimeDelay;
174  public AnimationCurve AnimScale = new AnimationCurve();
175  public AnimationCurve3 AnimPosition = new AnimationCurve3();
176  public AnimationCurve AnimRotation = new AnimationCurve();
177 
178  }
179 
180  // Config
181  public Material MaterialLine;
182 
183  // Privates
184  private LineData lineData = new LineData();
185 
186  // Functions
187  protected virtual void OnDestroy()
188  {
189  // Line renderer
190  if (lineData != null)
191  {
192  if (lineData.Renderer != null)
193  {
194  Destroy(lineData.Renderer);
195  }
196  if (lineData.Filter != null)
197  {
198  Destroy(lineData.Filter);
199  }
200  }
201  lineData = null;
202  }
203 
204  protected const int PointsOnCircle = 50;
205  protected bool Draw_Circle(Vector3 center, Vector3 normal, Color color, float radius = 0.25f, float lineWidth = DefaultLineWidth)
206  {
207  bool returnValue = false;
208  float theta = 0;
209  float radPerPoint = (2.0f * Mathf.PI) / (float)PointsOnCircle;
210  Quaternion q = Quaternion.FromToRotation(Vector3.up, normal);
211  Vector3 start = q * new Vector3(Mathf.Cos(theta) * radius, 0.0f, Mathf.Sin(theta) * radius) + center;
212 
213  for (int i = 1; i <= PointsOnCircle; i++)
214  {
215  theta = (i % PointsOnCircle) * radPerPoint;
216 
217  Vector3 end = q * new Vector3(Mathf.Cos(theta) * radius, 0.0f, Mathf.Sin(theta) * radius) + center;
218  returnValue |= Draw_Line(start, end, color, color, lineWidth);
219 
220  start = end;
221  }
222 
223  return returnValue;
224  }
225 
226  protected bool Draw_Circle_Partial(Vector3 center, Vector3 normal, Color color, float radius = 0.25f, float lineWidth = DefaultLineWidth, float circleAngleArc = 360.0f)
227  {
228  bool returnValue = false;
229  float theta = 0;
230  float radPerPoint = (circleAngleArc * Mathf.Deg2Rad * 0.5f) / (float)PointsOnCircle;
231  Quaternion q = Quaternion.FromToRotation(Vector3.up, normal);
232  Vector3 start0 = q * new Vector3(Mathf.Cos(theta) * radius, 0.0f, Mathf.Sin(theta) * radius) + center;
233  Vector3 start1 = q * new Vector3(Mathf.Cos(theta) * -radius, 0.0f, Mathf.Sin(theta) * -radius) + center;
234 
235  int maxPointCount = (circleAngleArc < 360.0f) ? (PointsOnCircle - 1) : PointsOnCircle;
236  for (int i = 1; i <= maxPointCount; i++)
237  {
238  theta = (i % PointsOnCircle) * radPerPoint;
239 
240  Vector3 end0 = q * new Vector3(Mathf.Cos(theta) * radius, 0.0f, Mathf.Sin(theta) * radius) + center;
241  Vector3 end1 = q * new Vector3(Mathf.Cos(theta) * -radius, 0.0f, Mathf.Sin(theta) * -radius) + center;
242  returnValue |= Draw_Line(start0, end0, color, color, lineWidth);
243  returnValue |= Draw_Line(start1, end1, color, color, lineWidth);
244 
245  start0 = end0;
246  start1 = end1;
247  }
248 
249  return returnValue;
250  }
251 
252  protected bool Draw_Cube(Vector3 point, Color color, float halfSize = DefaultLineWidth)
253  {
254  return Draw_Line(point - Vector3.right * halfSize, point + Vector3.right * halfSize, color, color, halfSize);
255  }
256 
257  protected bool Draw_AnimatedBox(AnimatedBox box)
258  {
259  // Update the time
260  if (!box.Update(Time.deltaTime))
261  {
262  return false;
263  }
264  if (box.IsAnimationComplete)
265  {
266  // Animation is done, just pass through
267  return Draw_Box(box.Center, box.Rotation, box.Color, box.HalfSize, box.LineWidth);
268  }
269 
270  // Draw it using the current animation state
271  return Draw_Box(
272  box.AnimPosition.Evaluate(box.Time),
273  box.Rotation * Quaternion.AngleAxis(360.0f * box.AnimRotation.Evaluate(box.Time), Vector3.up),
274  box.Color,
275  box.HalfSize * box.AnimScale.Evaluate(box.Time),
276  box.LineWidth);
277  }
278 
279  protected bool Draw_Box(Vector3 center, Quaternion rotation, Color color, Vector3 halfSize, float lineWidth = DefaultLineWidth)
280  {
281  bool needsUpdate = false;
282 
283  Vector3 basisX = rotation * Vector3.right;
284  Vector3 basisY = rotation * Vector3.up;
285  Vector3 basisZ = rotation * Vector3.forward;
286  Vector3[] pts =
287  {
288  center + basisX * halfSize.x + basisY * halfSize.y + basisZ * halfSize.z,
289  center + basisX * halfSize.x + basisY * halfSize.y - basisZ * halfSize.z,
290  center - basisX * halfSize.x + basisY * halfSize.y - basisZ * halfSize.z,
291  center - basisX * halfSize.x + basisY * halfSize.y + basisZ * halfSize.z,
292 
293  center + basisX * halfSize.x - basisY * halfSize.y + basisZ * halfSize.z,
294  center + basisX * halfSize.x - basisY * halfSize.y - basisZ * halfSize.z,
295  center - basisX * halfSize.x - basisY * halfSize.y - basisZ * halfSize.z,
296  center - basisX * halfSize.x - basisY * halfSize.y + basisZ * halfSize.z
297  };
298 
299  // Bottom
300  needsUpdate |= Draw_Line(pts[0], pts[1], color, color, lineWidth);
301  needsUpdate |= Draw_Line(pts[1], pts[2], color, color, lineWidth);
302  needsUpdate |= Draw_Line(pts[2], pts[3], color, color, lineWidth);
303  needsUpdate |= Draw_Line(pts[3], pts[0], color, color, lineWidth);
304 
305  // Top
306  needsUpdate |= Draw_Line(pts[4], pts[5], color, color, lineWidth);
307  needsUpdate |= Draw_Line(pts[5], pts[6], color, color, lineWidth);
308  needsUpdate |= Draw_Line(pts[6], pts[7], color, color, lineWidth);
309  needsUpdate |= Draw_Line(pts[7], pts[4], color, color, lineWidth);
310 
311  // Vertical lines
312  needsUpdate |= Draw_Line(pts[0], pts[4], color, color, lineWidth);
313  needsUpdate |= Draw_Line(pts[1], pts[5], color, color, lineWidth);
314  needsUpdate |= Draw_Line(pts[2], pts[6], color, color, lineWidth);
315  needsUpdate |= Draw_Line(pts[3], pts[7], color, color, lineWidth);
316 
317  return needsUpdate;
318  }
319 
320  protected bool Draw_Line(Vector3 start, Vector3 end, Color colorStart, Color colorEnd, float lineWidth = DefaultLineWidth)
321  {
322  // Create up a new line (unless it's already created)
323  while (lineData.LineIndex >= lineData.Lines.Count)
324  {
325  lineData.Lines.Add(new Line());
326  }
327 
328  // Set it
329  bool needsUpdate = lineData.Lines[lineData.LineIndex].Set_IfDifferent(transform.InverseTransformPoint(start), transform.InverseTransformPoint(end), colorStart, colorEnd, lineWidth);
330 
331  // Inc out count
332  ++lineData.LineIndex;
333 
334  return needsUpdate;
335  }
336 
337  protected bool Draw_TransformBasis(Transform transformToDraw, float basisLength = DefaultBasisLength, float lineWidth = DefaultLineWidth * 2.0f)
338  {
339  // Basis
340  bool needsUpdate = false;
341  needsUpdate |= Draw_Line(transformToDraw.transform.position, transformToDraw.transform.position + transformToDraw.transform.right * basisLength, Color.red, Color.red, lineWidth);
342  needsUpdate |= Draw_Line(transformToDraw.transform.position, transformToDraw.transform.position + transformToDraw.transform.up * basisLength, Color.green, Color.green, lineWidth);
343  needsUpdate |= Draw_Line(transformToDraw.transform.position, transformToDraw.transform.position + transformToDraw.transform.forward * basisLength, Color.blue, Color.blue, lineWidth);
344 
345  return needsUpdate;
346  }
347 
348  private void Lines_LineDataToMesh()
349  {
350  // Allocate them up
351  Vector3[] verts = new Vector3[lineData.Lines.Count * 8];
352  int[] tris = new int[lineData.Lines.Count * 12 * 3];
353  Color[] colors = new Color[verts.Length];
354 
355  // Build the data
356  for (int i = 0; i < lineData.Lines.Count; ++i)
357  {
358  // Base index calculations
359  int vert = i * 8;
360  int v0 = vert;
361  int tri = i * 12 * 3;
362 
363  // Setup
364  Vector3 dirUnit = (lineData.Lines[i].p1 - lineData.Lines[i].p0).normalized;
365  Vector3 normX = Vector3.Cross((Mathf.Abs(dirUnit.y) >= 0.99f) ? Vector3.right : Vector3.up, dirUnit).normalized;
366  Vector3 normy = Vector3.Cross(normX, dirUnit);
367 
368  // Vertices
369  verts[vert] = lineData.Lines[i].p0 + normX * lineData.Lines[i].lineWidth + normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c0; ++vert;
370  verts[vert] = lineData.Lines[i].p0 - normX * lineData.Lines[i].lineWidth + normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c0; ++vert;
371  verts[vert] = lineData.Lines[i].p0 - normX * lineData.Lines[i].lineWidth - normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c0; ++vert;
372  verts[vert] = lineData.Lines[i].p0 + normX * lineData.Lines[i].lineWidth - normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c0; ++vert;
373 
374  verts[vert] = lineData.Lines[i].p1 + normX * lineData.Lines[i].lineWidth + normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c1; ++vert;
375  verts[vert] = lineData.Lines[i].p1 - normX * lineData.Lines[i].lineWidth + normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c1; ++vert;
376  verts[vert] = lineData.Lines[i].p1 - normX * lineData.Lines[i].lineWidth - normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c1; ++vert;
377  verts[vert] = lineData.Lines[i].p1 + normX * lineData.Lines[i].lineWidth - normy * lineData.Lines[i].lineWidth; colors[vert] = lineData.Lines[i].c1; ++vert;
378 
379  // Indices
380  tris[tri + 0] = (v0 + 0); tris[tri + 1] = (v0 + 5); tris[tri + 2] = (v0 + 4); tri += 3;
381  tris[tri + 0] = (v0 + 1); tris[tri + 1] = (v0 + 5); tris[tri + 2] = (v0 + 0); tri += 3;
382 
383  tris[tri + 0] = (v0 + 1); tris[tri + 1] = (v0 + 6); tris[tri + 2] = (v0 + 5); tri += 3;
384  tris[tri + 0] = (v0 + 2); tris[tri + 1] = (v0 + 6); tris[tri + 2] = (v0 + 1); tri += 3;
385 
386  tris[tri + 0] = (v0 + 2); tris[tri + 1] = (v0 + 7); tris[tri + 2] = (v0 + 6); tri += 3;
387  tris[tri + 0] = (v0 + 3); tris[tri + 1] = (v0 + 7); tris[tri + 2] = (v0 + 2); tri += 3;
388 
389  tris[tri + 0] = (v0 + 3); tris[tri + 1] = (v0 + 7); tris[tri + 2] = (v0 + 4); tri += 3;
390  tris[tri + 0] = (v0 + 3); tris[tri + 1] = (v0 + 4); tris[tri + 2] = (v0 + 0); tri += 3;
391 
392  tris[tri + 0] = (v0 + 0); tris[tri + 1] = (v0 + 3); tris[tri + 2] = (v0 + 2); tri += 3;
393  tris[tri + 0] = (v0 + 0); tris[tri + 1] = (v0 + 2); tris[tri + 2] = (v0 + 1); tri += 3;
394 
395  tris[tri + 0] = (v0 + 5); tris[tri + 1] = (v0 + 6); tris[tri + 2] = (v0 + 7); tri += 3;
396  tris[tri + 0] = (v0 + 5); tris[tri + 1] = (v0 + 7); tris[tri + 2] = (v0 + 4); tri += 3;
397  }
398 
399  // Create up the components
400  if (lineData.Filter == null)
401  {
402  lineData.Filter = gameObject.AddComponent<MeshFilter>();
403  }
404  if (lineData.Renderer == null)
405  {
406  lineData.Renderer = gameObject.AddComponent<MeshRenderer>();
407  lineData.Renderer.material = MaterialLine;
408  }
409 
410  // Create or clear the mesh
411  Mesh mesh = null;
412  if (lineData.Filter.mesh != null)
413  {
414  mesh = lineData.Filter.mesh;
415  mesh.Clear();
416  }
417  else
418  {
419  mesh = new Mesh();
420  mesh.name = "LineDrawer.Lines_LineDataToMesh";
421  }
422 
423  // Set them into the mesh
424  mesh.vertices = verts;
425  mesh.triangles = tris;
426  mesh.colors = colors;
427  mesh.RecalculateBounds();
428  mesh.RecalculateNormals();
429 
430  lineData.Filter.mesh = mesh;
431 
432  // If no triangles, hide it
433  lineData.Renderer.enabled = (lineData.Lines.Count == 0) ? false : true;
434 
435  // Line index reset
436  lineData.LineIndex = 0;
437  }
438 
439  protected void LineDraw_Begin()
440  {
441  lineData.LineIndex = 0;
442  for (int i = 0; i < lineData.Lines.Count; ++i)
443  {
444  lineData.Lines[i].isValid = false;
445  }
446  }
447 
448  protected void LineDraw_End(bool needsUpdate)
449  {
450  if (lineData == null)
451  {
452  return;
453  }
454 
455  // Check if we have any not dirty
456  int i = 0;
457  while (i < lineData.Lines.Count)
458  {
459  if (!lineData.Lines[i].isValid)
460  {
461  needsUpdate = true;
462  lineData.Lines.RemoveAt(i);
463  continue;
464  }
465  ++i;
466  }
467 
468  // Do the update (if needed)
469  if (needsUpdate)
470  {
471  Lines_LineDataToMesh();
472  }
473  }
474  }
475 }
bool Draw_Circle_Partial(Vector3 center, Vector3 normal, Color color, float radius=0.25f, float lineWidth=DefaultLineWidth, float circleAngleArc=360.0f)
Definition: LineDrawer.cs:226
bool Draw_Circle(Vector3 center, Vector3 normal, Color color, float radius=0.25f, float lineWidth=DefaultLineWidth)
Definition: LineDrawer.cs:205
Line(Vector3 _p0, Vector3 _p1, Color _c0, Color _c1, float _lineWidth=DefaultLineWidth)
Definition: LineDrawer.cs:25
AnimatedBox(float timeDelay, Vector3 center, Quaternion rotation, Color color, Vector3 halfSize, float lineWidth=DefaultLineWidth *3.0f)
Definition: LineDrawer.cs:87
Encapsulates the primary DLL functions, including marshalling helper functions. The DLL functions are...
bool Draw_Box(Vector3 center, Quaternion rotation, Color color, Vector3 halfSize, float lineWidth=DefaultLineWidth)
Definition: LineDrawer.cs:279
Playspace alignment results. Reports internal alignment of room in Unity space and basic alignment of...
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
bool Set_IfDifferent(Vector3 _p0, Vector3 _p1, Color _c0, Color _c1, float _lineWidth)
Definition: LineDrawer.cs:34
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
static 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)
Perform a raycast against the internal world representation of the understanding DLL. This will not be valid until after scanning is finalized.
bool Draw_Line(Vector3 start, Vector3 end, Color colorStart, Color colorEnd, float lineWidth=DefaultLineWidth)
Definition: LineDrawer.cs:320
The SpatialUnderstanding class controls the state and flow of the scanning process used in the unders...
bool Draw_Cube(Vector3 point, Color color, float halfSize=DefaultLineWidth)
Definition: LineDrawer.cs:252
bool Draw_TransformBasis(Transform transformToDraw, float basisLength=DefaultBasisLength, float lineWidth=DefaultLineWidth *2.0f)
Definition: LineDrawer.cs:337
static int QueryPlayspaceAlignment([In] IntPtr playspaceAlignment)
Query the playspace alignment data. This will not be valid until after scanning is finalized...