Coverage Report

Created: 2026-01-17 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/navigate.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2023
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Compositor sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
#include "nodes_stacks.h"
27
#include "visual_manager.h"
28
29
#if !defined(GPAC_DISABLE_COMPOSITOR)
30
31
#ifndef GPAC_DISABLE_3D
32
33
static void camera_changed(GF_Compositor *compositor, GF_Camera *cam)
34
{
35
  cam->flags |= CAM_IS_DIRTY;
36
  gf_sc_invalidate(compositor, NULL);
37
  if (compositor->active_layer) gf_node_dirty_set(compositor->active_layer, 0, 1);
38
}
39
40
#endif
41
42
static void nav_set_zoom_trans_2d(GF_VisualManager *visual, Fixed zoom, Fixed dx, Fixed dy)
43
0
{
44
0
  compositor_2d_set_user_transform(visual->compositor, zoom, visual->compositor->trans_x + dx, visual->compositor->trans_y + dy, 0);
45
#ifndef GPAC_DISABLE_3D
46
  if (visual->type_3d) camera_changed(visual->compositor, &visual->camera);
47
#endif
48
0
}
49
50
51
#ifndef GPAC_DISABLE_3D
52
53
/*shortcut*/
54
static void gf_mx_rotation_matrix(GF_Matrix *mx, SFVec3f axis_pt, SFVec3f axis, Fixed angle)
55
{
56
  gf_mx_init(*mx);
57
  gf_mx_add_translation(mx, axis_pt.x, axis_pt.y, axis_pt.z);
58
  gf_mx_add_rotation(mx, angle, axis.x, axis.y, axis.z);
59
  gf_mx_add_translation(mx, -axis_pt.x, -axis_pt.y, -axis_pt.z);
60
}
61
62
static void view_orbit_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx)
63
{
64
  GF_Matrix mx;
65
  if (!dx) return;
66
  gf_mx_rotation_matrix(&mx, cam->target, cam->up, dx);
67
  gf_mx_apply_vec(&mx, &cam->position);
68
  camera_changed(compositor, cam);
69
}
70
static void view_orbit_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy)
71
{
72
  GF_Matrix mx;
73
  SFVec3f axis;
74
  if (!dy) return;
75
  axis = camera_get_right_dir(cam);
76
  gf_mx_rotation_matrix(&mx, cam->target, axis, dy);
77
  gf_mx_apply_vec(&mx, &cam->position);
78
  /*update up vector*/
79
  cam->up = gf_vec_cross(camera_get_pos_dir(cam), axis);
80
  gf_vec_norm(&cam->up);
81
  camera_changed(compositor, cam);
82
}
83
84
static void view_exam_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx)
85
{
86
  GF_Matrix mx;
87
  if (!dx) return;
88
  gf_mx_rotation_matrix(&mx, cam->examine_center, cam->up, dx);
89
  gf_mx_apply_vec(&mx, &cam->position);
90
  gf_mx_apply_vec(&mx, &cam->target);
91
  camera_changed(compositor, cam);
92
}
93
94
static void view_exam_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy)
95
{
96
  GF_Matrix mx;
97
  SFVec3f axis;
98
  if (!dy) return;
99
  axis = camera_get_right_dir(cam);
100
  gf_mx_rotation_matrix(&mx, cam->examine_center, axis, dy);
101
  gf_mx_apply_vec(&mx, &cam->position);
102
  gf_mx_apply_vec(&mx, &cam->target);
103
  /*update up vector*/
104
  cam->up = gf_vec_cross(camera_get_pos_dir(cam), axis);
105
  gf_vec_norm(&cam->up);
106
  camera_changed(compositor, cam);
107
}
108
109
static void view_roll(GF_Compositor *compositor, GF_Camera *cam, Fixed dd)
110
{
111
  GF_Matrix mx;
112
  SFVec3f delta;
113
  if (!dd) return;
114
  gf_vec_add(delta, cam->target, cam->up);
115
  gf_mx_rotation_matrix(&mx, cam->target, camera_get_pos_dir(cam), dd);
116
  gf_mx_apply_vec(&mx, &delta);
117
  gf_vec_diff(cam->up, delta, cam->target);
118
  gf_vec_norm(&cam->up);
119
  camera_changed(compositor, cam);
120
}
121
122
static void update_pan_up(GF_Compositor *compositor, GF_Camera *cam)
123
{
124
  SFVec3f axis, dir;
125
  /*update up vector so that right is always horizontal (no y component)*/
126
  dir = camera_get_pos_dir(cam);
127
  axis = camera_get_right_dir(cam);
128
  axis.y = 0;
129
  gf_vec_norm(&axis);
130
  cam->up = gf_vec_cross(dir, axis);
131
  gf_vec_norm(&cam->up);
132
133
  camera_changed(compositor, cam);
134
}
135
136
static void view_pan_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx)
137
{
138
  GF_Matrix mx;
139
  if (!dx) return;
140
  gf_mx_rotation_matrix(&mx, cam->position, cam->up, dx);
141
  gf_mx_apply_vec(&mx, &cam->target);
142
143
  update_pan_up(compositor, cam);
144
}
145
static void view_pan_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy)
146
{
147
  GF_Matrix mx;
148
  GF_Vec prev_target = cam->target;
149
  if (!dy) return;
150
  gf_mx_rotation_matrix(&mx, cam->position, camera_get_right_dir(cam), dy);
151
  gf_mx_apply_vec(&mx, &cam->target);
152
  switch (cam->navigate_mode) {
153
  case GF_NAVIGATE_WALK:
154
  case GF_NAVIGATE_VR:
155
  case GF_NAVIGATE_GAME:
156
    if (cam->target.z*prev_target.z<0) {
157
      cam->target = prev_target;
158
      return;
159
    }
160
  default:
161
    break;
162
  }
163
164
  update_pan_up(compositor, cam);
165
}
166
167
/*for translation moves when jumping*/
168
#define JUMP_SCALE_FACTOR 4
169
170
static void view_translate_x(GF_Compositor *compositor, GF_Camera *cam, Fixed dx)
171
{
172
  SFVec3f v;
173
  if (!dx) return;
174
  if (cam->jumping) dx *= JUMP_SCALE_FACTOR;
175
  v = gf_vec_scale(camera_get_right_dir(cam), dx);
176
  gf_vec_add(cam->target, cam->target, v);
177
  gf_vec_add(cam->position, cam->position, v);
178
  camera_changed(compositor, cam);
179
}
180
static void view_translate_y(GF_Compositor *compositor, GF_Camera *cam, Fixed dy)
181
{
182
  SFVec3f v;
183
  if (!dy) return;
184
  if (cam->jumping) dy *= JUMP_SCALE_FACTOR;
185
  v = gf_vec_scale(cam->up, dy);
186
  gf_vec_add(cam->target, cam->target, v);
187
  gf_vec_add(cam->position, cam->position, v);
188
  camera_changed(compositor, cam);
189
}
190
191
static void view_translate_z(GF_Compositor *compositor, GF_Camera *cam, Fixed dz)
192
{
193
  SFVec3f v;
194
  if (!dz) return;
195
  if (cam->jumping) dz *= JUMP_SCALE_FACTOR;
196
  dz = gf_mulfix(dz, cam->speed);
197
  v = gf_vec_scale(camera_get_target_dir(cam), dz);
198
  gf_vec_add(cam->target, cam->target, v);
199
  gf_vec_add(cam->position, cam->position, v);
200
  camera_changed(compositor, cam);
201
}
202
203
static void view_zoom(GF_Compositor *compositor, GF_Camera *cam, Fixed z)
204
{
205
  Fixed oz;
206
  if ((z>FIX_ONE) || (z<-FIX_ONE)) return;
207
  oz = gf_divfix(cam->vp_fov, cam->fieldOfView);
208
  if (oz<FIX_ONE) z/=4;
209
  oz += z;
210
  if (oz<=0) return;
211
212
  cam->fieldOfView = gf_divfix(cam->vp_fov, oz);
213
  if (cam->fieldOfView>GF_PI) cam->fieldOfView=GF_PI;
214
  camera_changed(compositor, cam);
215
}
216
217
Bool gf_sc_fit_world_to_screen(GF_Compositor *compositor)
218
{
219
  GF_TraverseState tr_state;
220
  SFVec3f pos, diff;
221
  Fixed dist, d;
222
  GF_Camera *cam;
223
  GF_Node *top;
224
225
#ifndef GPAC_DISABLE_VRML
226
//  if (gf_list_count(compositor->visual->back_stack)) return;
227
  if (gf_list_count(compositor->visual->view_stack)) return 0;
228
#endif
229
230
  gf_mx_p(compositor->mx);
231
  top = gf_sg_get_root_node(compositor->scene);
232
  if (!top) {
233
    gf_mx_v(compositor->mx);
234
    return 0;
235
  }
236
  memset(&tr_state, 0, sizeof(GF_TraverseState));
237
  gf_mx_init(tr_state.model_matrix);
238
  tr_state.traversing_mode = TRAVERSE_GET_BOUNDS;
239
  tr_state.visual = compositor->visual;
240
  gf_node_traverse(top, &tr_state);
241
  if (gf_node_dirty_get(top)) {
242
    tr_state.bbox.is_set = 0;
243
  }
244
245
  if (!tr_state.bbox.is_set) {
246
    gf_mx_v(compositor->mx);
247
    /*empty world ...*/
248
    if (tr_state.bbox.radius==-1) return 1;
249
    /*2D world with 3D camera forced*/
250
    if (tr_state.bounds.width&&tr_state.bounds.height) return 1;
251
    return 0;
252
  }
253
254
  cam = &compositor->visual->camera;
255
256
  cam->world_bbox = tr_state.bbox;
257
  /*fit is based on bounding sphere*/
258
  dist = gf_divfix(tr_state.bbox.radius, gf_sin(cam->fieldOfView/2) );
259
  gf_vec_diff(diff, cam->center, tr_state.bbox.center);
260
  /*do not update if camera is outside the scene bounding sphere and dist is too close*/
261
  if (gf_vec_len(diff) > tr_state.bbox.radius + cam->radius) {
262
    gf_vec_diff(diff, cam->vp_position, tr_state.bbox.center);
263
    d = gf_vec_len(diff);
264
    if (d<dist) {
265
      gf_mx_v(compositor->mx);
266
      return 1;
267
    }
268
  }
269
270
  diff = gf_vec_scale(camera_get_pos_dir(cam), dist);
271
  gf_vec_add(pos, tr_state.bbox.center, diff);
272
  diff = cam->position;
273
  camera_set_vectors(cam, pos, cam->vp_orientation, cam->fieldOfView);
274
  cam->position = diff;
275
  camera_move_to(cam, pos, cam->target, cam->up);
276
  if (!compositor->player) {
277
    camera_stop_anim(cam);
278
    camera_set_vectors(cam, cam->end_pos, cam->end_ori, cam->end_fov);
279
  }
280
281
  cam->examine_center = tr_state.bbox.center;
282
  cam->flags |= CF_STORE_VP;
283
  if (cam->z_far < dist) cam->z_far = 10*dist;
284
  camera_changed(compositor, cam);
285
  gf_mx_v(compositor->mx);
286
  return 1;
287
}
288
289
static void handle_mouse_move_3d(GF_Compositor *compositor, GF_Camera *cam, u32 keys, Fixed dx, Fixed dy)
290
{
291
  Fixed trans_scale = cam->width/20;
292
  //if default VP is quite far from center use larger dz/dy moves
293
  if (cam->vp_dist>100) trans_scale *= 10;
294
295
  switch (cam->navigate_mode) {
296
  /*FIXME- we'll likely need a "step" value for walk at some point*/
297
  case GF_NAVIGATE_WALK:
298
  case GF_NAVIGATE_FLY:
299
    view_pan_x(compositor, cam, -dx);
300
    if (keys & GF_KEY_MOD_CTRL) view_pan_y(compositor, cam, dy);
301
    else view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale));
