Coverage Report

Created: 2025-10-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/mpeg4_viewport.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
GF_EXPORT
32
GF_Err gf_sc_get_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char **outName, Bool *is_bound)
33
0
{
34
0
#ifndef GPAC_DISABLE_VRML
35
0
  u32 count;
36
0
  GF_Node *n;
37
0
  if (!compositor->visual) return GF_BAD_PARAM;
38
0
  count = gf_list_count(compositor->visual->view_stack);
39
0
  if (!viewpoint_idx) return GF_BAD_PARAM;
40
0
  if (viewpoint_idx>count) return GF_EOS;
41
42
0
  n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
43
0
  switch (gf_node_get_tag(n)) {
44
0
  case TAG_MPEG4_Viewport:
45
0
    if (outName)
46
0
      *outName = ((M_Viewport*)n)->description.buffer;
47
0
    if (is_bound)
48
0
      *is_bound = ((M_Viewport*)n)->isBound;
49
0
    return GF_OK;
50
0
  case TAG_MPEG4_Viewpoint:
51
0
#ifndef GPAC_DISABLE_X3D
52
0
  case TAG_X3D_Viewpoint:
53
0
#endif
54
0
    if (outName)
55
0
      *outName = ((M_Viewpoint*)n)->description.buffer;
56
0
    if (is_bound)
57
0
      *is_bound = ((M_Viewpoint*)n)->isBound;
58
0
    return GF_OK;
59
0
  default:
60
0
    if (outName)
61
0
      *outName = NULL;
62
0
    if (is_bound)
63
0
      *is_bound = GF_FALSE;
64
0
    return GF_OK;
65
0
  }
66
#else
67
  return GF_NOT_SUPPORTED;
68
#endif
69
0
}
70
71
GF_EXPORT
72
GF_Err gf_sc_set_viewpoint(GF_Compositor *compositor, u32 viewpoint_idx, const char *viewpoint_name)
73
0
{
74
0
#ifndef GPAC_DISABLE_VRML
75
0
  u32 count, i;
76
0
  GF_Node *n;
77
0
  if (!compositor->visual) return GF_BAD_PARAM;
78
0
  count = gf_list_count(compositor->visual->view_stack);
79
0
  if (viewpoint_idx>count) return GF_BAD_PARAM;
80
0
  if (!viewpoint_idx && !viewpoint_name) return GF_BAD_PARAM;
81
82
0
  if (viewpoint_idx) {
83
0
    Bool bind;
84
0
    n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
85
0
    bind = Bindable_GetIsBound(n);
86
0
    Bindable_SetSetBind(n, !bind);
87
0
    return GF_OK;
88
0
  }
89
0
  for (i=0; i<count; i++) {
90
0
    char *name = NULL;
91
0
    n = (GF_Node*)gf_list_get(compositor->visual->view_stack, viewpoint_idx-1);
92
0
    switch (gf_node_get_tag(n)) {
93
0
    case TAG_MPEG4_Viewport:
94
0
      name = ((M_Viewport*)n)->description.buffer;
95
0
      break;
96
0
    case TAG_MPEG4_Viewpoint:
97
0
      name = ((M_Viewpoint*)n)->description.buffer;
98
0
      break;
99
0
#ifndef GPAC_DISABLE_X3D
100
0
    case TAG_X3D_Viewpoint:
101
0
      name = ((M_Viewpoint*)n)->description.buffer;
102
0
      break;
103
0
#endif
104
0
    default:
105
0
      break;
106
0
    }
107
0
    if (name && !stricmp(name, viewpoint_name)) {
108
0
      Bool bind = Bindable_GetIsBound(n);
109
0
      Bindable_SetSetBind(n, !bind);
110
0
      return GF_OK;
111
0
    }
112
0
  }
113
0
  return GF_BAD_PARAM;
114
#else
115
  return GF_NOT_SUPPORTED;
116
#endif
117
0
}
118
119
#ifndef GPAC_DISABLE_VRML
120
121
122
0
#define VPCHANGED(__comp) { GF_Event evt; evt.type = GF_EVENT_VIEWPOINTS; gf_sc_send_event(__comp, &evt); }
123
124
125
static void DestroyViewStack(GF_Node *node)
126
0
{
127
0
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
128
0
  PreDestroyBindable(node, st->reg_stacks);
129
0
  gf_list_del(st->reg_stacks);
130
0
  VPCHANGED(gf_sc_get_compositor(node));
131
0
  gf_free(st);
132
0
}
133
134
static void viewport_set_bind(GF_Node *node, GF_Route *route)
135
0
{
136
0
  GF_Compositor *rend = gf_sc_get_compositor(node);
137
0
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
138
0
  Bindable_OnSetBind(node, st->reg_stacks, NULL);
139
140
0
  gf_sc_invalidate(rend, NULL);
141
  /*notify change of vp stack*/
142
0
  VPCHANGED(rend);
143
  /*and dirty ourselves to force frustrum update*/
144
0
  gf_node_dirty_set(node, 0, 0);
145
0
}
146
147
148
static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy)
149
0
{
150
0
  Fixed sx, sy, w, h, tx, ty;
151
#ifndef GPAC_DISABLE_3D
152
  GF_Matrix mx;
153
#endif
154
0
  GF_Matrix2D mat;
155
0
  GF_Rect rc, rc_bckup;
156
0
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
157
0
  M_Viewport *vp = (M_Viewport *) node;
158
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
159
160
0
  if (is_destroy) {
161
0
    DestroyViewStack(node);
162
0
    return;
163
0
  }
164
165
#ifndef GPAC_DISABLE_3D
166
  if (tr_state->visual->type_3d>1) return;
167
#endif
168
169
  /*first traverse, bound if needed*/
170
0
  if (gf_list_find(tr_state->viewpoints, node) < 0) {
171
0
    gf_list_add(tr_state->viewpoints, node);
172
0
    if (gf_list_find(st->reg_stacks, tr_state->viewpoints)<0)
173
0
      gf_list_add(st->reg_stacks, tr_state->viewpoints);
174
175
0
    if (gf_list_get(tr_state->viewpoints, 0) == vp) {
176
0
      if (!vp->isBound) Bindable_SetIsBound(node, 1);
177
0
    } else {
178
0
      if (gf_inline_is_default_viewpoint(node)) Bindable_SetSetBindEx(node, 1, tr_state->viewpoints);
179
0
    }
180
0
    VPCHANGED(tr_state->visual->compositor);
181
    /*in any case don't draw the first time (since the viewport could have been declared last)*/
182
0
    gf_sc_invalidate(tr_state->visual->compositor, NULL);
183
0
    return;
184
0
  }
185
186
0
  if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return;
187
0
  if (!vp->isBound) return;
188
189
0
  if (gf_list_get(tr_state->viewpoints, 0) != vp)
190
0
    return;
191
192
#ifndef GPAC_DISABLE_3D
193
  if (tr_state->visual->type_3d) {
194
    w = tr_state->bbox.max_edge.x - tr_state->bbox.min_edge.x;
195
    h = tr_state->bbox.max_edge.y - tr_state->bbox.min_edge.y;
196
  } else
197
#endif
198
0
  {
199
0
    w = tr_state->bounds.width;
200
0
    h = tr_state->bounds.height;
201
0
  }
202
0
  if (!w || !h) return;
203
204
205
  /*if no parent this is the main viewport, don't update if not changed*/
206
//  if (!tr_state->is_layer && !gf_node_dirty_get(node)) return;
207
208
0
  gf_node_dirty_clear(node, 0);
209
210
0
  gf_mx2d_init(mat);
211
0
  gf_mx2d_add_translation(&mat, vp->position.x, vp->position.y);
212
0
  gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation);
