rendering.h
Go to the documentation of this file.
1 // License: Apache 2.0. See LICENSE file in root directory.
2 // Copyright(c) 2015 Intel Corporation. All Rights Reserved.
3 
4 #pragma once
5 
6 #ifndef NOMINMAX
7 #define NOMINMAX
8 #endif
9 #include <glad/glad.h>
10 
11 #include <librealsense2/rs.hpp>
13 #include <utilities/time/timer.h>
14 
15 
16 #include <vector>
17 #include <algorithm>
18 #include <cstring>
19 #include <ctype.h>
20 #include <memory>
21 #include <string>
22 #include <cassert>
23 #include <sstream>
24 #include <iomanip>
25 #include <array>
26 #include <chrono>
27 #define _USE_MATH_DEFINES
28 #include <cmath>
29 #include <map>
30 #include <unordered_map>
31 #include <mutex>
32 #include <algorithm>
33 #include <iostream>
34 #include <thread>
35 #include <chrono>
36 
37 #ifdef _MSC_VER
38 #ifndef GL_CLAMP_TO_BORDER
39 #define GL_CLAMP_TO_BORDER 0x812D
40 #endif
41 #ifndef GL_CLAMP_TO_EDGE
42 #define GL_CLAMP_TO_EDGE 0x812F
43 #endif
44 #endif
45 
46 namespace rs2
47 {
48  class fps_calc
49  {
50  public:
52  : _counter(0),
53  _delta(0),
54  _last_timestamp(0),
56  {}
57 
58  fps_calc(const fps_calc& other)
59  {
60  std::lock_guard<std::mutex> lock(other._mtx);
61  _counter = other._counter;
62  _delta = other._delta;
65  }
66  void add_timestamp(double timestamp, unsigned long long frame_counter)
67  {
68  std::lock_guard<std::mutex> lock(_mtx);
69  if (++_counter >= _skip_frames)
70  {
71  if (_last_timestamp != 0)
72  {
73  _delta = timestamp - _last_timestamp;
74  _num_of_frames = frame_counter - _last_frame_counter;
75  }
76 
79  _counter = 0;
80  }
81  }
82 
83  double get_fps() const
84  {
85  std::lock_guard<std::mutex> lock(_mtx);
86  if (_delta == 0)
87  return 0;
88 
89  return (static_cast<double>(_numerator) * _num_of_frames) / _delta;
90  }
91 
92  private:
93  static const int _numerator = 1000;
94  static const int _skip_frames = 5;
95  int _counter;
96  double _delta;
98  unsigned long long _num_of_frames;
99  unsigned long long _last_frame_counter;
100  mutable std::mutex _mtx;
101  };
102 
103  inline float clamp(float x, float min, float max)
104  {
105  return std::max(std::min(max, x), min);
106  }
107 
108  inline float smoothstep(float x, float min, float max)
109  {
110  if (max == min)
111  {
112  x = clamp((x - min) , 0.0, 1.0);
113  }
114  else
115  {
116  x = clamp((x - min) / (max - min), 0.0, 1.0);
117  }
118 
119  return x*x*(3 - 2 * x);
120  }
121 
122  inline float lerp(float a, float b, float t)
123  {
124  return b * t + a * (1 - t);
125  }
126 
127  struct plane
128  {
129  float a;
130  float b;
131  float c;
132  float d;
133  };
134  inline bool operator==(const plane& lhs, const plane& rhs) { return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c && lhs.d == rhs.d; }
135 
136  struct float3
137  {
138  float x, y, z;
139 
140  float length() const { return sqrt(x*x + y*y + z*z); }
141 
143  {
144  return (length() > 0)? float3{ x / length(), y / length(), z / length() }:*this;
145  }
146  };
147 
148  struct float4
149  {
150  float x, y, z, w;
151  };
152 
153  inline float3 cross(const float3& a, const float3& b)
154  {
155  return { a.y * b.z - b.y * a.z, a.x * b.z - b.x * a.z, a.x * b.y - a.y * b.x };
156  }
157 
158  inline float evaluate_plane(const plane& plane, const float3& point)
159  {
160  return plane.a * point.x + plane.b * point.y + plane.c * point.z + plane.d;
161  }
162 
163  inline float3 operator*(const float3& a, float t)
164  {
165  return { a.x * t, a.y * t, a.z * t };
166  }
167 
168  inline float3 operator/(const float3& a, float t)
169  {
170  return { a.x / t, a.y / t, a.z / t };
171  }
172 
173  inline float3 operator+(const float3& a, const float3& b)
174  {
175  return { a.x + b.x, a.y + b.y, a.z + b.z };
176  }
177 
178  inline float3 operator-(const float3& a, const float3& b)
179  {
180  return { a.x - b.x, a.y - b.y, a.z - b.z };
181  }
182 
183  inline float3 lerp(const float3& a, const float3& b, float t)
184  {
185  return b * t + a * (1 - t);
186  }
187 
188  struct float2
189  {
190  float x, y;
191 
192  float length() const { return sqrt(x*x + y*y); }
193 
195  {
196  return { x / length(), y / length() };
197  }
198  };
199 
200  inline float dot(const rs2::float2& a, const rs2::float2& b)
201  {
202  return a.x * b.x + a.y * b.y;
203  }
204 
205  inline rs2::float2 lerp(const rs2::float2& a, const rs2::float2& b, float t)
206  {
207  return rs2::float2{ lerp(a.x, b.x, t), lerp(a.y, b.y, t) };
208  }
209 
210  inline float3 lerp(const std::array<float3, 4>& rect, const float2& p)
211  {
212  auto v1 = lerp(rect[0], rect[1], p.x);
213  auto v2 = lerp(rect[3], rect[2], p.x);
214  return lerp(v1, v2, p.y);
215  }
216 
217  using plane_3d = std::array<float3, 4>;
218 
219  inline std::vector<plane_3d> subdivide(const plane_3d& rect, int parts = 4)
220  {
221  std::vector<plane_3d> res;
222  res.reserve(parts*parts);
223  for (float i = 0.f; i < parts; i++)
224  {
225  for (float j = 0.f; j < parts; j++)
226  {
227  plane_3d r;
228  r[0] = lerp(rect, { i / parts, j / parts });
229  r[1] = lerp(rect, { i / parts, (j + 1) / parts });
230  r[2] = lerp(rect, { (i + 1) / parts, (j + 1) / parts });
231  r[3] = lerp(rect, { (i + 1) / parts, j / parts });
232  res.push_back(r);
233  }
234  }
235  return res;
236  }
237 
238  inline float operator*(const float3& a, const float3& b)
239  {
240  return a.x*b.x + a.y*b.y + a.z*b.z;
241  }
242 
243  inline bool is_valid(const plane_3d& p)
244  {
245  std::vector<float> angles;
246  angles.reserve(4);
247  for (size_t i = 0; i < p.size(); i++)
248  {
249  auto p1 = p[i];
250  auto p2 = p[(i+1) % p.size()];
251  if ((p2 - p1).length() < 1e-3) return false;
252 
253  p1 = p1.normalize();
254  p2 = p2.normalize();
255 
256  angles.push_back(acos((p1 * p2) / sqrt(p1.length() * p2.length())));
257  }
258  return std::all_of(angles.begin(), angles.end(), [](float f) { return f > 0; }) ||
259  std::all_of(angles.begin(), angles.end(), [](float f) { return f < 0; });
260  }
261 
263  {
264  return { a.x - b.x, a.y - b.y };
265  }
266 
267  inline float2 operator*(float a, float2 b)
268  {
269  return { a * b.x, a * b.y };
270  }
271 
272  struct matrix4
273  {
274  float mat[4][4];
275 
276  operator float*() const
277  {
278  return (float*)&mat;
279  }
280 
281  static matrix4 identity()
282  {
283  matrix4 m;
284  for (int i = 0; i < 4; i++)
285  m.mat[i][i] = 1.f;
286  return m;
287  }
288 
290  {
291  std::memset(mat, 0, sizeof(mat));
292  }
293 
294  matrix4(float vals[4][4])
295  {
296  std::memcpy(mat,vals,sizeof(mat));
297  }
298 
299  // convert glGetFloatv output to matrix4
300  //
301  // float m[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
302  // into
303  // rs2::matrix4 m = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15} };
304  // 0 1 2 3
305  // 4 5 6 7
306  // 8 9 10 11
307  // 12 13 14 15
308  matrix4(float vals[16])
309  {
310  for (int i = 0; i < 4; i++)
311  {
312  for (int j = 0; j < 4; j++)
313  {
314  mat[i][j] = vals[i * 4 + j];
315  }
316  }
317  }
318 
319  float& operator()(int i, int j) { return mat[i][j]; }
320  const float& operator()(int i, int j) const { return mat[i][j]; }
321 
322  //init rotation matrix from quaternion
324  {
325  mat[0][0] = 1 - 2*q.y*q.y - 2*q.z*q.z; mat[0][1] = 2*q.x*q.y - 2*q.z*q.w; mat[0][2] = 2*q.x*q.z + 2*q.y*q.w; mat[0][3] = 0.0f;
326  mat[1][0] = 2*q.x*q.y + 2*q.z*q.w; mat[1][1] = 1 - 2*q.x*q.x - 2*q.z*q.z; mat[1][2] = 2*q.y*q.z - 2*q.x*q.w; mat[1][3] = 0.0f;
327  mat[2][0] = 2*q.x*q.z - 2*q.y*q.w; mat[2][1] = 2*q.y*q.z + 2*q.x*q.w; mat[2][2] = 1 - 2*q.x*q.x - 2*q.y*q.y; mat[2][3] = 0.0f;
328  mat[3][0] = 0.0f; mat[3][1] = 0.0f; mat[3][2] = 0.0f; mat[3][3] = 1.0f;
329  }
330 
331  //init translation matrix from vector
333  {
334  mat[0][0] = 1.0f; mat[0][1] = 0.0f; mat[0][2] = 0.0f; mat[0][3] = t.x;
335  mat[1][0] = 0.0f; mat[1][1] = 1.0f; mat[1][2] = 0.0f; mat[1][3] = t.y;
336  mat[2][0] = 0.0f; mat[2][1] = 0.0f; mat[2][2] = 1.0f; mat[2][3] = t.z;
337  mat[3][0] = 0.0f; mat[3][1] = 0.0f; mat[3][2] = 0.0f; mat[3][3] = 1.0f;
338  }
339 
341  {
342  float norm = sqrtf(a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w);
343  rs2_quaternion res = a;
344  res.x /= norm;
345  res.y /= norm;
346  res.z /= norm;
347  res.w /= norm;
348  return res;
349  }
350 
352  {
353  float tr[4];
355  tr[0] = (mat[0][0] + mat[1][1] + mat[2][2]);
356  tr[1] = (mat[0][0] - mat[1][1] - mat[2][2]);
357  tr[2] = (-mat[0][0] + mat[1][1] - mat[2][2]);
358  tr[3] = (-mat[0][0] - mat[1][1] + mat[2][2]);
359  if (tr[0] >= tr[1] && tr[0] >= tr[2] && tr[0] >= tr[3])
360  {
361  float s = 2 * sqrt(tr[0] + 1);
362  res.w = s / 4;
363  res.x = (mat[2][1] - mat[1][2]) / s;
364  res.y = (mat[0][2] - mat[2][0]) / s;
365  res.z = (mat[1][0] - mat[0][1]) / s;
366  }
367  else if (tr[1] >= tr[2] && tr[1] >= tr[3]) {
368  float s = 2 * sqrt(tr[1] + 1);
369  res.w = (mat[2][1] - mat[1][2]) / s;
370  res.x = s / 4;
371  res.y = (mat[1][0] + mat[0][1]) / s;
372  res.z = (mat[2][0] + mat[0][2]) / s;
373  }
374  else if (tr[2] >= tr[3]) {
375  float s = 2 * sqrt(tr[2] + 1);
376  res.w = (mat[0][2] - mat[2][0]) / s;
377  res.x = (mat[1][0] + mat[0][1]) / s;
378  res.y = s / 4;
379  res.z = (mat[1][2] + mat[2][1]) / s;
380  }
381  else {
382  float s = 2 * sqrt(tr[3] + 1);
383  res.w = (mat[1][0] - mat[0][1]) / s;
384  res.x = (mat[0][2] + mat[2][0]) / s;
385  res.y = (mat[1][2] + mat[2][1]) / s;
386  res.z = s / 4;
387  }
388  return normalize(res);
389  }
390 
391  void to_column_major(float column_major[16])
392  {
393  column_major[0] = mat[0][0];
394  column_major[1] = mat[1][0];
395  column_major[2] = mat[2][0];
396  column_major[3] = mat[3][0];
397  column_major[4] = mat[0][1];
398  column_major[5] = mat[1][1];
399  column_major[6] = mat[2][1];
400  column_major[7] = mat[3][1];
401  column_major[8] = mat[0][2];
402  column_major[9] = mat[1][2];
403  column_major[10] = mat[2][2];
404  column_major[11] = mat[3][2];
405  column_major[12] = mat[0][3];
406  column_major[13] = mat[1][3];
407  column_major[14] = mat[2][3];
408  column_major[15] = mat[3][3];
409  }
410  };
411 
412  inline matrix4 operator*(const matrix4& a, const matrix4& b)
413  {
414  matrix4 res;
415  for (int i = 0; i < 4; i++)
416  {
417  for (int j = 0; j < 4; j++)
418  {
419  float sum = 0.0f;
420  for (int k = 0; k < 4; k++)
421  {
422  sum += a.mat[i][k] * b.mat[k][j];
423  }
424  res.mat[i][j] = sum;
425  }
426  }
427  return res;
428  }
429 
430  inline float4 operator*(const matrix4& a, const float4& b)
431  {
432  float4 res;
433  int i = 0;
434  res.x = a(i, 0) * b.x + a(i, 1) * b.y + a(i, 2) * b.z + a(i, 3) * b.w; i++;
435  res.y = a(i, 0) * b.x + a(i, 1) * b.y + a(i, 2) * b.z + a(i, 3) * b.w; i++;
436  res.z = a(i, 0) * b.x + a(i, 1) * b.y + a(i, 2) * b.z + a(i, 3) * b.w; i++;
437  res.w = a(i, 0) * b.x + a(i, 1) * b.y + a(i, 2) * b.z + a(i, 3) * b.w; i++;
438  return res;
439  }
440 
442  {
443  matrix4 rotation(pose.rotation);
444  matrix4 translation(pose.translation);
445  matrix4 G_tm2_body_to_tm2_world = translation * rotation;
446  float rotate_180_y[4][4] = { { -1, 0, 0, 0 },
447  { 0, 1, 0, 0 },
448  { 0, 0,-1, 0 },
449  { 0, 0, 0, 1 } };
450  matrix4 G_vr_body_to_tm2_body(rotate_180_y);
451  matrix4 G_vr_body_to_tm2_world = G_tm2_body_to_tm2_world * G_vr_body_to_tm2_body;
452 
453  float rotate_90_x[4][4] = { { 1, 0, 0, 0 },
454  { 0, 0,-1, 0 },
455  { 0, 1, 0, 0 },
456  { 0, 0, 0, 1 } };
457  matrix4 G_tm2_world_to_vr_world(rotate_90_x);
458  matrix4 G_vr_body_to_vr_world = G_tm2_world_to_vr_world * G_vr_body_to_tm2_world;
459 
460  return G_vr_body_to_vr_world;
461  }
462 
464  {
465  matrix4 G_vr_body_to_vr_world = tm2_pose_to_world_transformation(pose);
466  rs2_pose res = pose;
467  res.translation.x = G_vr_body_to_vr_world.mat[0][3];
468  res.translation.y = G_vr_body_to_vr_world.mat[1][3];
469  res.translation.z = G_vr_body_to_vr_world.mat[2][3];
470  res.rotation = G_vr_body_to_vr_world.to_quaternion();
471  return res;
472  }
473 
474  struct mouse_info
475  {
476  float2 cursor{ 0.f, 0.f };
477  float2 prev_cursor{ 0.f, 0.f };
478  bool mouse_down[2] { false, false };
479  int mouse_wheel = 0;
480  float ui_wheel = 0.f;
481  };
482 
483  template<typename T>
484  T normalizeT(const T& in_val, const T& min, const T& max)
485  {
486  if (min >= max) return 0;
487  return ((in_val - min)/(max - min));
488  }
489 
490  template<typename T>
491  T unnormalizeT(const T& in_val, const T& min, const T& max)
492  {
493  if (min == max) return min;
494  return ((in_val * (max - min)) + min);
495  }
496 
497  struct rect
498  {
499  float x, y;
500  float w, h;
501 
502  void operator=(const rect& other)
503  {
504  x = other.x;
505  y = other.y;
506  w = other.w;
507  h = other.h;
508  }
509 
510  operator bool() const
511  {
512  return w*w > 0 && h*h > 0;
513  }
514 
515  bool operator==(const rect& other) const
516  {
517  return x == other.x && y == other.y && w == other.w && h == other.h;
518  }
519 
520  bool operator!=(const rect& other) const
521  {
522  return !(*this == other);
523  }
524 
525  rect normalize(const rect& normalize_to) const
526  {
527  return rect{normalizeT(x, normalize_to.x, normalize_to.x + normalize_to.w),
528  normalizeT(y, normalize_to.y, normalize_to.y + normalize_to.h),
529  normalizeT(w, 0.f, normalize_to.w),
530  normalizeT(h, 0.f, normalize_to.h)};
531  }
532 
533  rect unnormalize(const rect& unnormalize_to) const
534  {
535  return rect{unnormalizeT(x, unnormalize_to.x, unnormalize_to.x + unnormalize_to.w),
536  unnormalizeT(y, unnormalize_to.y, unnormalize_to.y + unnormalize_to.h),
537  unnormalizeT(w, 0.f, unnormalize_to.w),
538  unnormalizeT(h, 0.f, unnormalize_to.h)};
539  }
540 
541  // Calculate the intersection between two rects
542  // If the intersection is empty, a rect with width and height zero will be returned
543  rect intersection(const rect& other) const
544  {
545  auto x1 = std::max(x, other.x);
546  auto y1 = std::max(y, other.y);
547  auto x2 = std::min(x + w, other.x + other.w);
548  auto y2 = std::min(y + h, other.y + other.h);
549 
550  return{
551  x1, y1,
552  std::max(x2 - x1, 0.f),
553  std::max(y2 - y1, 0.f)
554  };
555  }
556 
557  // Calculate the area of the rect
558  float area() const
559  {
560  return w * h;
561  }
562 
563  rect cut_by(const rect& r) const
564  {
565  auto x1 = x;
566  auto y1 = y;
567  auto x2 = x + w;
568  auto y2 = y + h;
569 
570  x1 = std::max(x1, r.x);
571  x1 = std::min(x1, r.x + r.w);
572  y1 = std::max(y1, r.y);
573  y1 = std::min(y1, r.y + r.h);
574 
575  x2 = std::max(x2, r.x);
576  x2 = std::min(x2, r.x + r.w);
577  y2 = std::max(y2, r.y);
578  y2 = std::min(y2, r.y + r.h);
579 
580  return { x1, y1, x2 - x1, y2 - y1 };
581  }
582 
583  bool contains(const float2& p) const
584  {
585  return (p.x >= x) && (p.x < x + w) && (p.y >= y) && (p.y < y + h);
586  }
587 
588  rect pan(const float2& p) const
589  {
590  return { x - p.x, y - p.y, w, h };
591  }
592 
593  rect center() const
594  {
595  return{ x + w / 2.f, y + h / 2.f, 0, 0 };
596  }
597 
598  rect lerp(float t, const rect& other) const
599  {
600  return{
601  rs2::lerp(x, other.x, t), rs2::lerp(y, other.y, t),
602  rs2::lerp(w, other.w, t), rs2::lerp(h, other.h, t),
603  };
604  }
605 
607  {
608  auto H = static_cast<float>(h), W = static_cast<float>(h) * size.x / size.y;
609  if (W > w)
610  {
611  auto scale = w / W;
612  W *= scale;
613  H *= scale;
614  }
615 
616  return{ float(floor(x + floor(w - W) / 2)),
617  float(floor(y + floor(h - H) / 2)),
618  W, H };
619  }
620 
621  rect scale(float factor) const
622  {
623  return { x, y, w * factor, h * factor };
624  }
625 
626  rect grow(int pixels) const
627  {
628  return { x - pixels, y - pixels, w + pixels*2, h + pixels*2 };
629  }
630 
631  rect grow(int dx, int dy) const
632  {
633  return { x - dx, y - dy, w + dx*2, h + dy*2 };
634  }
635 
637  {
638  return { x + pixels.x, y + pixels.y, w - pixels.x * 2, h - pixels.y * 2 };
639  }
640 
641  rect center_at(const float2& new_center) const
642  {
643  auto c = center();
644  auto diff_x = new_center.x - c.x;
645  auto diff_y = new_center.y - c.y;
646 
647  return { x + diff_x, y + diff_y, w, h };
648  }
649 
650  rect fit(rect r) const
651  {
652  float new_w = w;
653  float new_h = h;
654 
655  if (w < r.w)
656  new_w = r.w;
657 
658  if (h < r.h)
659  new_h = r.h;
660 
661  auto res = rect{x, y, new_w, new_h};
662  return res.adjust_ratio({w,h});
663  }
664 
665  rect zoom(float zoom_factor) const
666  {
667  auto c = center();
668  return scale(zoom_factor).center_at({c.x,c.y});
669  }
670 
671  rect enclose_in(rect in_rect) const
672  {
673  rect out_rect{x, y, w, h};
674  if (w > in_rect.w || h > in_rect.h)
675  {
676  return in_rect;
677  }
678 
679  if (x < in_rect.x)
680  {
681  out_rect.x = in_rect.x;
682  }
683 
684  if (y < in_rect.y)
685  {
686  out_rect.y = in_rect.y;
687  }
688 
689 
690  if (x + w > in_rect.x + in_rect.w)
691  {
692  out_rect.x = in_rect.x + in_rect.w - w;
693  }
694 
695  if (y + h > in_rect.y + in_rect.h)
696  {
697  out_rect.y = in_rect.y + in_rect.h - h;
698  }
699 
700  return out_rect;
701  }
702 
703  bool intersects(const rect& other) const
704  {
705  return other.contains({ x, y }) || other.contains({ x + w, y }) ||
706  other.contains({ x, y + h }) || other.contains({ x + w, y + h }) ||
707  contains({ other.x, other.y });
708  }
709  };
710 
712  // Simple font loading code //
714 
715 #include "../third-party/stb_easy_font.h"
716 
717  inline void draw_text(int x, int y, const char * text)
718  {
719  char buffer[60000]; // ~300 chars
721  glVertexPointer(2, GL_FLOAT, 16, buffer);
722  glDrawArrays(GL_QUADS, 0, 4 * stb_easy_font_print((float)x, (float)(y - 7), (char *)text, nullptr, buffer, sizeof(buffer)));
724  }
725 
727  // Image display code //
729 
730  class color_map
731  {
732  public:
733  color_map(std::map<float, float3> map, int steps = 4000) : _map(map)
734  {
735  initialize(steps);
736  }
737 
738  color_map(const std::vector<float3>& values, int steps = 4000)
739  {
740  for (size_t i = 0; i < values.size(); i++)
741  {
742  _map[(float)i/(values.size()-1)] = values[i];
743  }
744  initialize(steps);
745  }
746 
748 
749  float3 get(float value) const
750  {
751  if (_max == _min) return *_data;
752  auto t = (value - _min) / (_max - _min);
753  t = std::min(std::max(t, 0.f), 1.f);
754  return _data[(int)(t * (_size - 1))];
755  }
756 
757  float min_key() const { return _min; }
758  float max_key() const { return _max; }
759 
760  private:
761  float3 calc(float value) const
762  {
763  if (_map.size() == 0) return { value, value, value };
764  // if we have exactly this value in the map, just return it
765  if( _map.find(value) != _map.end() ) return _map.at(value);
766  // if we are beyond the limits, return the first/last element
767  if( value < _map.begin()->first ) return _map.begin()->second;
768  if( value > _map.rbegin()->first ) return _map.rbegin()->second;
769 
770  auto lower = _map.lower_bound(value) == _map.begin() ? _map.begin() : --(_map.lower_bound(value)) ;
771  auto upper = _map.upper_bound(value);
772 
773  auto t = (value - lower->first) / (upper->first - lower->first);
774  auto c1 = lower->second;
775  auto c2 = upper->second;
776  return lerp(c1, c2, t);
777  }
778 
779  void initialize(int steps)
780  {
781  if (_map.size() == 0) return;
782 
783  _min = _map.begin()->first;
784  _max = _map.rbegin()->first;
785 
786  _cache.resize(steps + 1);
787  for (int i = 0; i <= steps; i++)
788  {
789  auto t = (float)i/steps;
790  auto x = _min + t*(_max - _min);
791  _cache[i] = calc(x);
792  }
793 
794  // Save size and data to avoid STL checks penalties in DEBUG
795  _size = _cache.size();
796  _data = _cache.data();
797  }
798 
799  std::map<float, float3> _map;
800  std::vector<float3> _cache;
801  float _min, _max;
802  size_t _size; float3* _data;
803  };
804 
805  // Temporal event is a very simple time filter
806  // that allows a concensus based on a set of measurements in time
807  // You set the window, and add measurements, and the class offers
808  // the most agreed upon opinion within the set time window
809  // It is useful to remove noise from UX elements
811  {
812  public:
813  using clock = std::chrono::steady_clock;
814 
815  temporal_event(clock::duration window) : _window(window) {}
816  temporal_event() : _window(std::chrono::milliseconds(1000)) {}
817 
818  void add_value(bool val)
819  {
820  std::lock_guard<std::mutex> lock(_m);
821  _measurements.push_back(std::make_pair(clock::now(), val));
822  }
823 
824  bool eval()
825  {
826  return get_stat() > 0.5f;
827  }
828 
829  float get_stat()
830  {
831  std::lock_guard<std::mutex> lock(_m);
832 
833  if (_t.get_elapsed() < _window) return false; // Ensure no false alarms in the warm-up time
834 
835  _measurements.erase(std::remove_if(_measurements.begin(), _measurements.end(),
836  [this](std::pair<clock::time_point, bool> pair) {
837  return (clock::now() - pair.first) > _window;
838  }),
839  _measurements.end());
840  auto trues = std::count_if(_measurements.begin(), _measurements.end(),
841  [](std::pair<clock::time_point, bool> pair) {
842  return pair.second;
843  });
844  return size_t(trues) / (float)_measurements.size();
845  }
846 
847  void reset()
848  {
849  std::lock_guard<std::mutex> lock(_m);
850  _t.reset();
851  _measurements.clear();
852  }
853 
854  private:
855  std::mutex _m;
856  clock::duration _window;
857  std::vector<std::pair<clock::time_point, bool>> _measurements;
859  };
860 
862  {
864  rs2::frame_queue last_queue[2];
865  mutable rs2::frame last[2];
866  public:
867  std::shared_ptr<colorizer> colorize;
868  std::shared_ptr<yuy_decoder> yuy2rgb;
869  std::shared_ptr<depth_huffman_decoder> depth_decode;
870  bool zoom_preview = false;
871  rect curr_preview_rect{};
872  int texture_id = 0;
873 
875  {
876  texture = other.texture;
877  }
878 
880  {
881  texture = other.texture;
882  return *this;
883  }
884 
885  rs2::frame get_last_frame(bool with_texture = false) const {
886  auto idx = with_texture ? 1 : 0;
887  last_queue[idx].poll_for_frame(&last[idx]);
888  return last[idx];
889  }
890 
891  texture_buffer() : last_queue(), texture() {}
892 
894  // If the frame is already a GPU frame
895  // Just get the texture from the frame
896  if (auto gf = get_last_frame(true).as<gl::gpu_frame>())
897  {
898  auto tex = gf.get_texture_id(texture_id);
899  return tex;
900  }
901  else return texture;
902  }
903 
904  // Simplified version of upload that lets us load basic RGBA textures
905  // This is used for the splash screen
906  void upload_image(int w, int h, void* data, int format = GL_RGBA)
907  {
908  if (!texture)
909  glGenTextures(1, &texture);
910  glBindTexture(GL_TEXTURE_2D, texture);
917  }
918 
920  {
921  if (!texture)
922  glGenTextures(1, &texture);
923 
924  int width = 0;
925  int height = 0;
926  int stride = 0;
927  auto format = frame.get_profile().format();
928 
929  // Decode compressed data required for mouse pointer depth calculations
930  if (RS2_FORMAT_Z16H==format)
931  {
932  frame = depth_decode->process(frame);
933  format = frame.get_profile().format();
934  }
935 
936  last_queue[0].enqueue(frame);
937 
938  // When frame is a GPU frame
939  // we don't need to access pixels, keep data NULL
940  auto data = !frame.is<gl::gpu_frame>() ? frame.get_data() : nullptr;
941 
942  auto rendered_frame = frame;
943  auto image = frame.as<video_frame>();
944 
945  if (image)
946  {
947  width = image.get_width();
948  height = image.get_height();
949  stride = image.get_stride_in_bytes();
950  }
951  else if (auto profile = frame.get_profile().as<rs2::video_stream_profile>())
952  {
953  width = profile.width();
954  height = profile.height();
955  stride = width;
956  }
957 
958  glBindTexture(GL_TEXTURE_2D, texture);
959  stride = stride == 0 ? width : stride;
960  //glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
961 
962  // Allow upload of points frame type
963  if (auto pc = frame.as<points>())
964  {
965  if (!frame.is<gl::gpu_frame>())
966  {
967  // Points can be uploaded as two different
968  // formats: XYZ for verteces and UV for texture coordinates
969  if (prefered_format == RS2_FORMAT_XYZ32F)
970  {
971  // Upload vertices
972  data = pc.get_vertices();
973  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data);
974  }
975  else
976  {
977  // Upload texture coordinates
978  data = pc.get_texture_coordinates();
979  glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, width, height, 0, GL_RG, GL_FLOAT, data);
980  }
981 
984  }
985  else
986  {
987  // Update texture_id based on desired format
988  if (prefered_format == RS2_FORMAT_XYZ32F) texture_id = 0;
989  else texture_id = 1;
990  }
991  }
992  else
993  {
994  switch (format)
995  {
996  case RS2_FORMAT_ANY:
997  throw std::runtime_error("not a valid format");
998  case RS2_FORMAT_Z16H:
999  throw std::runtime_error("unexpected format: Z16H. Check decoder processing block");
1000  case RS2_FORMAT_Z16:
1003  if (frame.is<depth_frame>())
1004  {
1005  if (prefered_format == RS2_FORMAT_Z16)
1006  {
1007  glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, data);
1008  }
1009  else if (prefered_format == RS2_FORMAT_DISPARITY32)
1010  {
1011  glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, data);
1012  }
1013  else
1014  {
1015  if (auto colorized_frame = colorize->colorize(frame).as<video_frame>())
1016  {
1017  if (!colorized_frame.is<gl::gpu_frame>())
1018  {
1019  data = colorized_frame.get_data();
1020 
1022  colorized_frame.get_width(),
1023  colorized_frame.get_height(),
1025  data);
1026 
1027  }
1028  rendered_frame = colorized_frame;
1029  }
1030  }
1031  }
1032  else glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, data);
1033  break;
1034  case RS2_FORMAT_FG:
1036  break;
1037  case RS2_FORMAT_XYZ32F:
1038  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, data);
1039  break;
1040  case RS2_FORMAT_YUYV:
1041  if (yuy2rgb)
1042  {
1043  if (auto colorized_frame = yuy2rgb->process(frame).as<video_frame>())
1044  {
1045  if (!colorized_frame.is<gl::gpu_frame>())
1046  {
1047  glBindTexture(GL_TEXTURE_2D, texture);
1048  data = colorized_frame.get_data();
1049 
1051  colorized_frame.get_width(),
1052  colorized_frame.get_height(),
1054  colorized_frame.get_data());
1055  }
1056  rendered_frame = colorized_frame;
1057  }
1058  }
1059  else
1060  {
1062  }
1063  break;
1064  case RS2_FORMAT_UYVY: // Use luminance component only to avoid costly UVUY->RGB conversion
1066  break;
1067  case RS2_FORMAT_RGB8: case RS2_FORMAT_BGR8: // Display both RGB and BGR by interpreting them RGB, to show the flipped byte ordering. Obviously, GL_BGR could be used on OpenGL 1.2+
1068  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
1069  break;
1070  case RS2_FORMAT_RGBA8: case RS2_FORMAT_BGRA8: // Display both RGBA and BGRA by interpreting them RGBA, to show the flipped byte ordering. Obviously, GL_BGRA could be used on OpenGL 1.2+
1071  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
1072  break;
1073  case RS2_FORMAT_Y8:
1075  break;
1077  {
1078  if (auto motion = frame.as<motion_frame>())
1079  {
1080  auto axes = motion.get_motion_data();
1081  draw_motion_data(axes.x, axes.y, axes.z);
1082  }
1083  else
1084  {
1085  throw std::runtime_error("Not expecting a frame with motion format that is not a motion_frame");
1086  }
1087  break;
1088  }
1089  case RS2_FORMAT_Y16:
1090  case RS2_FORMAT_Y10BPACK:
1092  break;
1093  case RS2_FORMAT_RAW8:
1094  case RS2_FORMAT_MOTION_RAW:
1095  case RS2_FORMAT_GPIO_RAW:
1097  break;
1098  case RS2_FORMAT_6DOF:
1099  {
1100  if (auto pose = frame.as<pose_frame>())
1101  {
1102  rs2_pose pose_data = pose.get_pose_data();
1103  draw_pose_data(pose_data, frame.get_profile().unique_id());
1104  }
1105  else
1106  {
1107  throw std::runtime_error("Not expecting a frame with 6DOF format that is not a pose_frame");
1108  }
1109  break;
1110  }
1111  //case RS2_FORMAT_RAW10:
1112  //{
1113  // // Visualize Raw10 by performing a naive down sample. Each 2x2 block contains one red pixel, two green pixels, and one blue pixel, so combine them into a single RGB triple.
1114  // rgb.clear(); rgb.resize(width / 2 * height / 2 * 3);
1115  // auto out = rgb.data(); auto in0 = reinterpret_cast<const uint8_t *>(data), in1 = in0 + width * 5 / 4;
1116  // for (auto y = 0; y<height; y += 2)
1117  // {
1118  // for (auto x = 0; x<width; x += 4)
1119  // {
1120  // *out++ = in0[0]; *out++ = (in0[1] + in1[0]) / 2; *out++ = in1[1]; // RGRG -> RGB RGB
1121  // *out++ = in0[2]; *out++ = (in0[3] + in1[2]) / 2; *out++ = in1[3]; // GBGB
1122  // in0 += 5; in1 += 5;
1123  // }
1124  // in0 = in1; in1 += width * 5 / 4;
1125  // }
1126  // glPixelStorei(GL_UNPACK_ROW_LENGTH, width / 2); // Update row stride to reflect post-downsampling dimensions of the target texture
1127  // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width / 2, height / 2, 0, GL_RGB, GL_UNSIGNED_BYTE, rgb.data());
1128  //}
1129  //break;
1130  default:
1131  {
1132  memset((void*)data, 0, height*width);
1134  }
1135  }
1136 
1139  }
1140 
1144 
1145  last_queue[1].enqueue(rendered_frame);
1146  }
1147 
1148  static void draw_axes(float axis_size = 1.f, float axisWidth = 4.f)
1149  {
1150 
1151  // Triangles For X axis
1153  glColor3f(1.0f, 0.0f, 0.0f);
1154  glVertex3f(axis_size * 1.1f, 0.f, 0.f);
1155  glVertex3f(axis_size, -axis_size * 0.05f, 0.f);
1156  glVertex3f(axis_size, axis_size * 0.05f, 0.f);
1157  glVertex3f(axis_size * 1.1f, 0.f, 0.f);
1158  glVertex3f(axis_size, 0.f, -axis_size * 0.05f);
1159  glVertex3f(axis_size, 0.f, axis_size * 0.05f);
1160  glEnd();
1161 
1162  // Triangles For Y axis
1164  glColor3f(0.f, 1.f, 0.f);
1165  glVertex3f(0.f, axis_size * 1.1f, 0.0f);
1166  glVertex3f(0.f, axis_size, 0.05f * axis_size);
1167  glVertex3f(0.f, axis_size, -0.05f * axis_size);
1168  glVertex3f(0.f, axis_size * 1.1f, 0.0f);
1169  glVertex3f( 0.05f * axis_size, axis_size, 0.f);
1170  glVertex3f(-0.05f * axis_size, axis_size, 0.f);
1171  glEnd();
1172 
1173  // Triangles For Z axis
1175  glColor3f(0.0f, 0.0f, 1.0f);
1176  glVertex3f(0.0f, 0.0f, 1.1f * axis_size);
1177  glVertex3f(0.0f, 0.05f * axis_size, 1.0f * axis_size);
1178  glVertex3f(0.0f, -0.05f * axis_size, 1.0f * axis_size);
1179  glVertex3f(0.0f, 0.0f, 1.1f * axis_size);
1180  glVertex3f(0.05f * axis_size, 0.f, 1.0f * axis_size);
1181  glVertex3f(-0.05f * axis_size, 0.f, 1.0f * axis_size);
1182  glEnd();
1183 
1184  glLineWidth(axisWidth);
1185 
1186  // Drawing Axis
1187  glBegin(GL_LINES);
1188  // X axis - Red
1189  glColor3f(1.0f, 0.0f, 0.0f);
1190  glVertex3f(0.0f, 0.0f, 0.0f);
1191  glVertex3f(axis_size, 0.0f, 0.0f);
1192 
1193  // Y axis - Green
1194  glColor3f(0.0f, 1.0f, 0.0f);
1195  glVertex3f(0.0f, 0.0f, 0.0f);
1196  glVertex3f(0.0f, axis_size, 0.0f);
1197 
1198  // Z axis - Blue
1199  glColor3f(0.0f, 0.0f, 1.0f);
1200  glVertex3f(0.0f, 0.0f, 0.0f);
1201  glVertex3f(0.0f, 0.0f, axis_size);
1202  glEnd();
1203  }
1204 
1205  // intensity is grey intensity
1206  static void draw_circle(float xx, float xy, float xz, float yx, float yy, float yz, float radius = 1.1, float3 center = { 0.0, 0.0, 0.0 }, float intensity = 0.5f)
1207  {
1208  const auto N = 50;
1209  glColor3f(intensity, intensity, intensity);
1210  glLineWidth(2);
1212 
1213  for (int i = 0; i <= N; i++)
1214  {
1215  const double theta = (2 * M_PI / N) * i;
1216  const auto cost = static_cast<float>(cos(theta));
1217  const auto sint = static_cast<float>(sin(theta));
1218  glVertex3f(
1219  center.x + radius * (xx * cost + yx * sint),
1220  center.y + radius * (xy * cost + yy * sint),
1221  center.z + radius * (xz * cost + yz * sint)
1222  );
1223  }
1224 
1225  glEnd();
1226  }
1227 
1229  {
1230  const auto N = 4;
1231  for (int i = 0; i < N; i++)
1232  {
1233  result[i] = 0;
1234  for (int j = 0; j < N; j++)
1235  {
1236  result[i] += vec[j] * mat[N*j + i];
1237  }
1238  }
1239  return;
1240  }
1241 
1242  float2 xyz_to_xy(float x, float y, float z, GLfloat model[], GLfloat proj[], float vec_norm)
1243  {
1244  GLfloat vec[4] = { x, y, z, 0 };
1245  float tmp_result[4];
1246  float result[4];
1247 
1248  const auto canvas_size = 230;
1249 
1250  multiply_vector_by_matrix(vec, model, tmp_result);
1251  multiply_vector_by_matrix(tmp_result, proj, result);
1252 
1253  return{ canvas_size * vec_norm *result[0], canvas_size * vec_norm *result[1] };
1254  }
1255 
1256  void print_text_in_3d(float x, float y, float z, const char* text, bool center_text, GLfloat model[], GLfloat proj[], float vec_norm)
1257  {
1258  auto xy = xyz_to_xy(x, y, z, model, proj, vec_norm);
1259  auto w = (center_text) ? stb_easy_font_width((char*)text) : 0;
1260  glColor3f(1.0f, 1.0f, 1.0f);
1261  draw_text((int)(xy.x - w / 2), (int)xy.y, text);
1262  }
1263 
1264  void draw_motion_data(float x, float y, float z)
1265  {
1267  glPushMatrix();
1269  glPushMatrix();
1270 
1271  glViewport(0, 0, 768, 768);
1272  glClearColor(0, 0, 0, 1);
1274 
1276  glLoadIdentity();
1277 
1278  glOrtho(-2.8, 2.8, -2.4, 2.4, -7, 7);
1279 
1280  glRotatef(25, 1.0f, 0.0f, 0.0f);
1281 
1282  glTranslatef(0, -0.33f, -1.f);
1283 
1284  float norm = std::sqrt(x*x + y*y + z*z);
1285 
1286  glRotatef(-135, 0.0f, 1.0f, 0.0f);
1287 
1288  draw_axes();
1289 
1290  draw_circle(1, 0, 0, 0, 1, 0);
1291  draw_circle(0, 1, 0, 0, 0, 1);
1292  draw_circle(1, 0, 0, 0, 0, 1);
1293 
1294  const auto canvas_size = 230;
1295  const auto vec_threshold = 0.2f;
1296  if (norm < vec_threshold)
1297  {
1298  const auto radius = 0.05;
1299  static const int circle_points = 100;
1300  static const float angle = 2.0f * 3.1416f / circle_points;
1301 
1302  glColor3f(1.0f, 1.0f, 1.0f);
1304  double angle1 = 0.0;
1305  glVertex2d(radius * cos(0.0), radius * sin(0.0));
1306  int i;
1307  for (i = 0; i < circle_points; i++)
1308  {
1309  glVertex2d(radius * cos(angle1), radius *sin(angle1));
1310  angle1 += angle;
1311  }
1312  glEnd();
1313  }
1314  else
1315  {
1316  auto vectorWidth = 5.f;
1317  glLineWidth(vectorWidth);
1318  glBegin(GL_LINES);
1319  glColor3f(1.0f, 1.0f, 1.0f);
1320  glVertex3f(0.0f, 0.0f, 0.0f);
1321  glVertex3f(x / norm, y / norm, z / norm);
1322  glEnd();
1323 
1324  // Save model and projection matrix for later
1325  GLfloat model[16];
1327  GLfloat proj[16];
1329 
1330  glLoadIdentity();
1331  glOrtho(-canvas_size, canvas_size, -canvas_size, canvas_size, -1, +1);
1332 
1333  std::ostringstream s1;
1334  const auto precision = 3;
1335 
1336  s1 << std::setprecision(precision) << norm;
1337  print_text_in_3d(x / 2, y / 2, z / 2, s1.str().c_str(), true, model, proj, 1 / norm);
1338  }
1339 
1340  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 768, 768, 0);
1341 
1343  glPopMatrix();
1345  glPopMatrix();
1346  }
1347 
1348 
1349  void draw_grid(float step)
1350  {
1351  glLineWidth(1);
1352  glBegin(GL_LINES);
1353  glColor4f(0.1f, 0.1f, 0.1f, 0.8f);
1354 
1355  for (float x = -1.5; x < 1.5; x += step)
1356  {
1357  for (float y = -1.5; y < 1.5; y += step)
1358  {
1359  glVertex3f(x, y, 0);
1360  glVertex3f(x + step, y, 0);
1361  glVertex3f(x + step, y, 0);
1362  glVertex3f(x + step, y + step, 0);
1363  }
1364  }
1365  glEnd();
1366  }
1367 
1368  void draw_pose_data(const rs2_pose& pose, int id)
1369  {
1370  //TODO: use id if required to keep track of some state
1372  glPushMatrix();
1374  glPushMatrix();
1375 
1376  glViewport(0, 0, 1024, 1024);
1377 
1378  glClearColor(0, 0, 0, 1);
1380 
1382  glLoadIdentity();
1383 
1384  draw_grid(1.f);
1385  draw_axes(0.3f, 2.f);
1386 
1387  // Drawing pose:
1388  matrix4 pose_trans = tm2_pose_to_world_transformation(pose);
1389  float model[16];
1390  pose_trans.to_column_major(model);
1391 
1392  // set the pose transformation as the model matrix to draw the axis
1394  glPushMatrix();
1395  glLoadMatrixf(model);
1396 
1397  draw_axes(0.3f, 2.f);
1398 
1399  // remove model matrix from the rest of the render
1400  glPopMatrix();
1401 
1402  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1024, 1024, 0);
1403 
1405  glPopMatrix();
1407  glPopMatrix();
1408  }
1409 
1410  bool try_pick(int x, int y, float* result)
1411  {
1412  auto image = get_last_frame().as<video_frame>();
1413  if (!image) return false;
1414 
1415  auto format = image.get_profile().format();
1416  switch (format)
1417  {
1418  case RS2_FORMAT_Z16:
1419  case RS2_FORMAT_Y16:
1421  {
1422  auto ptr = (const uint16_t*)image.get_data();
1423  *result = ptr[y * (image.get_stride_in_bytes() / sizeof(uint16_t)) + x];
1424  return true;
1425  }
1427  {
1428  auto ptr = (const float*)image.get_data();
1429  *result = ptr[y * (image.get_stride_in_bytes() / sizeof(float)) + x];
1430  return true;
1431  }
1432  case RS2_FORMAT_RAW8:
1433  case RS2_FORMAT_Y8:
1434  {
1435  auto ptr = (const uint8_t*)image.get_data();
1436  *result = ptr[y * image.get_stride_in_bytes() + x];
1437  return true;
1438  }
1439  default:
1440  return false;
1441  }
1442  }
1443 
1444  void draw_texture(const rect& s, const rect& t) const
1445  {
1447  {
1448  glTexCoord2f(s.x, s.y + s.h); glVertex2f(t.x, t.y + t.h);
1449  glTexCoord2f(s.x, s.y); glVertex2f(t.x, t.y);
1450  glTexCoord2f(s.x + s.w, s.y + s.h); glVertex2f(t.x + t.w, t.y + t.h);
1451  glTexCoord2f(s.x + s.w, s.y); glVertex2f(t.x + t.w, t.y);
1452  }
1453  glEnd();
1454  }
1455 
1456  void show(const rect& r, float alpha, const rect& normalized_zoom = rect{0, 0, 1, 1}) const
1457  {
1458  glEnable(GL_BLEND);
1459 
1461  glBegin(GL_QUADS);
1462  glColor4f(1.0f, 1.0f, 1.0f, 1 - alpha);
1463  glEnd();
1464 
1465  glBindTexture(GL_TEXTURE_2D, get_gl_handle());
1466 
1468  draw_texture(normalized_zoom, r);
1469 
1472 
1474  }
1475 
1476  void show_preview(const rect& r, const rect& normalized_zoom = rect{0, 0, 1, 1})
1477  {
1478  glBindTexture(GL_TEXTURE_2D, get_gl_handle());
1479 
1481 
1482  // Show stream thumbnail
1483  static const rect unit_square_coordinates{0, 0, 1, 1};
1484  static const float2 thumbnail_size = {141, 141};
1485  static const float2 thumbnail_margin = { 10, 27 };
1486  rect thumbnail{r.x + r.w, r.y + r.h, thumbnail_size.x, thumbnail_size.y };
1487  thumbnail = thumbnail.adjust_ratio({r.w, r.h}).enclose_in(r.shrink_by(thumbnail_margin));
1488  rect zoomed_rect = normalized_zoom.unnormalize(r);
1489 
1490  if (r != zoomed_rect)
1491  {
1492  draw_texture(unit_square_coordinates, thumbnail);
1493 
1494  // Draw thumbnail border
1495  static const auto top_line_offset = 0.5f;
1496  static const auto right_line_offset = top_line_offset / 2;
1497  glColor3f(0.0, 0.0, 0.0);
1499  glVertex2f(thumbnail.x - top_line_offset, thumbnail.y - top_line_offset);
1500  glVertex2f(thumbnail.x + thumbnail.w + right_line_offset / 2, thumbnail.y - top_line_offset);
1501  glVertex2f(thumbnail.x + thumbnail.w + right_line_offset / 2, thumbnail.y + thumbnail.h + top_line_offset);
1502  glVertex2f(thumbnail.x - top_line_offset, thumbnail.y + thumbnail.h + top_line_offset);
1503  glEnd();
1504 
1505  curr_preview_rect = thumbnail;
1506  zoom_preview = true;
1507  }
1508  else
1509  {
1510  zoom_preview = false;
1511  }
1512 
1515 
1516  if (r != zoomed_rect)
1517  {
1518  // Draw ROI
1519  auto normalized_thumbnail_roi = normalized_zoom.unnormalize(thumbnail);
1520  glLineWidth(1);
1522  glColor4f(1,1,1,1);
1523  glVertex2f(normalized_thumbnail_roi.x, normalized_thumbnail_roi.y);
1524  glVertex2f(normalized_thumbnail_roi.x, normalized_thumbnail_roi.y + normalized_thumbnail_roi.h);
1525  glVertex2f(normalized_thumbnail_roi.x + normalized_thumbnail_roi.w, normalized_thumbnail_roi.y + normalized_thumbnail_roi.h);
1526  glVertex2f(normalized_thumbnail_roi.x + normalized_thumbnail_roi.w, normalized_thumbnail_roi.y);
1527  glVertex2f(normalized_thumbnail_roi.x, normalized_thumbnail_roi.y);
1528  glEnd();
1529  }
1530  }
1531  };
1532 
1533  // Helper class that lets smoothly animate between its values
1534  template<class T>
1535  class animated
1536  {
1537  private:
1538  T _old, _new;
1539  std::chrono::system_clock::time_point _last_update;
1540  std::chrono::system_clock::duration _duration;
1541  public:
1542  animated(T def, std::chrono::system_clock::duration duration = std::chrono::milliseconds(200))
1543  : _duration(duration), _old(def), _new(def)
1544  {
1545  static_assert((std::is_arithmetic<T>::value), "animated class supports arithmetic built-in types only");
1546  _last_update = std::chrono::system_clock::now();
1547  }
1548  animated& operator=(const T& other)
1549  {
1550  if (other != _new)
1551  {
1552  _old = get();
1553  _new = other;
1554  _last_update = std::chrono::system_clock::now();
1555  }
1556  return *this;
1557  }
1558  T get() const
1559  {
1561  auto ms = std::chrono::duration_cast<std::chrono::microseconds>(now - _last_update).count();
1562  auto duration_ms = std::chrono::duration_cast<std::chrono::microseconds>(_duration).count();
1563  auto t = (float)ms / duration_ms;
1564  t = std::max(0.f, std::min(rs2::smoothstep(t, 0.f, 1.f), 1.f));
1565  return static_cast<T>(_old * (1.f - t) + _new * t);
1566  }
1567  operator T() const { return get(); }
1568  T value() const { return _new; }
1569  };
1570 
1571  inline bool is_integer(float f)
1572  {
1573  return (fabs(fmod(f, 1)) < std::numeric_limits<float>::min());
1574  }
1575 
1576  struct to_string
1577  {
1578  std::ostringstream ss;
1579  template<class T> to_string & operator << (const T & val) { ss << val; return *this; }
1580  operator std::string() const { return ss.str(); }
1581  };
1582 
1584  {
1586  << " in " << e.get_failed_function() << "("
1587  << e.get_failed_args() << "):\n" << e.what();
1588  }
1589 
1591  {
1592  if (version / 10000 == 0) return to_string() << version;
1593  return to_string() << (version / 10000) << "." << (version % 10000) / 100 << "." << (version % 100);
1594  }
1595 
1596  // Comparing parameter against a range of values of the same type
1597  // https://stackoverflow.com/questions/15181579/c-most-efficient-way-to-compare-a-variable-to-multiple-values
1598  template <typename T>
1599  bool val_in_range(const T& val, const std::initializer_list<T>& list)
1600  {
1601  for (const auto& i : list) {
1602  if (val == i) {
1603  return true;
1604  }
1605  }
1606  return false;
1607  }
1608 
1610  {
1611  // Check whether the produced
1612  switch (format)
1613  {
1614  case RS2_FORMAT_ANY:
1615  case RS2_FORMAT_XYZ32F:
1616  case RS2_FORMAT_MOTION_RAW:
1618  case RS2_FORMAT_GPIO_RAW:
1619  case RS2_FORMAT_6DOF:
1620  return false;
1621  default:
1622  return true;
1623  }
1624  }
1625 
1626  inline float to_rad(float deg)
1627  {
1628  return static_cast<float>(deg * (M_PI / 180.f));
1629  }
1630 
1632  {
1633  matrix4 data;
1634  for (int i = 0; i < 4; i++)
1635  for (int j = 0; j < 4; j++)
1636  data.mat[i][j] = (i == j) ? 1.f : 0.f;
1637  return data;
1638  }
1639 
1640  // Single-Wave - helper function that smoothly goes from 0 to 1 between 0 and 0.5,
1641  // and then smoothly returns from 1 to 0 between 0.5 and 1.0, and stays 0 anytime after
1642  // Useful to animate variable on and off based on last time something happened
1643  inline float single_wave(float x)
1644  {
1645  auto c = clamp(x, 0.f, 1.f);
1646  return 0.5f * (sinf(2.f * float(M_PI) * c - float(M_PI_2)) + 1.f);
1647  }
1648 
1649  // convert 3d points into 2d viewport coordinates
1651  {
1652  //
1653  // retrieve model view and projection matrix
1654  //
1655  // RS2_GL_MATRIX_CAMERA contains the model view matrix
1656  // RS2_GL_MATRIX_TRANSFORMATION is identity matrix
1657  // RS2_GL_MATRIX_PROJECTION is the projection matrix
1658  //
1659  // internal representation is in column major order, i.e., 13th, 14th, and 15th elelments
1660  // of the 16 element model view matrix represents translations
1661  // float mat[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
1662  //
1663  // rs2::matrix4 m = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15} };
1664  // 0 1 2 3
1665  // 4 5 6 7
1666  // 8 9 10 11
1667  // 12 13 14 15
1668  //
1669  // when use matrix4 in glUniformMatrix4fv, transpose option is GL_FALSE so data is passed into
1670  // shader as column major order
1671  //
1672  // rs2::matrix4 p = get_matrix(RS2_GL_MATRIX_PROJECTION);
1673  // rs2::matrix4 v = get_matrix(RS2_GL_MATRIX_CAMERA);
1674  // rs2::matrix4 f = get_matrix(RS2_GL_MATRIX_TRANSFORMATION);
1675 
1676  // matrix * operation in column major, transpose matrix
1677  // 0 4 8 12
1678  // 1 5 9 13
1679  // 2 6 10 14
1680  // 3 7 11 15
1681  //
1682  matrix4 vc;
1683  matrix4 pc;
1684  matrix4 fc;
1685 
1686  for (int i = 0; i < 4; i++)
1687  {
1688  for (int j = 0; j < 4; j++)
1689  {
1690  pc(i, j) = p(j, i);
1691  vc(i, j) = v(j, i);
1692  fc(i, j) = f(j, i);
1693  }
1694  }
1695 
1696  // obtain the final transformation matrix
1697  auto mvp = pc * vc * fc;
1698 
1699  // test - origin (0, 0, -1.0, 1) should be translated into (0, 0, 0, 0) at this point
1700  //float4 origin{ 0.f, 0.f, -1.f, 1.f };
1701 
1702  // translate 3d vertex into 2d windows coordinates
1703  float4 p3d;
1704  p3d.x = point.x;
1705  p3d.y = point.y;
1706  p3d.z = point.z;
1707  p3d.w = 1.0;
1708 
1709  // transform from object coordinates into clip coordinates
1710  float4 p2d = mvp * p3d;
1711 
1712  // clip to [-w, w] and normalize
1713  if (abs(p2d.w) > 0.0)
1714  {
1715  p2d.x /= p2d.w;
1716  p2d.y /= p2d.w;
1717  p2d.z /= p2d.w;
1718  p2d.w /= p2d.w;
1719  }
1720 
1721  p2d.x = clamp(p2d.x, -1.0, 1.0);
1722  p2d.y = clamp(p2d.y, -1.0, 1.0);
1723  p2d.z = clamp(p2d.z, -1.0, 1.0);
1724 
1725  // viewport coordinates
1726  float x_vp = round((p2d.x + 1.f) / 2.f * vp[2]) + vp[0];
1727  float y_vp = round((p2d.y + 1.f) / 2.f * vp[3]) + vp[1];
1728 
1729  float2 p_w;
1730  p_w.x = x_vp;
1731  p_w.y = y_vp;
1732 
1733  return p_w;
1734  }
1735 }
float y
Definition: rendering.h:499
void draw_text(int x, int y, const char *text)
Definition: rendering.h:717
float smoothstep(float x, float min, float max)
Definition: rendering.h:108
float3 cross(const float3 &a, const float3 &b)
Definition: rendering.h:153
static const textual_icon lock
Definition: model-views.h:218
#define glDisableClientState
float lerp(float a, float b, float t)
Definition: rendering.h:122
GLuint get_gl_handle() const
Definition: rendering.h:893
bool intersects(const rect &other) const
Definition: rendering.h:703
#define GL_TEXTURE_MAG_FILTER
#define glEnableClientState
animated(T def, std::chrono::system_clock::duration duration=std::chrono::milliseconds(200))
Definition: rendering.h:1542
#define GL_LINE_LOOP
float to_rad(float deg)
Definition: rendering.h:1626
GLboolean GLboolean GLboolean b
void show_preview(const rect &r, const rect &normalized_zoom=rect{0, 0, 1, 1})
Definition: rendering.h:1476
void upload(rs2::frame frame, rs2_format prefered_format=RS2_FORMAT_ANY)
Definition: rendering.h:919
GLint y
#define GL_RG8
rect center() const
Definition: rendering.h:593
bool is_integer(float f)
Definition: rendering.h:1571
static matrix4 identity()
Definition: rendering.h:281
std::ostringstream ss
Definition: rendering.h:1578
float3 normalize() const
Definition: rendering.h:142
khronos_float_t GLfloat
void add_timestamp(double timestamp, unsigned long long frame_counter)
Definition: rendering.h:66
matrix4(float vals[16])
Definition: rendering.h:308
void draw_axes()
Definition: rs-motion.cpp:15
#define glRotatef
#define GL_VERTEX_ARRAY
float length() const
Definition: rendering.h:192
#define GL_RG16F
#define GL_QUAD_STRIP
bool operator==(const plane &lhs, const plane &rhs)
Definition: rendering.h:134
float z
Definition: rs_types.h:131
matrix4(const rs2_quaternion &q)
Definition: rendering.h:323
std::vector< std::pair< clock::time_point, bool > > _measurements
Definition: rendering.h:857
GLdouble s
#define GL_TEXTURE_2D
#define GL_RGBA
#define glVertexPointer
temporal_event(clock::duration window)
Definition: rendering.h:815
#define glOrtho
#define glBegin
GLfloat GLfloat p
Definition: glext.h:12687
const GLfloat * m
Definition: glext.h:6814
void draw_grid(float step)
Definition: rendering.h:1349
#define glCopyTexImage2D
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:10806
#define GL_UNSIGNED_BYTE
rect intersection(const rect &other) const
Definition: rendering.h:543
bool try_pick(int x, int y, float *result)
Definition: rendering.h:1410
def axes(out, pos, rotation=np.eye(3), size=0.075, thickness=2)
GLdouble GLdouble GLdouble y2
#define glPopMatrix
color_map(const std::vector< float3 > &values, int steps=4000)
Definition: rendering.h:738
#define GL_PROJECTION
rect shrink_by(float2 pixels) const
Definition: rendering.h:636
std::enable_if< std::is_base_of< rs2::frame, T >::value, bool >::type poll_for_frame(T *output) const
static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size)
GLfloat value
const float & operator()(int i, int j) const
Definition: rendering.h:320
stream_profile get_profile() const
Definition: rs_frame.hpp:557
void enqueue(frame f) const
rect center_at(const float2 &new_center) const
Definition: rendering.h:641
unsigned long long _num_of_frames
Definition: rendering.h:98
#define glDrawArrays
rs2_vector translation
Definition: rs_types.h:142
rect pan(const float2 &p) const
Definition: rendering.h:588
rs2_quaternion to_quaternion()
Definition: rendering.h:351
#define GL_RGB16F
color_map(std::map< float, float3 > map, int steps=4000)
Definition: rendering.h:733
#define GL_CLAMP_TO_EDGE
std::vector< float3 > _cache
Definition: rendering.h:800
GLfloat angle
Definition: glext.h:6819
float2 normalize() const
Definition: rendering.h:194
#define GL_R32F
void operator=(const rect &other)
Definition: rendering.h:502
#define GL_POLYGON
const void * get_data() const
Definition: rs_frame.hpp:545
unsigned short uint16_t
Definition: stdint.h:79
std::array< float3, 4 > plane_3d
Definition: rendering.h:217
std::mutex _mtx
Definition: rendering.h:100
Definition: cah-model.h:10
GLdouble GLdouble GLdouble w
GLsizei const GLchar *const * string
std::vector< plane_3d > subdivide(const plane_3d &rect, int parts=4)
Definition: rendering.h:219
#define GL_BLEND
float3 operator+(const float3 &a, const float3 &b)
Definition: rendering.h:173
#define GL_LINEAR
std::chrono::steady_clock clock
Definition: rendering.h:813
#define glLoadIdentity
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:1960
GLdouble GLdouble z
unsigned char uint8_t
Definition: stdint.h:78
rect unnormalize(const rect &unnormalize_to) const
Definition: rendering.h:533
#define glTexImage2D
float evaluate_plane(const plane &plane, const float3 &point)
Definition: rendering.h:158
#define glColor4f
#define GL_CLAMP
#define glVertex3f
#define GL_SRC_ALPHA
e
Definition: rmse.py:177
point
Definition: rmse.py:166
GLenum GLfloat * buffer
float a
Definition: rendering.h:129
#define glEnable
#define GL_LUMINANCE
#define glGetFloatv
::realsense_legacy_msgs::pose_< std::allocator< void > > pose
Definition: pose.h:88
std::ostream & operator<<(std::ostream &os, const textual_icon &i)
Definition: model-views.h:118
GLenum GLenum GLsizei void * image
#define GL_TEXTURE_WRAP_T
bool is_valid(const plane_3d &p)
Definition: rendering.h:243
T unnormalizeT(const T &in_val, const T &min, const T &max)
Definition: rendering.h:491
rect fit(rect r) const
Definition: rendering.h:650
clock::duration _window
Definition: rendering.h:856
GLdouble t
GLboolean GLboolean GLboolean GLboolean a
GLuint GLfloat * val
const int H
const char * rs2_exception_type_to_string(rs2_exception_type type)
Definition: rs.cpp:1272
float x
Definition: rendering.h:499
Quaternion used to represent rotation.
Definition: rs_types.h:135
const std::string & get_failed_args() const
Definition: rs_types.hpp:117
std::string error_to_string(const error &e)
Definition: rendering.h:1583
float y
Definition: rs_types.h:131
GLdouble f
T normalizeT(const T &in_val, const T &min, const T &max)
Definition: rendering.h:484
static void draw_circle(float xx, float xy, float xz, float yx, float yy, float yz, float radius=1.1, float3 center={0.0, 0.0, 0.0}, float intensity=0.5f)
Definition: rendering.h:1206
bool is() const
Definition: rs_frame.hpp:570
rs2_pose correct_tm2_pose(const rs2_pose &pose)
Definition: rendering.h:463
const int W
int xx
Definition: rmse.py:52
bool contains(const std::shared_ptr< librealsense::device_info > &first, const std::shared_ptr< librealsense::device_info > &second)
Definition: context.cpp:49
GLfloat GLfloat GLfloat alpha
#define glGenTextures
#define GL_FLOAT
GLsizeiptr size
float h
Definition: rendering.h:500
#define GL_COLOR_BUFFER_BIT
int yy
Definition: rmse.py:53
#define glTexParameteri
const GLubyte * c
Definition: glext.h:12690
GLdouble GLdouble r
double _delta
Definition: rendering.h:96
#define GL_LINES
GLenum GLint GLint * precision
Definition: glext.h:1883
GLdouble x
#define glLineWidth
static const int _numerator
Definition: rendering.h:93
std::shared_ptr< depth_huffman_decoder > depth_decode
Definition: rendering.h:869
#define glPushMatrix
float3 operator-(const float3 &a, const float3 &b)
Definition: rendering.h:178
GLdouble GLdouble x2
#define glLoadMatrixf
#define GL_PROJECTION_MATRIX
rs2::frame get_last_frame(bool with_texture=false) const
Definition: rendering.h:885
#define glClear
float min_key() const
Definition: rendering.h:757
GLint GLsizei GLsizei height
#define GL_UNSIGNED_SHORT
float single_wave(float x)
Definition: rendering.h:1643
GLint GLint GLsizei GLint GLenum format
float dot(const rs2::float2 &a, const rs2::float2 &b)
Definition: rendering.h:200
rs2_quaternion rotation
Definition: rs_types.h:145
#define glTranslatef
matrix4(const rs2_vector &t)
Definition: rendering.h:332
#define GL_TEXTURE_MIN_FILTER
#define GL_QUADS
rs2_intrinsics normalize(const rs2_intrinsics &intr)
Definition: l500-color.cpp:272
float3 operator/(const float3 &a, float t)
Definition: rendering.h:168
#define glEnd
GLint j
rect grow(int pixels) const
Definition: rendering.h:626
GLdouble GLdouble GLint stride
std::chrono::system_clock::time_point _last_update
Definition: rendering.h:1539
rs2_format
A stream&#39;s format identifies how binary data is encoded within a frame.
Definition: rs_sensor.h:59
utilities::time::stopwatch _t
Definition: rendering.h:858
float3 operator*(const float3 &a, float t)
Definition: rendering.h:163
texture_buffer(const texture_buffer &other)
Definition: rendering.h:874
double _last_timestamp
Definition: rendering.h:97
float c
Definition: rendering.h:131
float mat[4][4]
Definition: rendering.h:274
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
float x
Definition: rs_types.h:131
#define GL_RG
double get_fps() const
Definition: rendering.h:83
std::shared_ptr< colorizer > colorize
Definition: rendering.h:867
#define glViewport
rs2_exception_type get_type() const
Definition: rs_types.hpp:122
float w
Definition: rendering.h:500
#define glTexCoord2f
#define GL_MODELVIEW
void show(const rect &r, float alpha, const rect &normalized_zoom=rect{0, 0, 1, 1}) const
Definition: rendering.h:1456
#define GL_ONE_MINUS_SRC_ALPHA
void add_value(bool val)
Definition: rendering.h:818
rect cut_by(const rect &r) const
Definition: rendering.h:563
bool is_rasterizeable(rs2_format format)
Definition: rendering.h:1609
float2 xyz_to_xy(float x, float y, float z, GLfloat model[], GLfloat proj[], float vec_norm)
Definition: rendering.h:1242
texture_buffer & operator=(const texture_buffer &other)
Definition: rendering.h:879
void draw_pose_data(const rs2_pose &pose, int id)
Definition: rendering.h:1368
GLsizei const GLfloat * values
GLdouble GLdouble GLint GLint GLdouble v1
fps_calc(const fps_calc &other)
Definition: rendering.h:58
void print_text_in_3d(float x, float y, float z, const char *text, bool center_text, GLfloat model[], GLfloat proj[], float vec_norm)
Definition: rendering.h:1256
rs2_format format() const
Definition: rs_frame.hpp:44
GLuint GLfloat GLfloat GLfloat x1
Definition: glext.h:9721
#define glVertex2d
3D vector in Euclidean coordinate space
Definition: rs_types.h:129
#define glColor3f
bool operator!=(const rect &other) const
Definition: rendering.h:520
bool val_in_range(const T &val, const std::initializer_list< T > &list)
Definition: rendering.h:1599
GLdouble GLdouble GLdouble q
#define GL_RED
unsigned int GLuint
static int stb_easy_font_width(char *text)
#define GL_LUMINANCE_ALPHA
float length() const
Definition: rendering.h:140
float3 calc(float value) const
Definition: rendering.h:761
void initialize(int steps)
Definition: rendering.h:779
std::map< float, float3 > _map
Definition: rendering.h:799
int min(int a, int b)
Definition: lz4s.c:73
bool operator==(const rect &other) const
Definition: rendering.h:515
float clamp(float x, float min, float max)
Definition: rendering.h:103
rect scale(float factor) const
Definition: rendering.h:621
#define glBindTexture
GLint GLsizei count
float max_key() const
Definition: rendering.h:758
bool contains(const float2 &p) const
Definition: rendering.h:583
#define GL_MODELVIEW_MATRIX
#define glClearColor
std::string api_version_to_string(int version)
Definition: rendering.h:1590
rect enclose_in(rect in_rect) const
Definition: rendering.h:671
#define GL_LINE_STRIP
rect normalize(const rect &normalize_to) const
Definition: rendering.h:525
unsigned long long _last_frame_counter
Definition: rendering.h:99
float d
Definition: rendering.h:132
rect lerp(float t, const rect &other) const
Definition: rendering.h:598
static void draw_axes(float axis_size=1.f, float axisWidth=4.f)
Definition: rendering.h:1148
GLuint texture
int i
rect adjust_ratio(float2 size) const
Definition: rendering.h:606
GLenum GLuint GLenum GLsizei length
GLuint res
Definition: glext.h:8856
matrix4 identity_matrix()
Definition: rendering.h:1631
float & operator()(int i, int j)
Definition: rendering.h:319
#define GL_RGB
void to_column_major(float column_major[16])
Definition: rendering.h:391
const GLdouble * v2
float b
Definition: rendering.h:130
float2 translate_3d_to_2d(float3 point, matrix4 p, matrix4 v, matrix4 f, int32_t vp[4])
Definition: rendering.h:1650
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition: glext.h:9721
std::chrono::system_clock::duration _duration
Definition: rendering.h:1540
#define glVertex2f
void draw_grid()
signed int int32_t
Definition: stdint.h:77
int unique_id() const
Definition: rs_frame.hpp:54
rect grow(int dx, int dy) const
Definition: rendering.h:631
void multiply_vector_by_matrix(GLfloat vec[], GLfloat mat[], GLfloat *result)
Definition: rendering.h:1228
std::shared_ptr< yuy_decoder > yuy2rgb
Definition: rendering.h:868
#define glBlendFunc
size_t _size
Definition: rendering.h:802
void draw_motion_data(float x, float y, float z)
Definition: rendering.h:1264
animated & operator=(const T &other)
Definition: rendering.h:1548
#define GL_NEAREST
matrix4(float vals[4][4])
Definition: rendering.h:294
rs2_quaternion normalize(rs2_quaternion a)
Definition: rendering.h:340
GLboolean * data
float area() const
Definition: rendering.h:558
matrix4 tm2_pose_to_world_transformation(const rs2_pose &pose)
Definition: rendering.h:441
void draw_texture(const rect &s, const rect &t) const
Definition: rendering.h:1444
GLuint64EXT * result
Definition: glext.h:10921
GLdouble v
rect zoom(float zoom_factor) const
Definition: rendering.h:665
#define GL_TEXTURE_WRAP_S
T value() const
Definition: rendering.h:1568
static const int _skip_frames
Definition: rendering.h:94
void upload_image(int w, int h, void *data, int format=GL_RGBA)
Definition: rendering.h:906
YYCODETYPE lhs
Definition: sqlite3.c:132469
int get_width() const
Definition: rs_frame.hpp:659
GeneratorWrapper< T > map(Func &&function, GeneratorWrapper< U > &&generator)
Definition: catch.hpp:4271
GLdouble y1
#define glMatrixMode
const std::string & get_failed_function() const
Definition: rs_types.hpp:112
Definition: parser.hpp:150
GLint GLsizei width
#define GL_TRIANGLES
#define glDisable
T as() const
Definition: rs_frame.hpp:580
std::string to_string(T value)


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:47:40