302
    break;
303
  case GF_NAVIGATE_VR:
304
    view_pan_x(compositor, cam, -dx);
305
    if (keys & GF_KEY_MOD_CTRL) view_zoom(compositor, cam, dy);
306
    else view_pan_y(compositor, cam, dy);
307
    break;
308
  case GF_NAVIGATE_PAN:
309
    view_pan_x(compositor, cam, -dx);
310
    if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale));
311
    else view_pan_y(compositor, cam, dy);
312
    break;
313
  case GF_NAVIGATE_SLIDE:
314
    view_translate_x(compositor, cam, gf_mulfix(dx, trans_scale));
315
    if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale));
316
    else view_translate_y(compositor, cam, gf_mulfix(dy, trans_scale));
317
    break;
318
  case GF_NAVIGATE_EXAMINE:
319
    if (keys & GF_KEY_MOD_CTRL) {
320
      view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale));
321
      view_roll(compositor, cam, gf_mulfix(dx, trans_scale));
322
    } else {
323
      if (ABS(dx) > ABS(dy)) {
324
        view_exam_x(compositor, cam, -gf_mulfix(GF_PI, dx));
325
      } else {
326
        view_exam_y(compositor, cam, gf_mulfix(GF_PI, dy));
327
      }
328
    }