213
214
  //compute scaling ratio
215
0
  sx = (vp->size.x>=0) ? vp->size.x : w;
216
0
  sy = (vp->size.y>=0) ? vp->size.y : h;
217
0
  rc = gf_rect_center(sx, sy);
218
0
  rc_bckup = rc;
219
220
0
  switch (vp->fit) {
221
  /*covers all area and respect aspect ratio*/
222
0
  case 2:
223
0
    if (gf_divfix(rc.width, w) > gf_divfix(rc.height, h)) {
224
0
      rc.width = gf_muldiv(rc.width, h, rc.height);
225
0
      rc.height = h;
226
0
    } else {
227
0
      rc.height = gf_muldiv(rc.height , w, rc.width);
228
0
      rc.width = w;
229
0
    }
230
0
    break;
231
  /*fits inside the area and respect AR*/
232
0
  case 1:
233
0
    if (gf_divfix(rc.width, w)> gf_divfix(rc.height, h)) {
234
0
      rc.height = gf_muldiv(rc.height, w, rc.width);
235
0
      rc.width = w;
236
0
    } else {
237
0
      rc.width = gf_muldiv(rc.width, h, rc.height);
238
0
      rc.height = h;
239
0
    }
240
0
    break;
241
  /*fit entirely: nothing to change*/
242
0
  case 0:
243
0
    rc.width = w;
244
0
    rc.height = h;
245
0
    break;
246
0
  default:
247
0
    return;
248
0
  }
