arcball_camera.h
Go to the documentation of this file.
1 #ifndef ARCBALL_CAMERA_H
2 #define ARCBALL_CAMERA_H
3 
4 #ifdef __cplusplus
5 extern "C" {
6 #endif // __cplusplus
7 
8 // Flags for tweaking the view matrix
9 #define ARCBALL_CAMERA_LEFT_HANDED_BIT 1
10 
11 // * eye:
12 // * Current eye position. Will be updated to new eye position.
13 // * target:
14 // * Current look target position. Will be updated to new position.
15 // * up:
16 // * Camera's "up" direction. Will be updated to new up vector.
17 // * view (optional):
18 // * The matrix that will be updated with the new view transform. Previous contents don't matter.
19 // * delta_time_seconds:
20 // * Amount of seconds passed since last update.
21 // * zoom_per_tick:
22 // * How much the camera should zoom with every scroll wheel tick.
23 // * pan_speed:
24 // * How fast the camera should pan when holding middle click.
25 // * rotation_multiplier:
26 // * For amplifying the rotation speed. 1.0 means 1-1 mapping between arcball rotation and camera rotation.
27 // * screen_width/screen_height:
28 // * Dimensions of the screen the camera is being used in (the window size).
29 // * x0, x1:
30 // * Previous and current x coordinate of the mouse, respectively.
31 // * y0, y1:
32 // * Previous and current y coordinate of the mouse, respectively.
33 // * midclick_held:
34 // * Whether the middle click button is currently held or not.
35 // * rclick_held:
36 // * Whether the right click button is currently held or not.
37 // * delta_scroll_ticks:
38 // * How many scroll wheel ticks passed since the last update (signed number)
39 // * flags:
40 // * For producing a different view matrix depending on your conventions.
42  float eye[3],
43  float target[3],
44  float up[3],
45  float view[16],
46  float delta_time_seconds,
47  float zoom_per_tick,
48  float pan_speed,
49  float rotation_multiplier,
50  int screen_width, int screen_height,
51  int x0, int x1,
52  int y0, int y1,
53  int midclick_held,
54  int rclick_held,
55  int delta_scroll_ticks,
56  unsigned int flags);
57 
58 // Utility for producing a look-to matrix without having to update a camera.
60  const float eye[3],
61  const float look[3],
62  const float up[3],
63  float view[16],
64  unsigned int flags);
65 
66 #ifdef __cplusplus
67 }
68 #endif // __cplusplus
69 
70 #endif // ARCBALL_CAMERA_H
71 
72 #ifdef ARCBALL_CAMERA_IMPLEMENTATION
73 
74 #include <math.h>
75 #include <assert.h>
76 
77 #ifdef __cplusplus
78 extern "C" {
79 #endif // __cplusplus
80 
82  float eye[3],
83  float target[3],
84  float up[3],
85  float view[16],
86  float delta_time_seconds,
87  float zoom_per_tick,
88  float pan_speed,
89  float rotation_multiplier,
90  int screen_width, int screen_height,
91  int px_x0, int px_x1,
92  int px_y0, int px_y1,
93  int midclick_held,
94  int rclick_held,
95  int delta_scroll_ticks,
96  unsigned int flags)
97 {
98  // check preconditions
99  {
100  float up_len = sqrtf(up[0] * up[0] + up[1] * up[1] + up[2] * up[2]);
101  assert(fabsf(up_len - 1.0f) < 0.000001f);
102 
103  float to_target[3] = {
104  target[0] - eye[0],
105  target[1] - eye[1],
106  target[2] - eye[2],
107  };
108  float to_target_len = sqrtf(to_target[0] * to_target[0] + to_target[1] * to_target[1] + to_target[2] * to_target[2]);
109  assert(to_target_len > 1e-6);
110  }
111 
112  // right click is held, then mouse movements implement rotation.
113  if (rclick_held)
114  {
115  float x0 = (float)(px_x0 - screen_width / 2);
116  float x1 = (float)(px_x1 - screen_width / 2);
117  float y0 = (float)((screen_height - px_y0 - 1) - screen_height / 2);
118  float y1 = (float)((screen_height - px_y1 - 1) - screen_height / 2);
119  float arcball_radius = (float)(screen_width > screen_height ? screen_width : screen_height);
120 
121  // distances to center of arcball
122  float dist0 = sqrtf(x0 * x0 + y0 * y0);
123  float dist1 = sqrtf(x1 * x1 + y1 * y1);
124 
125  float z0;
126  if (dist0 > arcball_radius)
127  {
128  // initial click was not on the arcball, so just do nothing.
129  goto end_rotate;
130  }
131  else
132  {
133  // compute depth of intersection using good old pythagoras
134  z0 = sqrtf(arcball_radius * arcball_radius - x0 * x0 - y0 * y0);
135  }
136 
137  float z1;
138  if (dist1 > arcball_radius)
139  {
140  // started inside the ball but went outside, so clamp it.
141  x1 = (x1 / dist1) * arcball_radius;
142  y1 = (y1 / dist1) * arcball_radius;
143  dist1 = arcball_radius;
144  z1 = 0.0f;
145  }
146  else
147  {
148  // compute depth of intersection using good old pythagoras
149  z1 = sqrtf(arcball_radius * arcball_radius - x1 * x1 - y1 * y1);
150  }
151 
152  // rotate intersection points according to where the eye is
153  {
154  float to_eye_unorm[3] = {
155  eye[0] - target[0],
156  eye[1] - target[1],
157  eye[2] - target[2]
158  };
159  float to_eye_len = sqrtf(to_eye_unorm[0] * to_eye_unorm[0] + to_eye_unorm[1] * to_eye_unorm[1] + to_eye_unorm[2] * to_eye_unorm[2]);
160  float to_eye[3] = {
161  to_eye_unorm[0] / to_eye_len,
162  to_eye_unorm[1] / to_eye_len,
163  to_eye_unorm[2] / to_eye_len
164  };
165 
166  float across[3] = {
167  -(to_eye[1] * up[2] - to_eye[2] * up[1]),
168  -(to_eye[2] * up[0] - to_eye[0] * up[2]),
169  -(to_eye[0] * up[1] - to_eye[1] * up[0])
170  };
171 
172  // matrix that transforms standard coordinates to be relative to the eye
173  float eye_m[9] = {
174  across[0], across[1], across[2],
175  up[0], up[1], up[2],
176  to_eye[0], to_eye[1], to_eye[2]
177  };
178 
179  float new_p0[3] = {
180  eye_m[0] * x0 + eye_m[3] * y0 + eye_m[6] * z0,
181  eye_m[1] * x0 + eye_m[4] * y0 + eye_m[7] * z0,
182  eye_m[2] * x0 + eye_m[5] * y0 + eye_m[8] * z0,
183  };
184 
185  float new_p1[3] = {
186  eye_m[0] * x1 + eye_m[3] * y1 + eye_m[6] * z1,
187  eye_m[1] * x1 + eye_m[4] * y1 + eye_m[7] * z1,
188  eye_m[2] * x1 + eye_m[5] * y1 + eye_m[8] * z1,
189  };
190 
191  x0 = new_p0[0];
192  y0 = new_p0[1];
193  z0 = new_p0[2];
194 
195  x1 = new_p1[0];
196  y1 = new_p1[1];
197  z1 = new_p1[2];
198  }
199 
200  // compute quaternion between the two vectors (http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final)
201  float qw, qx, qy, qz;
202  {
203  float norm_u_norm_v = sqrtf((x0 * x0 + y0 * y0 + z0 * z0) * (x1 * x1 + y1 * y1 + z1 * z1));
204  qw = norm_u_norm_v + (x0 * x1 + y0 * y1 + z0 * z1);
205 
206  if (qw < 1.e-6f * norm_u_norm_v)
207  {
208  /* If u and v are exactly opposite, rotate 180 degrees
209  * around an arbitrary orthogonal axis. Axis normalisation
210  * can happen later, when we normalise the quaternion. */
211  qw = 0.0f;
212  if (fabsf(x0) > fabsf(z0))
213  {
214  qx = -y0;
215  qy = x0;
216  qz = 0.0f;
217  }
218  else
219  {
220  qx = 0.0f;
221  qy = -z0;
222  qz = y0;
223  }
224  }
225  else
226  {
227  /* Otherwise, build quaternion the standard way. */
228  qx = y0 * z1 - z0 * y1;
229  qy = z0 * x1 - x0 * z1;
230  qz = x0 * y1 - y0 * x1;
231  }
232 
233  float q_len = sqrtf(qx * qx + qy * qy + qz * qz + qw * qw);
234  qx /= q_len;
235  qy /= q_len;
236  qz /= q_len;
237  qw /= q_len;
238  }
239 
240  // amplify the quaternion's rotation by the multiplier
241  // this is done by slerp(Quaternion.identity, q, multiplier)
242  // math from http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
243  {
244  // cos(angle) of the quaternion
245  float c = qw;
246  if (c > 0.9995f)
247  {
248  // if the angle is small, linearly interpolate and normalize.
249  qx = rotation_multiplier * qx;
250  qy = rotation_multiplier * qy;
251  qz = rotation_multiplier * qz;
252  qw = 1.0f + rotation_multiplier * (qw - 1.0f);
253  float q_len = sqrtf(qx * qx + qy * qy + qz * qz + qw * qw);
254  qx /= q_len;
255  qy /= q_len;
256  qz /= q_len;
257  qw /= q_len;
258  }
259  else
260  {
261  // clamp to domain of acos for robustness
262  if (c < -1.0f)
263  c = -1.0f;
264  else if (c > 1.0f)
265  c = 1.0f;
266  // angle of the initial rotation
267  float theta_0 = acosf(c);
268  // apply multiplier to rotation
269  float theta = theta_0 * rotation_multiplier;
270 
271  // compute the quaternion normalized difference
272  float qx2 = qx;
273  float qy2 = qy;
274  float qz2 = qz;
275  float qw2 = qw - c;
276  float q2_len = sqrtf(qx2 * qx2 + qy2 * qy2 + qz2 * qz2 + qw2 * qw2);
277  qx2 /= q2_len;
278  qy2 /= q2_len;
279  qz2 /= q2_len;
280  qw2 /= q2_len;
281 
282  // do the slerp
283  qx = qx2 * sinf(theta);
284  qy = qy2 * sinf(theta);
285  qz = qz2 * sinf(theta);
286  qw = cosf(theta) + qw2 * sinf(theta);
287  }
288  }
289 
290  // vector from the target to the eye, which will be rotated according to the arcball's arc.
291  float to_eye[3] = { eye[0] - target[0], eye[1] - target[1], eye[2] - target[2] };
292 
293  // convert quaternion to matrix (note: row major)
294  float qmat[9] = {
295  (1.0f - 2.0f * qy * qy - 2.0f * qz * qz), 2.0f * (qx * qy + qw * qz), 2.0f * (qx * qz - qw * qy),
296  2.0f * (qx * qy - qw * qz), (1.0f - 2.0f * qx * qx - 2.0f * qz * qz), 2.0f * (qy * qz + qw * qx),
297  2.0f * (qx * qz + qw * qy), 2.0f * (qy * qz - qw * qx), (1.0f - 2.0f * qx * qx - 2.0f * qy * qy)
298  };
299 
300  // compute rotated vector
301  float to_eye2[3] = {
302  to_eye[0] * qmat[0] + to_eye[1] * qmat[1] + to_eye[2] * qmat[2],
303  to_eye[0] * qmat[3] + to_eye[1] * qmat[4] + to_eye[2] * qmat[5],
304  to_eye[0] * qmat[6] + to_eye[1] * qmat[7] + to_eye[2] * qmat[8]
305  };
306 
307  // compute rotated up vector
308  float up2[3] = {
309  up[0] * qmat[0] + up[1] * qmat[1] + up[2] * qmat[2],
310  up[0] * qmat[3] + up[1] * qmat[4] + up[2] * qmat[5],
311  up[0] * qmat[6] + up[1] * qmat[7] + up[2] * qmat[8]
312  };
313 
314  float up2_len = sqrtf(up2[0] * up2[0] + up2[1] * up2[1] + up2[2] * up2[2]);
315  up2[0] /= up2_len;
316  up2[1] /= up2_len;
317  up2[2] /= up2_len;
318 
319  // update eye position
320  eye[0] = target[0] + to_eye2[0];
321  eye[1] = target[1] + to_eye2[1];
322  eye[2] = target[2] + to_eye2[2];
323 
324  // update up vector
325  up[0] = up2[0];
326  up[1] = up2[1];
327  up[2] = up2[2];
328  }
329 end_rotate:
330 
331  // if midclick is held, then mouse movements implement translation
332  if (midclick_held)
333  {
334  int dx = -(px_x0 - px_x1);
335  int dy = (px_y0 - px_y1);
336 
337  float to_eye_unorm[3] = {
338  eye[0] - target[0],
339  eye[1] - target[1],
340  eye[2] - target[2]
341  };
342  float to_eye_len = sqrtf(to_eye_unorm[0] * to_eye_unorm[0] + to_eye_unorm[1] * to_eye_unorm[1] + to_eye_unorm[2] * to_eye_unorm[2]);
343  float to_eye[3] = {
344  to_eye_unorm[0] / to_eye_len,
345  to_eye_unorm[1] / to_eye_len,
346  to_eye_unorm[2] / to_eye_len
347  };
348 
349  float across[3] = {
350  -(to_eye[1] * up[2] - to_eye[2] * up[1]),
351  -(to_eye[2] * up[0] - to_eye[0] * up[2]),
352  -(to_eye[0] * up[1] - to_eye[1] * up[0])
353  };
354 
355  float pan_delta[3] = {
356  delta_time_seconds * pan_speed * (dx * across[0] + dy * up[0]),
357  delta_time_seconds * pan_speed * (dx * across[1] + dy * up[1]),
358  delta_time_seconds * pan_speed * (dx * across[2] + dy * up[2]),
359  };
360 
361  eye[0] += pan_delta[0];
362  eye[1] += pan_delta[1];
363  eye[2] += pan_delta[2];
364 
365  target[0] += pan_delta[0];
366  target[1] += pan_delta[1];
367  target[2] += pan_delta[2];
368  }
369 
370  // compute how much scrolling happened
371  float zoom_dist = zoom_per_tick * delta_scroll_ticks;
372 
373  // the direction that the eye will move when zoomed
374  float to_target[3] = {
375  target[0] - eye[0],
376  target[1] - eye[1],
377  target[2] - eye[2],
378  };
379 
380  float to_target_len = sqrtf(to_target[0] * to_target[0] + to_target[1] * to_target[1] + to_target[2] * to_target[2]);
381 
382  // if the zoom would get you too close, clamp it.
383  if (!rclick_held)
384  {
385  if (zoom_dist >= to_target_len - 0.00001f)
386  {
387  zoom_dist = to_target_len - 0.00001f;
388  }
389  }
390 
391  // normalize the zoom direction
392  float look[3] = {
393  to_target[0] / to_target_len,
394  to_target[1] / to_target_len,
395  to_target[2] / to_target_len,
396  };
397 
398  float eye_zoom[3] = {
399  look[0] * zoom_dist,
400  look[1] * zoom_dist,
401  look[2] * zoom_dist
402  };
403 
404  eye[0] += eye_zoom[0];
405  eye[1] += eye_zoom[1];
406  eye[2] += eye_zoom[2];
407 
408  if (rclick_held)
409  {
410  // affect target too if right click is held
411  // this allows you to move forward and backward (as opposed to zoom)
412  target[0] += eye_zoom[0];
413  target[1] += eye_zoom[1];
414  target[2] += eye_zoom[2];
415  }
416 
417  arcball_camera_look_to(eye, look, up, view, flags);
418 }
419 
421  const float eye[3],
422  const float look[3],
423  const float up[3],
424  float view[16],
425  unsigned int flags)
426 {
427  if (!view)
428  return;
429 
430  float look_len = sqrtf(look[0] * look[0] + look[1] * look[1] + look[2] * look[2]);
431  float up_len = sqrtf(up[0] * up[0] + up[1] * up[1] + up[2] * up[2]);
432 
433  assert(fabsf(look_len - 1.0f) < 0.000001f);
434  assert(fabsf(up_len - 1.0f) < 0.000001f);
435 
436  // up'' = normalize(up)
437  float up_norm[3] = { up[0] / up_len, up[1] / up_len, up[2] / up_len };
438 
439  // f = normalize(look)
440  float f[3] = { look[0] / look_len, look[1] / look_len, look[2] / look_len };
441 
442  // s = normalize(cross(f, up2))
443  float s[3] = {
444  f[1] * up_norm[2] - f[2] * up_norm[1],
445  f[2] * up_norm[0] - f[0] * up_norm[2],
446  f[0] * up_norm[1] - f[1] * up_norm[0]
447  };
448  float s_len = sqrtf(s[0] * s[0] + s[1] * s[1] + s[2] * s[2]);
449  s[0] /= s_len;
450  s[1] /= s_len;
451  s[2] /= s_len;
452 
453  // u = normalize(cross(normalize(s), f))
454  float u[3] = {
455  s[1] * f[2] - s[2] * f[1],
456  s[2] * f[0] - s[0] * f[2],
457  s[0] * f[1] - s[1] * f[0]
458  };
459  float u_len = sqrtf(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
460  u[0] /= u_len;
461  u[1] /= u_len;
462  u[2] /= u_len;
463 
464  if (!(flags & ARCBALL_CAMERA_LEFT_HANDED_BIT))
465  {
466  // in a right-handed coordinate system, the camera's z looks away from the look direction.
467  // this gets flipped again later when you multiply by a right-handed projection matrix
468  // (notice the last row of gluPerspective, which makes it back into a left-handed system after perspective division)
469  f[0] = -f[0];
470  f[1] = -f[1];
471  f[2] = -f[2];
472  }
473 
474  // t = [s;u;f] * -eye
475  float t[3] = {
476  s[0] * -eye[0] + s[1] * -eye[1] + s[2] * -eye[2],
477  u[0] * -eye[0] + u[1] * -eye[1] + u[2] * -eye[2],
478  f[0] * -eye[0] + f[1] * -eye[1] + f[2] * -eye[2]
479  };
480 
481  // m = [s,t[0]; u,t[1]; -f,t[2]];
482  view[0] = s[0];
483  view[1] = u[0];
484  view[2] = f[0];
485  view[3] = 0.0f;
486  view[4] = s[1];
487  view[5] = u[1];
488  view[6] = f[1];
489  view[7] = 0.0f;
490  view[8] = s[2];
491  view[9] = u[2];
492  view[10] = f[2];
493  view[11] = 0.0f;
494  view[12] = t[0];
495  view[13] = t[1];
496  view[14] = t[2];
497  view[15] = 1.0f;
498 }
499 
500 #ifdef __cplusplus
501 }
502 #endif // __cplusplus
503 
504 #endif // ARCBALL_CAMERA_IMPLEMENTATION
void arcball_camera_look_to(const float eye[3], const float look[3], const float up[3], float view[16], unsigned int flags)
int y0
Definition: rmse.py:49
GLdouble s
e
Definition: rmse.py:177
GLdouble t
#define assert(condition)
Definition: lz4.c:245
GLbitfield flags
#define ARCBALL_CAMERA_LEFT_HANDED_BIT
Definition: arcball_camera.h:9
void arcball_camera_update(float eye[3], float target[3], float up[3], float view[16], float delta_time_seconds, float zoom_per_tick, float pan_speed, float rotation_multiplier, int screen_width, int screen_height, int x0, int x1, int y0, int y1, int midclick_held, int rclick_held, int delta_scroll_ticks, unsigned int flags)
int x1
Definition: rmse.py:50
GLdouble f
int x0
Definition: rmse.py:48
GLdouble y1


librealsense2
Author(s): LibRealSense ROS Team
autogenerated on Thu Dec 22 2022 03:41:42