329
    break;
330
  case GF_NAVIGATE_ORBIT:
331
    if (keys & GF_KEY_MOD_CTRL) {
332
      view_translate_z(compositor, cam, gf_mulfix(dy, trans_scale));
333
    } else {
334
      view_orbit_x(compositor, cam, -gf_mulfix(GF_PI, dx));
335
      view_orbit_y(compositor, cam, gf_mulfix(GF_PI, dy));
336
    }
337
    break;
338
  case GF_NAVIGATE_GAME:
339
    view_pan_x(compositor, cam, -dx);
340
    view_pan_y(compositor, cam, dy);
341
    break;
342
  }
343
}
344
345
static Bool compositor_handle_navigation_3d(GF_Compositor *compositor, GF_Event *ev)
346
{
347
  Fixed x, y, trans_scale;
348
  Fixed dx, dy, key_trans, key_pan, key_exam;
349
  s32 key_inv;
350
  u32 keys;
351
  GF_Camera *cam;
352
  Fixed zoom = compositor->zoom;
353
354
  cam = NULL;
355
#ifndef GPAC_DISABLE_VRML
356
  if (compositor->active_layer) {
357
    cam = compositor_layer3d_get_camera(compositor->active_layer);
358
  }
359
#endif
360
361
  if (!cam) {
362
    cam = &compositor->visual->camera;
363
  }
364
  if (!cam || (cam->navigate_mode==GF_NAVIGATE_NONE)) return 0;
365
366
  keys = compositor->key_states;
367
  if (!cam->navigate_mode && !(keys & GF_KEY_MOD_ALT) ) return 0;
368
  x = y = 0;
369
  /*renorm between -1, 1*/
370
  if (ev->type <= GF_EVENT_LAST_MOUSE) {
371
    x = gf_divfix( INT2FIX(ev->mouse.x - (s32) compositor->visual->width/2), INT2FIX(compositor->visual->width));
372
    y = gf_divfix( INT2FIX(ev->mouse.y - (s32) compositor->visual->height/2), INT2FIX(compositor->visual->height));
373
  }
374
375
  dx = (x - compositor->grab_x);
376
  dy = (compositor->grab_y - y);
377
378
  trans_scale = cam->width/20;
379
  key_trans = cam->avatar_size.x/2;
380
  //if default VP is quite far from center use larger dz/dy moves
381
  if (cam->vp_dist>100) trans_scale *= 10;
382
383
  if (cam->world_bbox.is_set && (key_trans*5 > cam->world_bbox.radius)) {
384
    key_trans = cam->world_bbox.radius / 100;
385
  }
386
387
  key_pan = FIX_ONE/20;
388
  key_exam = FIX_ONE/20;
389
  key_inv = 1;
390
391
  if (keys & GF_KEY_MOD_SHIFT) {
392
    dx *= 4;
393
    dy *= 4;
394
    key_pan *= 4;
395
    key_exam *= 4;
396
    key_trans*=4;
397
  }
398
399
  if (! compositor->orientation_sensors_active) {
400
    Fixed yaw, pitch, roll;
401
    gf_mx_get_yaw_pitch_roll(&compositor->visual->camera.modelview, &yaw, &pitch, &roll);
402
    compositor->audio_renderer->yaw = yaw;
403
    compositor->audio_renderer->pitch = pitch;
404
    compositor->audio_renderer->roll = roll;
405
  }
406
407
  switch (ev->type) {
408
  case GF_EVENT_MOUSEDOWN:
409
    /*left*/
410
    if (ev->mouse.button==GF_MOUSE_LEFT) {
411
      compositor->grab_x = x;
412
      compositor->grab_y = y;
413
      compositor->navigation_state = 1;
414
      compositor->auto_rotate=0;
415
416
      /*change vp and examine center to current location*/
417
      if ((keys & GF_KEY_MOD_CTRL) && compositor->hit_square_dist) {
418
        cam->vp_position = cam->position;
419
        cam->vp_orientation = camera_get_orientation(cam->position, cam->target, cam->up);
420
        cam->vp_fov = cam->fieldOfView;
421
        cam->examine_center = compositor->hit_world_point;
422
        camera_changed(compositor, cam);
423
        return 1;
424
      }
425
    }
426
    /*right*/
427
    else if (ev->mouse.button==GF_MOUSE_RIGHT) {
428
      if (compositor->navigation_state && (cam->navigate_mode==GF_NAVIGATE_WALK)) {
429
        camera_jump(cam);
430
        gf_sc_invalidate(compositor, NULL);
431
        return 1;
432
      }
433
      else if (keys & GF_KEY_MOD_CTRL) gf_sc_fit_world_to_screen(compositor);
434
    }
435
    break;
436
437
  case GF_EVENT_MOUSEMOVE:
438
    if (compositor->orientation_sensors_active) return 0;
439
440
    if (!compositor->navigation_state) {
441
      if (cam->navigate_mode==GF_NAVIGATE_GAME) {
442
        /*init mode*/
443
        compositor->grab_x = x;
444
        compositor->grab_y = y;
445
        compositor->navigation_state = 1;
446
      }
447
      compositor->auto_rotate=0;
448
      return 0;
449
    }
450
    compositor->navigation_state++;
451
452
    if (x <= -0.49) compositor->auto_rotate = 1;
453
    else if (x >= 0.49) compositor->auto_rotate = 2;
454
    else if (y <= -0.49) compositor->auto_rotate = 3;
455
    else if (y >= 0.49) compositor->auto_rotate = 4;
456
    else
457
      compositor->auto_rotate = 0;
458
459
    handle_mouse_move_3d(compositor, cam, keys, dx, dy);
460
461
    compositor->grab_x = x;
462
    compositor->grab_y = y;
463
    return 1;
464
465
  case GF_EVENT_MOUSEWHEEL:
466
    switch (cam->navigate_mode) {
467
    /*FIXME- we'll likely need a "step" value for walk at some point*/
468
    case GF_NAVIGATE_WALK:
469
    case GF_NAVIGATE_FLY:
470
      view_pan_y(compositor, cam, gf_mulfix(key_pan, ev->mouse.wheel_pos));
471
      break;
472
    case GF_NAVIGATE_VR:
473
      view_zoom(compositor, cam, gf_mulfix(key_pan, ev->mouse.wheel_pos));
474
      break;
475
    case GF_NAVIGATE_SLIDE:
476
    case GF_NAVIGATE_EXAMINE:
477
    case GF_NAVIGATE_ORBIT:
478
    case GF_NAVIGATE_PAN:
479
      if (cam->is_3D) {
480
        view_translate_z(compositor, cam, gf_mulfix(trans_scale, ev->mouse.wheel_pos) * ((keys & GF_KEY_MOD_SHIFT) ? 4 : 1));
481
      } else {
482
        nav_set_zoom_trans_2d(compositor->visual, zoom + INT2FIX(ev->mouse.wheel_pos)/10, 0, 0);
483
      }
484
    }
485
    return 1;
486
487
  case GF_EVENT_MULTITOUCH:
488
    compositor->auto_rotate=0;
489
    compositor->navigation_state = 0;
490
    if (ev->mtouch.num_fingers==2) {
491
      if( ABS(ev->mtouch.pinch) * 100 > 2 ) {
492
        if (cam->is_3D) {
493
          view_translate_z(compositor, cam, gf_mulfix(cam->width, compositor->visual->width* ev->mtouch.pinch));
494
        } else {
495
          nav_set_zoom_trans_2d(compositor->visual, zoom + gf_mulfix(trans_scale, ev->mtouch.pinch), 0, 0);
496
        }
497
        return 1;
498
      }
499
      if( ABS(ev->mtouch.rotation) > GF_PI/40 ) {
500
        view_roll(compositor, cam, gf_mulfix(ev->mtouch.rotation, trans_scale));
501
        return 1;
502
      }
503
    } else if (ev->mtouch.num_fingers==3) {
504
      compositor->visual->camera.start_zoom = compositor->zoom;
505
      compositor->zoom = FIX_ONE;
506
      compositor->interoccular_offset = 0;
507
      compositor->focdist = 0;
508
      compositor->interoccular_offset = 0;
509
      compositor->focdist = 0;
510
      compositor_3d_reset_camera(compositor);
511
      return 1;
512
    }
513
    return 0;
514
515
  case GF_EVENT_MOUSEUP:
516
    if (ev->mouse.button==GF_MOUSE_LEFT) {
517
      compositor->navigation_state = 0;
518
    }
519
    break;
520
521
  case GF_EVENT_KEYDOWN:
522
    switch (ev->key.key_code) {
523
    case GF_KEY_BACKSPACE:
524
      gf_sc_reset_graphics(compositor);
525
      return 1;
526
    case GF_KEY_C:
527
      compositor->collide_mode = compositor->collide_mode  ? GF_COLLISION_NONE : GF_COLLISION_DISPLACEMENT;
528
      return 1;
529
    case GF_KEY_J:
530
      if (cam->navigate_mode==GF_NAVIGATE_WALK) {
531
        camera_jump(cam);
532
        gf_sc_invalidate(compositor, NULL);
533
        return 1;
534
      }
535
      break;
536
    case GF_KEY_HOME:
537
      if (!compositor->navigation_state) {
538
        compositor->visual->camera.start_zoom = compositor->zoom;
539
        compositor->zoom = FIX_ONE;
540
        compositor->interoccular_offset = 0;
541
        compositor->focdist = 0;
542
        compositor->interoccular_offset = 0;
543
        compositor->focdist = 0;
544
        compositor_3d_reset_camera(compositor);
545
      }
546
      break;
547
    case GF_KEY_END:
548
      if (cam->navigate_mode==GF_NAVIGATE_GAME) {
549
        cam->navigate_mode = GF_NAVIGATE_WALK;
550
        compositor->navigation_state = 0;
551
        return 1;
552
      }
553
      break;
554
    case GF_KEY_LEFT:
555
      key_inv = -1;
556
    case GF_KEY_RIGHT:
557
      if (keys & GF_KEY_MOD_ALT) {
558
        if ( (keys & GF_KEY_MOD_SHIFT) && (compositor->visual->nb_views > 1) ) {
559
          /*+ or - 10 cm*/
560
          compositor->focdist += INT2FIX(key_inv);
561
          cam->flags |= CAM_IS_DIRTY;
562
          GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("AutoStereo view distance %f - focus %f\n", FIX2FLT(compositor->video_out->dispdist)/100, FIX2FLT(compositor->focdist)/100));
563
          gf_sc_invalidate(compositor, NULL);
564
          return 1;
565
        }
566
        return 0;
567
      }
568
569
570
      switch (cam->navigate_mode) {
571
      case GF_NAVIGATE_SLIDE:
572
        if (keys & GF_KEY_MOD_CTRL) view_pan_x(compositor, cam, key_inv * key_pan);
573
        else view_translate_x(compositor, cam, key_inv * key_trans);
574
        break;
575
      case GF_NAVIGATE_EXAMINE:
576
        if (keys & GF_KEY_MOD_CTRL) view_roll(compositor, cam, gf_mulfix(dx, trans_scale));
577
        else view_exam_x(compositor, cam, -key_inv * key_exam);
578
        break;
579
      case GF_NAVIGATE_ORBIT:
580
        if (keys & GF_KEY_MOD_CTRL) view_translate_x(compositor, cam, key_inv * key_trans);
581
        else view_orbit_x(compositor, cam, -key_inv * key_exam);
582
        break;
583
      case GF_NAVIGATE_GAME:
584
        view_translate_x(compositor, cam, key_inv * key_trans);
585
        break;
586
      case GF_NAVIGATE_VR:
587
        view_pan_x(compositor, cam, -key_inv * key_pan);
588
        break;
589
      /*walk/fly/pan*/
590
      default:
591
        if (keys & GF_KEY_MOD_CTRL) view_translate_x(compositor, cam, key_inv * key_trans);
592
        else view_pan_x(compositor, cam, -key_inv * key_pan);
593
        break;
594
      }
595
      return 1;
596
    case GF_KEY_DOWN:
597
      key_inv = -1;
598
    case GF_KEY_UP:
599
      if (keys & GF_KEY_MOD_ALT) {
600
        if ( (keys & GF_KEY_MOD_SHIFT) && (compositor->visual->nb_views > 1) ) {
601
          compositor->interoccular_offset += FLT2FIX(0.5) * key_inv;
602
          GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("AutoStereo interoccular distance %f\n", FIX2FLT(compositor->iod + compositor->interoccular_offset)));
603
          cam->flags |= CAM_IS_DIRTY;
604
          gf_sc_invalidate(compositor, NULL);
605
          return 1;
606
        }
607
        return 0;
608
      }
609
      switch (cam->navigate_mode) {
610
      case GF_NAVIGATE_SLIDE:
611
        if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, key_inv * key_trans);
612
        else view_translate_y(compositor, cam, key_inv * key_trans);
613
        break;
614
      case GF_NAVIGATE_EXAMINE:
615
        if (keys & GF_KEY_MOD_CTRL) view_translate_z(compositor, cam, key_inv * key_trans);
616
        else view_exam_y(compositor, cam, -key_inv * key_exam);
617
        break;
618
      case GF_NAVIGATE_ORBIT:
619
        if (keys & GF_KEY_MOD_CTRL) view_translate_y(compositor, cam, key_inv * key_trans);
620
        else view_orbit_y(compositor, cam, -key_inv * key_exam);
621
        break;
622
      case GF_NAVIGATE_PAN:
623
        if (keys & GF_KEY_MOD_CTRL) view_translate_y(compositor, cam, key_inv * key_trans);
624
        else view_pan_y(compositor, cam, key_inv * key_pan);
625
        break;
626
      case GF_NAVIGATE_GAME:
627
        view_translate_z(compositor, cam, key_inv * key_trans);
628
        break;
629
      case GF_NAVIGATE_VR:
630
        if (keys & GF_KEY_MOD_CTRL) view_zoom(compositor, cam, key_inv * key_pan);
631
        else view_pan_y(compositor, cam, key_inv * key_pan);
632
        break;
633
      /*walk/fly*/
634
      default:
635
        if (keys & GF_KEY_MOD_CTRL) view_pan_y(compositor, cam, key_inv * key_pan);
636
        else view_translate_z(compositor, cam, key_inv * key_trans);
637
        break;
638
      }