249
0
  if (!rc.width || !rc.height)
250
0
    return;
251
0
  sx = gf_divfix(rc.width, rc_bckup.width);
252
0
  sy = gf_divfix(rc.height, rc_bckup.height);
253
254
  /*viewport on root visual, remove compositor scale*/
255
0
  if (!tr_state->is_layer && (tr_state->visual->compositor->visual==tr_state->visual) ) {
256
0
    sx = gf_divfix(sx, tr_state->visual->compositor->scale_x);
257
0
    sy = gf_divfix(sy, tr_state->visual->compositor->scale_y);
258
0
  }
259
260
0
  rc.x = - rc.width/2;
261
0
  rc.y = rc.height/2;
262
263
0
  tx = ty = 0;
264
0
  if (vp->fit && vp->alignment.count) {
265
    /*left alignment*/
266
0
    if (vp->alignment.vals[0] == -1) tx = rc.width/2 - w/2;
267
0
    else if (vp->alignment.vals[0] == 1) tx = w/2 - rc.width/2;
268
269
0
    if (vp->alignment.count>1) {
270
      /*top-alignment*/
271
0
      if (vp->alignment.vals[1]==-1) ty = rc.height/2 - h/2;
272
0
      else if (vp->alignment.vals[1]==1) ty = h/2 - rc.height/2;
273
0
    }
274
0
  }
275
276
0
  gf_mx2d_init(mat);
277
0
  if (tr_state->pixel_metrics) {
278
0
    gf_mx2d_add_scale(&mat, sx, sy);
279
0
  } else {
280
    /*if we are not in pixelMetrics, undo the meterMetrics->pixelMetrics transformation*/
281
0
    gf_mx2d_add_scale(&mat, gf_divfix(sx, tr_state->min_hsize), gf_divfix(sy, tr_state->min_hsize) );
282
0
  }
283
0
  gf_mx2d_add_translation(&mat, tx, ty);
284
285
0
  gf_mx2d_add_translation(&mat, -gf_mulfix(vp->position.x,sx), -gf_mulfix(vp->position.y,sy) );
286
0
  gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation);
287
288
0
  tr_state->bounds = rc;
289
0
  tr_state->bounds.x += tx;
290
0
  tr_state->bounds.y += ty;
291
292
#ifndef GPAC_DISABLE_3D
293
  if (tr_state->visual->type_3d) {
294
    /*in layers directly modify the model matrix*/
295
    if (tr_state->is_layer) {
296
      gf_mx_from_mx2d(&mx, &mat);
297
      gf_mx_add_matrix(&tr_state->model_matrix, &mx);
298
    }
299
    /*otherwise add to camera viewport matrix*/
300
    else {
301
      gf_mx_from_mx2d(&tr_state->camera->viewport, &mat);
302
      tr_state->camera->flags = (CAM_HAS_VIEWPORT | CAM_IS_DIRTY);
303
    }
304
  } else