639
      return 1;
640
641
    case GF_KEY_PAGEDOWN:
642
      if (keys & GF_KEY_MOD_CTRL) {
643
        view_zoom(compositor, cam, FIX_ONE/10);
644
        return 1;
645
      }
646
      break;
647
    case GF_KEY_PAGEUP:
648
      if (keys & GF_KEY_MOD_CTRL) {
649
        view_zoom(compositor, cam, -FIX_ONE/10);
650
        return 1;
651
      }
652
      break;
653
    case GF_KEY_D:
654
      if (keys & GF_KEY_MOD_CTRL) {
655
        if (compositor->tvtd==TILE_DEBUG_FULL) compositor->tvtd = 0;
656
        else compositor->tvtd ++;
657
        gf_sc_invalidate(compositor, NULL);
658
        return 1;
659
      }
660
      break;
661
    case GF_KEY_G:
662
      if (keys & GF_KEY_MOD_CTRL) {
663
        compositor->vrhud_mode++;
664
        if (compositor->vrhud_mode==5) compositor->vrhud_mode=0;
665
        gf_sc_invalidate(compositor, NULL);
666
        return 1;
667
      }
668
      break;
669
    case GF_KEY_A:
670
      if (keys & GF_KEY_MOD_CTRL) {
671
        compositor->tvtf = !compositor->tvtf;
672
        gf_sc_invalidate(compositor, NULL);
673
        return 1;
674
      }
675
      break;
676
    }
677
    break;
678
  case GF_EVENT_SENSOR_ORIENTATION:
679
  {
680
    Fixed z, w, yaw, /*pitch, */roll;
681
    GF_Vec target;
682
    GF_Matrix mx;
683
684
    /* In iOS we get x, y, z in quaternions (first measurement is the frame of reference) */
685
    if (ev->sensor.w) {
686
      x = ev->sensor.x;
687
      y = ev->sensor.y;
688
      z = ev->sensor.z;
689
      w = ev->sensor.w;
690
    
691
      yaw = gf_atan2(2*gf_mulfix(z,w) - 2*gf_mulfix(y,x) , 1 - 2*gf_mulfix(z,z) - 2*gf_mulfix(x,x));
692
      //pitch = asin(2*y*z + 2*x*w);
693
      roll = gf_atan2(2*gf_mulfix(y,w) - 2*gf_mulfix(z,x) , 1 - 2*gf_mulfix(y,y) - 2*gf_mulfix(x,x));
694
    } else {
695
      /*
696
       * In Android we get yaw, pitch, roll values (in rad)
697
       * The frame of reference is absolute
698
       */
699
      yaw = ev->sensor.x;
700
      //pitch = ev->sensor.y;
701
      roll = ev->sensor.z;
702
    }
703
    target.x = 0;
704
    target.y = -FIX_ONE;
705
    target.z = 0;
706
    gf_mx_init(mx);
707
    gf_mx_add_rotation(&mx, yaw, 0, FIX_ONE, 0);
708
    gf_mx_add_rotation(&mx, -roll, FIX_ONE, 0, 0);
709
    
710
    gf_mx_apply_vec(&mx, &target);
711
712
    cam->target = target;
713
    update_pan_up(compositor, cam);
714
  }