305
#endif
306
0
    gf_mx2d_pre_multiply(&tr_state->transform, &mat);
307
0
}
308
309
void compositor_init_viewport(GF_Compositor *compositor, GF_Node *node)
310
0
{
311
0
  ViewStack *ptr;
312
0
  GF_SAFEALLOC(ptr, ViewStack);
313
0
  if (!ptr) {
314
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate viewport stack\n"));
315
0
    return;
316
0
  }
317
318
0
  ptr->reg_stacks = gf_list_new();
319
320
0
  gf_node_set_private(node, ptr);
321
0
  gf_node_set_callback_function(node, TraverseViewport);
322
0
  ((M_Viewport*)node)->on_set_bind = viewport_set_bind;
323
0
}
324
325
326
#ifndef GPAC_DISABLE_3D
327
328
static void viewpoint_set_bind(GF_Node *node, GF_Route *route)
329
{
330
  GF_Compositor *rend = gf_sc_get_compositor(node);
331
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
332
333
  if (!((M_Viewpoint*)node)->isBound )
334
    st->prev_was_bound = 0;
335
  Bindable_OnSetBind(node, st->reg_stacks, NULL);
336
  gf_sc_invalidate(rend, NULL);
337
  /*notify change of vp stack*/
338
  VPCHANGED(rend);
339
  /*and dirty ourselves to force frustrum update*/
340
  gf_node_dirty_set(node, 0, 0);
341
}
342
343
static void TraverseViewpoint(GF_Node *node, void *rs, Bool is_destroy)
344
{
345
  SFVec3f pos, v1, v2;
346
  SFRotation ori;
347
  GF_Matrix mx;
348
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
349
  M_Viewpoint *vp = (M_Viewpoint*) node;
350
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
351
352
  if (is_destroy) {
353
    DestroyViewStack(node);
354
    return;
355
  }
356
  /*may happen in get_bounds*/
357
  if (!tr_state->viewpoints) return;
358
//  if (!tr_state->camera->is_3D) return;
359
360
  /*first traverse, bound if needed*/
361
  if (gf_list_find(tr_state->viewpoints, node) < 0) {
362
    gf_list_add(tr_state->viewpoints, node);
363
    if (gf_list_find(st->reg_stacks, tr_state->viewpoints)<0)
364
      gf_list_add(st->reg_stacks, tr_state->viewpoints);
365
366
    if (gf_list_get(tr_state->viewpoints, 0) == vp) {
367
      if (!vp->isBound) Bindable_SetIsBound(node, 1);
368
    } else {
369
      if (gf_inline_is_default_viewpoint(node)) Bindable_SetSetBind(node, 1);
370
    }
371
    VPCHANGED(tr_state->visual->compositor);
372
    /*in any case don't draw the first time (since the viewport could have been declared last)*/
373
    if (tr_state->layer3d) gf_node_dirty_set(tr_state->layer3d, GF_SG_VRML_BINDABLE_DIRTY, 0);
374
    gf_sc_invalidate(tr_state->visual->compositor, NULL);
375
  }
376
  /*not evaluating vp, return*/
377
  if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
378
    /*store model matrix if changed - NOTE: we always have a 1-frame delay between VP used and real world...
379
    we could remove this by pre-traversing the scene before applying vp, but that would mean 2 scene
380
    traversals*/
381
    if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) {
382
      if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) {
383
        gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
384
        gf_node_dirty_set(node, 0, 0);
385
      }
386
    }
387
    return;
388
  }
389
390
  /*not bound or in 2D visual*/
391
  if (!vp->isBound || !tr_state->navigations) return;
392
393
  if (!gf_node_dirty_get(node)) {
394
    if ((tr_state->vp_size.x == st->last_vp_size.x) &&  (tr_state->vp_size.y == st->last_vp_size.y)) return;
395
  }
396
  gf_node_dirty_clear(node, 0);
397
398
  st->last_vp_size.x = tr_state->vp_size.x;
399
  st->last_vp_size.y = tr_state->vp_size.y;
400
401
  /*move to local system*/
402
  gf_mx_copy(mx, st->world_view_mx);
403
  gf_mx_add_translation(&mx, vp->position.x, vp->position.y, vp->position.z);
404
  gf_mx_add_rotation(&mx, vp->orientation.q, vp->orientation.x, vp->orientation.y, vp->orientation.z);
405
  gf_mx_decompose(&mx, &pos, &v1, &ori, &v2);
406
  /*get center*/
407
  v1.x = v1.y = v1.z = 0;
408
#ifndef GPAC_DISABLE_X3D
409
  /*X3D specifies examine center*/
410
  if (gf_node_get_tag(node)==TAG_X3D_Viewpoint) v1 = ((X_Viewpoint *)node)->centerOfRotation;
411
#endif
412
  gf_mx_apply_vec(&st->world_view_mx, &v1);
413
  /*set frustrum param - animate only if not bound last frame and jump false*/
414
  visual_3d_viewpoint_change(tr_state, node, (!st->prev_was_bound && !vp->jump) ? 1 : 0, vp->fieldOfView, pos, ori, v1);
415
  st->prev_was_bound = 1;
416
}
417
418
void compositor_init_viewpoint(GF_Compositor *compositor, GF_Node *node)
419
{
420
  ViewStack *st;
421
  GF_SAFEALLOC(st, ViewStack);
422
  if (!st) {
423
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate viewpoint stack\n"));
424
    return;
425
  }
426
427
  st->reg_stacks = gf_list_new();
428
  gf_mx_init(st->world_view_mx);
429
  gf_node_set_private(node, st);
430
  gf_node_set_callback_function(node, TraverseViewpoint);
431
  ((M_Viewpoint*)node)->on_set_bind = viewpoint_set_bind;
432
}
433
434
#endif
435
436
static void navinfo_set_bind(GF_Node *node, GF_Route *route)
437
0
{
438
0
  if (node) {
439
0
    ViewStack *st = (ViewStack *) gf_node_get_private(node);
440
0
    Bindable_OnSetBind(node, st->reg_stacks, NULL);
441
0
    gf_sc_invalidate( gf_sc_get_compositor(node), NULL);
442
0
  }
443
0
}
444
445
static void TraverseNavigationInfo(GF_Node *node, void *rs, Bool is_destroy)
446
0
{
447
0
  u32 i;
448
#ifndef GPAC_DISABLE_3D
449
  u32 nb_select_mode;
450
  SFVec3f start, end;
451
  Fixed scale;
452
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
453
#endif
454
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
455
0
  M_NavigationInfo *ni = (M_NavigationInfo *) node;
456
457
0
  if (is_destroy) {
458
0
    DestroyViewStack(node);
459
0
    return;
460
0
  }
461
0
#ifdef GPAC_DISABLE_3D
462
463
  /*FIXME, we only deal with one node, no bind stack for the current time*/
464
0
  for (i=0; i<ni->type.count; i++) {
465
0
    if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) {
466
0
      tr_state->visual->compositor->navigation_disabled = 1;
467
0
    }
468
0
  }
469
#else
470
471
  if (!tr_state->navigations) return;
472
473
  /*first traverse, bound if needed*/
474
  if (gf_list_find(tr_state->navigations, node) < 0) {
475
    gf_list_add(tr_state->navigations, node);
476
    if (gf_list_get(tr_state->navigations, 0) == ni) {
477
      if (!ni->isBound) Bindable_SetIsBound(node, 1);
478
    }
479
    if (gf_list_find(st->reg_stacks, tr_state->navigations)<0)
480
      gf_list_add(st->reg_stacks, tr_state->navigations);
481
    gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
482
    /*in any case don't draw the first time*/
483
    gf_sc_invalidate(tr_state->visual->compositor, NULL);
484
    return;
485
  }