715
    return 1;
716
  }
717
  return 0;
718
}
719
720
#endif
721
722
723
static Bool compositor_handle_navigation_2d(GF_VisualManager *visual, GF_Event *ev)
724
0
{
725
0
  Fixed x, y, dx, dy, key_trans, key_rot, zoom, new_zoom;
726
0
  u32 navigation_mode;
727
0
  s32 key_inv;
728
0
  Bool is_pixel_metrics = visual->compositor->traverse_state->pixel_metrics;
729
0
  u32 keys = visual->compositor->key_states;
730
731
0
  zoom = visual->compositor->zoom;
732
0
  navigation_mode = visual->compositor->navigate_mode;
733
#ifndef GPAC_DISABLE_3D
734
  if (visual->type_3d) navigation_mode = visual->camera.navigate_mode;
735
#endif
736
737
0
  if (navigation_mode==GF_NAVIGATE_NONE) return 0;
738
0
  if (!navigation_mode && !(keys & GF_KEY_MOD_ALT) ) return 0;
739
740
741
0
  x = y = 0;
742
  /*renorm between -1, 1*/
743
0
  if (ev->type <= GF_EVENT_LAST_MOUSE) {
744
0
    x = INT2FIX(ev->mouse.x);
745
0
    y = INT2FIX(ev->mouse.y);
746
0
  }
747
0
  dx = x - visual->compositor->grab_x;
748
0
  if (visual->center_coords) {
749
0
    dy = visual->compositor->grab_y - y;
750
0
  } else {
751
0
    dy = y - visual->compositor->grab_y;
752
0
  }
753
0
  if (!is_pixel_metrics) {
754
0
    dx /= visual->width;
755
0
    dy /= visual->height;
756
0
  }
757
758
0
  key_inv = 1;
759
0
  key_trans = INT2FIX(2);
760
0
  key_rot = GF_PI/100;
761
762
0
  if (keys & GF_KEY_MOD_SHIFT) {
763
0
    dx *= 4;
764
0
    dy *= 4;
765
0
    key_rot *= 4;
766
0
    key_trans*=4;
767
0
  }
768
769
0
  if (!is_pixel_metrics) {
770
0
    key_trans /= visual->width;
771
0
  }
772
773
0
  switch (ev->type) {
774
0
  case GF_EVENT_MOUSEDOWN:
775
    /*left*/
776
0
    if (ev->mouse.button==GF_MOUSE_LEFT) {
777
0
      visual->compositor->grab_x = x;
778
0
      visual->compositor->grab_y = y;
779
0
      visual->compositor->navigation_state = 1;
780
      /*update zoom center*/
781
0
      if (keys & GF_KEY_MOD_CTRL) {
782
0
        visual->compositor->trans_x -= visual->compositor->grab_x - INT2FIX(visual->width)/2;
783
0
        visual->compositor->trans_y += INT2FIX(visual->height)/2 - visual->compositor->grab_y;
784
0
        nav_set_zoom_trans_2d(visual, visual->compositor->zoom, 0, 0);
785
0
      }
786
0
      return 0;
787
0
    }
788
0
    break;
789
790
0
  case GF_EVENT_MOUSEUP:
791
0
    if (ev->mouse.button==GF_MOUSE_LEFT) {
792
0
      visual->compositor->navigation_state = 0;
793
0
      return 0;
794
0
    }
795
0
    break;
796
0
  case GF_EVENT_MULTITOUCH:
797
0
    if (ev->mtouch.num_fingers==2) {
798
0
      if( ABS(ev->mtouch.pinch) * 100 > 2 ) {
799
0
        new_zoom = zoom + ev->mtouch.pinch * MIN(visual->width, visual->height)/100;
800
0
        nav_set_zoom_trans_2d(visual, new_zoom, 0, 0);
801
0
        return 1;
802
0
      }
803
0
      if( ABS(ev->mtouch.rotation) > GF_PI/40 ) {
804
0
        visual->compositor->rotation -= ev->mtouch.rotation;
805
0
        nav_set_zoom_trans_2d(visual, zoom, 0, 0);
806
0
        return 1;
807
0
      }
808
0
    } else if (ev->mtouch.num_fingers==3) {
809
0
      visual->compositor->trans_x = visual->compositor->trans_y = 0;
810
0
      visual->compositor->rotation = 0;
811
0
      visual->compositor->zoom = FIX_ONE;
812
0
      nav_set_zoom_trans_2d(visual, FIX_ONE, 0, 0);
813
0
      return 1;
814
0
    }
815
0
    return 0;
816
817
0
  case GF_EVENT_MOUSEWHEEL:
818
0
    switch (navigation_mode) {
819
0
    case GF_NAVIGATE_SLIDE:
820
0
      new_zoom = zoom + INT2FIX(ev->mouse.wheel_pos)/10;
821
0
      nav_set_zoom_trans_2d(visual, new_zoom, 0, 0);
822
0
      return 1;
823
0
    case GF_NAVIGATE_EXAMINE:
824
0
      if (ev->mouse.wheel_pos>0)
825
0
        visual->compositor->rotation += gf_asin( GF_PI / 10);
826
0
      else
827
0
        visual->compositor->rotation -= gf_asin( GF_PI / 10);
828
0
      nav_set_zoom_trans_2d(visual, zoom, 0, 0);
829
0
      return 1;
830
0
    }
831
0
    return 0;
832
833
0
  case GF_EVENT_MOUSEMOVE:
834
0
    if (!visual->compositor->navigation_state) return 0;
835
0
    visual->compositor->navigation_state++;
836
0
    switch (navigation_mode) {
837
0
    case GF_NAVIGATE_SLIDE:
838
0
      if (keys & GF_KEY_MOD_CTRL) {
839
0
        if (dy) {
840
0
          new_zoom = zoom;
841
0
          if (new_zoom > FIX_ONE) new_zoom += dy/20;
842
0
          else new_zoom += dy/80;
843
0
          nav_set_zoom_trans_2d(visual, new_zoom, 0, 0);
844
0
        }
845
0
      } else {
846
0
        nav_set_zoom_trans_2d(visual, zoom, dx, dy);
847
0
      }
848
0
      break;
849
0
    case GF_NAVIGATE_EXAMINE:
850
0
    {
851
0
      Fixed sin = gf_mulfix(GF_PI, dy) / visual->height;
852
      //truncate in [-1;1] for arcsin()
853
0
      if (sin < -FIX_ONE)
854
0
        sin = -FIX_ONE;
855
0
      if (sin >  FIX_ONE)
856
0
        sin =  FIX_ONE;
857
0
      visual->compositor->rotation += gf_asin(sin);
858
0
      nav_set_zoom_trans_2d(visual, zoom, 0, 0);
859
0
    }
860
0
    break;
861
0
    }
862
0
    visual->compositor->grab_x = x;
863
0
    visual->compositor->grab_y = y;
864
0
    return 1;
865
0
  case GF_EVENT_KEYDOWN:
866
0
    switch (ev->key.key_code) {
867
0
    case GF_KEY_BACKSPACE:
868
0
      gf_sc_reset_graphics(visual->compositor);
869
0
      return 1;
870
0
    case GF_KEY_HOME:
871
0
      if (!visual->compositor->navigation_state) {
872
0
        visual->compositor->trans_x = visual->compositor->trans_y = 0;
873
0
        visual->compositor->rotation = 0;
874
0
        visual->compositor->zoom = FIX_ONE;
875
0
        nav_set_zoom_trans_2d(visual, FIX_ONE, 0, 0);
876
0
      }
877
0
      return 1;
878
0
    case GF_KEY_LEFT:
879
0
      key_inv = -1;
880
0
    case GF_KEY_RIGHT:
881
0
      if (navigation_mode == GF_NAVIGATE_SLIDE) {
882
0
        nav_set_zoom_trans_2d(visual, zoom, key_inv*key_trans, 0);
883
0
      }
884
0
      else {
885
0
        visual->compositor->rotation -= key_inv * key_rot;
886
0
        nav_set_zoom_trans_2d(visual, zoom, 0, 0);
887
0
      }
888
0
      return 1;
889
0
    case GF_KEY_DOWN:
890
0
      key_inv = -1;
891
0
    case GF_KEY_UP:
892
0
      if (navigation_mode == GF_NAVIGATE_SLIDE) {
893
0
        if (keys & GF_KEY_MOD_CTRL) {
894
0
          new_zoom = zoom;
895
0
          if (new_zoom > FIX_ONE) new_zoom += key_inv*FIX_ONE/10;
896
0
          else new_zoom += key_inv*FIX_ONE/20;
897
0
          nav_set_zoom_trans_2d(visual, new_zoom, 0, 0);
898
0
        } else {
899
0
          nav_set_zoom_trans_2d(visual, zoom, 0, key_inv*key_trans);
900
0
        }
901
0
      }
902
0
      else {
903
0
        visual->compositor->rotation += key_inv*key_rot;
904
0
        nav_set_zoom_trans_2d(visual, zoom, 0, 0);
905
0
      }
906
0
      return 1;
907
0
    }
908
0
    break;
909
0
  }
910
0
  return 0;
911
0
}
912
913
void compositor_handle_auto_navigation(GF_Compositor *compositor)
914
0
{
915
#ifndef GPAC_DISABLE_3D
916
  GF_Camera *cam = NULL;
917
  Fixed dx, dy;
918
#ifndef GPAC_DISABLE_VRML
919
  if (compositor->active_layer) {
920
    cam = compositor_layer3d_get_camera(compositor->active_layer);
921
  }
922
  if (!cam)
923
    cam = &compositor->visual->camera;
924
#endif
925
926
  dx = dy = 0;
927
  switch (compositor->auto_rotate) {
928
  case 1:
929
    dx = -FIX_ONE/100;
930
    break;
931
  case 2:
932
    dx = FIX_ONE/100;
933
    break;
934
  case 3:
935
    dy = FIX_ONE/100;
936
    if (cam->up.z>0.9999) compositor->auto_rotate=4;
937
    break;
938
  case 4:
939
    dy = -FIX_ONE/100;
940
    if (cam->up.z<-0.9999) compositor->auto_rotate=3;
941
    break;
942
  default:
943
    return;
944
  }
945
  if (compositor->gazer_enabled)
946
    compositor->gaze_changed = GF_FALSE;
947
  handle_mouse_move_3d(compositor, cam, 0, dx, dy);
948
#endif
949
0
}
950
951
Bool compositor_handle_navigation(GF_Compositor *compositor, GF_Event *ev)
952
0
{
953
0
  if (!compositor->scene) return 0;
954
#ifndef GPAC_DISABLE_3D
955
  if ( (compositor->visual->type_3d>0) || compositor->active_layer)
956
    return compositor_handle_navigation_3d(compositor, ev);
957
#endif
958
0
  return compositor_handle_navigation_2d(compositor->visual, ev);
959
0
}
960
961
#endif //!defined(GPAC_DISABLE_COMPOSITOR)