486
  /*not bound*/
487
  if (!ni->isBound) return;
488
  /*not evaluating, return*/
489
  if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
490
    if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) {
491
      if (!gf_mx_equal(&st->world_view_mx, &tr_state->model_matrix)) {
492
        gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
493
        gf_node_dirty_set(node, 0, 0);
494
      }
495
    }
496
    return;
497
  }
498
499
  if (!gf_node_dirty_get(node)) return;
500
  gf_node_dirty_clear(node, 0);
501
502
  nb_select_mode = 0;
503
  tr_state->camera->navigation_flags = 0;
504
  tr_state->camera->navigate_mode = 0;
505
  for (i=0; i<ni->type.count; i++) {
506
    if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "ANY")) tr_state->camera->navigation_flags |= NAV_ANY;
507
    else {
508
      nb_select_mode++;
509
    }
510
511
    if (!tr_state->camera->navigate_mode) {
512
      if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "NONE")) tr_state->camera->navigate_mode = GF_NAVIGATE_NONE;
513
      else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "WALK")) tr_state->camera->navigate_mode = GF_NAVIGATE_WALK;
514
      else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "EXAMINE")) tr_state->camera->navigate_mode = GF_NAVIGATE_EXAMINE;
515
      else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "FLY")) tr_state->camera->navigate_mode = GF_NAVIGATE_FLY;
516
      else if (ni->type.vals[i] && !stricmp(ni->type.vals[i], "VR")) tr_state->camera->navigate_mode = GF_NAVIGATE_VR;
517
    }
518
  }
519
  if (nb_select_mode>1) tr_state->camera->navigation_flags |= NAV_SELECTABLE;
520
521
  if (ni->headlight) tr_state->camera->navigation_flags |= NAV_HEADLIGHT;
522
523
  start.x = start.y = start.z = 0;
524
  end.x = end.y = 0;
525
  end.z = FIX_ONE;
526
  gf_mx_apply_vec(&st->world_view_mx, &start);
527
  gf_mx_apply_vec(&st->world_view_mx, &end);
528
  gf_vec_diff(end, end, start);
529
  scale = gf_vec_len(end);
530
531
  tr_state->camera->speed = gf_mulfix(scale, ni->speed);
532
  tr_state->camera->visibility = gf_mulfix(scale, ni->visibilityLimit);
533
  if (ni->avatarSize.count) tr_state->camera->avatar_size.x = gf_mulfix(scale, ni->avatarSize.vals[0]);
534
  if (ni->avatarSize.count>1) tr_state->camera->avatar_size.y = gf_mulfix(scale, ni->avatarSize.vals[1]);
535
  if (ni->avatarSize.count>2) tr_state->camera->avatar_size.z = gf_mulfix(scale, ni->avatarSize.vals[2]);
536
537
#if 0
538
  if (tr_state->pixel_metrics) {
539
    u32 s = MAX(tr_state->visual->width, tr_state->visual->height);
540
    s /= 2;
541
//    tr_state->camera->speed = ni->speed;
542
    tr_state->camera->visibility *= s;
543
    tr_state->camera->avatar_size.x *= s;
544
    tr_state->camera->avatar_size.y *= s;
545
    tr_state->camera->avatar_size.z *= s;
546
  }
547
#endif
548
549
#endif
550
551
0
}
552
553
void compositor_init_navigation_info(GF_Compositor *compositor, GF_Node *node)
554
0
{
555
0
  ViewStack *st;
556
0
  GF_SAFEALLOC(st, ViewStack);
557
0
  if (!st) {
558
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate navigation stack\n"));
559
0
    return;
560
0
  }
561
562
0
  st->reg_stacks = gf_list_new();
563
0
  gf_node_set_private(node, st);
564
0
  gf_node_set_callback_function(node, TraverseNavigationInfo);
565
0
  ((M_NavigationInfo*)node)->on_set_bind = navinfo_set_bind;
566
567
#ifdef GPAC_ENABLE_COVERAGE
568
  if (gf_sys_is_cov_mode()) {
569
    navinfo_set_bind(NULL, NULL);
570
  }
571
#endif
572
0
}
573
574
575
#ifndef GPAC_DISABLE_3D
576
577
static void fog_set_bind(GF_Node *node, GF_Route *route)
578
{
579
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
580
  Bindable_OnSetBind(node, st->reg_stacks, NULL);
581
  gf_sc_invalidate(gf_sc_get_compositor(node), NULL);
582
}
583
584
static void TraverseFog(GF_Node *node, void *rs, Bool is_destroy)
585
{
586
  Fixed density, vrange;
587
  SFVec3f start, end;
588
  ViewStack *vp_st;
589
  M_Viewpoint *vp;
590
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
591
  M_Fog *fog = (M_Fog *) node;
592
  ViewStack *st = (ViewStack *) gf_node_get_private(node);
593
594
  if (is_destroy) {
595
    DestroyViewStack(node);
596
    return;
597
  }
598
599
  if (!tr_state->fogs) return;
600
601
  /*first traverse, bound if needed*/
602
  if (gf_list_find(tr_state->fogs, node) < 0) {
603
    gf_list_add(tr_state->fogs, node);
604
    if (gf_list_get(tr_state->fogs, 0) == fog) {
605
      if (!fog->isBound) Bindable_SetIsBound(node, 1);
606
    }
607
    if (gf_list_find(st->reg_stacks, tr_state->fogs)<0)
608
      gf_list_add(st->reg_stacks, tr_state->fogs);
609
610
    gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
611
    /*in any case don't draw the first time*/
612
    gf_sc_invalidate(tr_state->visual->compositor, NULL);
613
    return;
614
  }
615
  /*not evaluating, return*/
616
  if (tr_state->traversing_mode != TRAVERSE_BINDABLE) {
617
    if ((tr_state->traversing_mode==TRAVERSE_SORT) || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) )
618
      gf_mx_copy(st->world_view_mx, tr_state->model_matrix);
619
    return;
620
  }
621
  /*not bound*/
622
  if (!fog->isBound || !fog->visibilityRange) return;
623
624
  /*fog visibility is expressed in current bound VP so get its matrix*/
625
  vp = (M_Viewpoint*)gf_list_get(tr_state->viewpoints, 0);
626
  vp_st = NULL;
627
  if (vp && vp->isBound) vp_st = (ViewStack *) gf_node_get_private((GF_Node *)vp);
628
629
  start.x = start.y = start.z = 0;
630
  end.x = end.y = 0;
631
  end.z = fog->visibilityRange;
632
  if (vp_st) {
633
    gf_mx_apply_vec(&vp_st->world_view_mx, &start);
634
    gf_mx_apply_vec(&vp_st->world_view_mx, &end);
635
  }
636
  gf_mx_apply_vec(&st->world_view_mx, &start);
637
  gf_mx_apply_vec(&st->world_view_mx, &end);
638
  gf_vec_diff(end, end, start);
639
  vrange = gf_vec_len(end);
640
641
  density = gf_invfix(vrange);
642
  visual_3d_set_fog(tr_state->visual, fog->fogType.buffer, fog->color, density, vrange);
643
}
644
645
void compositor_init_fog(GF_Compositor *compositor, GF_Node *node)
646
{
647
  ViewStack *st;
648
  GF_SAFEALLOC(st, ViewStack);
649
  if (!st) {
650
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate fog stack\n"));
651
    return;
652
  }
653
654
  st->reg_stacks = gf_list_new();
655
  gf_node_set_private(node, st);
656
  gf_node_set_callback_function(node, TraverseFog);
657
  ((M_Fog*)node)->on_set_bind = fog_set_bind;
658
}
659
660
#endif  /*GPAC_DISABLE_3D*/
661
662
#endif /*GPAC_DISABLE_VRML*/
663
664
665
#endif //!defined(GPAC_DISABLE_COMPOSITOR)