Coverage Report

Created: 2026-06-24 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/mpeg4_grouping.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
27
#include "nodes_stacks.h"
28
#include "mpeg4_grouping.h"
29
#include "visual_manager.h"
30
31
#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)
32
33
/*This is the generic routine for child traversing*/
34
void group_2d_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state)
35
0
{
36
0
  u32 backup;
37
#ifdef GF_SR_USE_VIDEO_CACHE
38
  Bool group_cached;
39
#endif
40
0
  GF_List *sensor_backup;
41
0
  GF_ChildNodeItem *child;
42
43
0
  backup = gf_node_dirty_get(node);
44
0
  if (backup & GF_SG_CHILD_DIRTY) {
45
0
    GF_SensorHandler *hsens;
46
0
    Bool check_anchor=0;
47
0
    u32 ntag = gf_node_get_tag(node);
48
0
    group->flags &= ~GROUP_HAS_SENSORS;
49
0
    if (group->sensors) gf_list_reset(group->sensors);
50
51
0
    drawable_reset_group_highlight(tr_state, node);
52
53
    /*never performs bounds recompute on the fly in 2D since we don't cull 2D groups
54
    but still mark the group as empty*/
55
0
    group->bounds.width = 0;
56
    /*special case for anchor which is a parent node acting as a sensor*/
57
0
    if (ntag==TAG_MPEG4_Anchor) check_anchor=1;
58
0
#ifndef GPAC_DISABLE_X3D
59
0
    else if (ntag==TAG_X3D_Anchor) check_anchor=1;
60
0
#endif
61
0
    if (check_anchor) {
62
0
      GF_SensorHandler *gf_sc_anchor_get_handler(GF_Node *n);
63
64
0
      hsens = gf_sc_anchor_get_handler(node);
65
0
      if (hsens) {
66
0
        if (!group->sensors) group->sensors = gf_list_new();
67
0
        gf_list_add(group->sensors, hsens);
68
0
        group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR;
69
0
      }
70
0
    } else {
71
0
      child = ((GF_ParentNode *)node)->children;
72
0
      while (child) {
73
0
        hsens = compositor_mpeg4_get_sensor_handler_ex(child->node, GF_TRUE);
74
0
        if (hsens) {
75
0
          if (!group->sensors) group->sensors = gf_list_new();
76
0
          gf_list_add(group->sensors, hsens);
77
0
          group->flags |= GROUP_HAS_SENSORS;
78
0
        }
79
0
        child = child->next;
80
0
      }
81
0
    }
82
0
  }
83
  /*sub-tree not dirty and getting bounds, direct copy */
84
0
  else if ((tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) && !tr_state->for_node && group->bounds.width) {
85
0
    tr_state->bounds = group->bounds;
86
0
    return;
87
0
  }
88
89
90
#ifdef GF_SR_USE_VIDEO_CACHE
91
  group_cached = group_2d_cache_traverse(node, group, tr_state);
92
#endif
93
94
  /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't
95
  perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent
96
  graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing
97
  bounds otherwise we'll never re-invalidate the subgraph anymore*/
98
0
  gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
99
100
101
#ifdef GF_SR_USE_VIDEO_CACHE
102
  if (group_cached) return;
103
#endif
104
105
  /*no culling in 2d*/
106
107
  /*picking: collect sensors*/
108
0
  sensor_backup = NULL;
109
0
  if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) {
110
    /*reset sensor stack if any sensors at this level*/
111
0
    sensor_backup = tr_state->vrml_sensors;
112
0
    gf_assert(group->sensors);
113
0
    tr_state->vrml_sensors = group->sensors;
114
0
  }
115
116
117
0
  if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
118
0
    child = ((GF_ParentNode *)node)->children;
119
0
    backup = tr_state->text_split_mode;
120
0
    if (tr_state->text_split_mode && (gf_node_list_get_count(child)>1) ) tr_state->text_split_mode = 0;
121
0
    group->flags &= ~GROUP_SKIP_CULLING;
122
0
    group->bounds.width = group->bounds.height = 0;
123
0
    tr_state->bounds.width = tr_state->bounds.height = 0;
124
#ifndef GPAC_DISABLE_3D
125
    tr_state->bbox.is_set = 0;
126
#endif
127
0
    while (child) {
128
0
      gf_node_traverse(child->node, tr_state);
129
0
      if (tr_state->disable_cull) {
130
0
        group->flags |= GROUP_SKIP_CULLING;
131
0
        tr_state->disable_cull = 0;
132
0
      }
133
      /*handle 3D nodes in 2D groups*/
134
#ifndef GPAC_DISABLE_3D
135
      if (tr_state->bbox.is_set) {
136
        gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox);
137
        tr_state->bbox.is_set = 0;
138
      }
139
#endif
140
0
      gf_rect_union(&group->bounds, &tr_state->bounds);
141
0
      tr_state->bounds.width = tr_state->bounds.height = 0;
142
0
      child = child->next;
143
0
    }
144
145
0
    tr_state->bounds = group->bounds;
146
147
0
    if (group->flags & GROUP_SKIP_CULLING)
148
0
      tr_state->disable_cull = 1;
149
0
    tr_state->text_split_mode = backup;
150
0
  }
151
  /*TRAVERSE_SORT */
152
0
  else if (tr_state->traversing_mode==TRAVERSE_SORT) {
153
0
    Bool prev_inv = tr_state->invalidate_all;
154
#ifdef GF_SR_USE_VIDEO_CACHE
155
    DrawableContext *first_ctx = tr_state->visual->cur_context;
156
    Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0;
157
    u32 cache_too_small = 0;
158
    u32 traverse_time = gf_sys_clock();
159
    u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue);
160
    tr_state->cache_too_small = 0;
161
#endif
162
163
0
    if (backup & GF_SG_VRML_COLOR_DIRTY) {
164
0
      tr_state->invalidate_all = 1;
165
0
      gf_node_dirty_clear(node, GF_SG_VRML_COLOR_DIRTY);
166
0
    }
167
168
0
    child = ((GF_ParentNode *)node)->children;
169
0
    while (child) {
170
0
      gf_node_traverse(child->node, tr_state);
171
0
      child = child->next;
172
#ifdef GF_SR_USE_VIDEO_CACHE
173
      if (tr_state->cache_too_small)
174
        cache_too_small++;
175
#endif
176
0
    }
177
178
0
    tr_state->invalidate_all = prev_inv;
179
180
#ifdef GF_SR_USE_VIDEO_CACHE
181
    if (cache_too_small) {
182
      tr_state->cache_too_small = 1;
183
    } else {
184
      /*get the traversal time for each group*/
185
      traverse_time = gf_sys_clock() - traverse_time;
186
      group->traverse_time += traverse_time;
187
      /*record the traversal information and turn cache on if possible*/
188
      group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx);
189
    }
190
#endif
191
192
0
    drawable_check_focus_highlight(node, tr_state, NULL);
193
0
  }
194
0
  else {
195
0
    child = ((GF_ParentNode *)node)->children;
196
0
    while (child) {
197
0
      gf_node_traverse(child->node, tr_state);
198
0
      child = child->next;
199
0
    }
200
201
0
  }
202
203
0
  if (sensor_backup) {
204
    /*restore previous traversing state sensors */
205
0
    tr_state->vrml_sensors = sensor_backup;
206
0
  }
207
0
}
208
209
/*This is the routine for OrderedGroup child traversing*/
210
void group_2d_traverse_with_order(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, u32 *positions)
211
0
{
212
0
  u32 i, count;
213
0
  u32 backup;
214
0
  GF_List *sensor_backup;
215
0
  GF_Node *child;
216
0
  GF_ChildNodeItem *list;
217
#ifdef GF_SR_USE_VIDEO_CACHE
218
  Bool group_cached;
219
#endif
220
221
0
  backup = gf_node_dirty_get(node);
222
0
  if (backup & GF_SG_CHILD_DIRTY) {
223
0
    GF_SensorHandler *hsens;
224
0
    Bool check_anchor=0;
225
    /*never trigger bounds recompute in 2D since we don't cull 2D groups*/
226
0
    u32 ntag = gf_node_get_tag(node);
227
0
    group->flags &= ~GROUP_HAS_SENSORS;
228
0
    drawable_reset_group_highlight(tr_state, node);
229
    /*special case for anchor which is a parent node acting as a sensor*/
230
0
    if (ntag==TAG_MPEG4_Anchor) check_anchor=1;
231
0
#ifndef GPAC_DISABLE_X3D
232
0
    else if (ntag==TAG_X3D_Anchor) check_anchor=1;
233
0
#endif
234
0
    if (check_anchor) {
235
0
      GF_SensorHandler *gf_sc_anchor_get_handler(GF_Node *n);
236
237
0
      hsens = gf_sc_anchor_get_handler(node);
238
0
      if (hsens) {
239
0
        if (!group->sensors) group->sensors = gf_list_new();
240
0
        gf_list_add(group->sensors, hsens);
241
0
        group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR;
242
0
      }
243
0
    } else {
244
0
      list = ((GF_ParentNode *)node)->children;
245
0
      while (list) {
246
0
        hsens = compositor_mpeg4_get_sensor_handler_ex(list->node, GF_TRUE);
247
0
        if (hsens) {
248
0
          if (!group->sensors) group->sensors = gf_list_new();
249
0
          gf_list_add(group->sensors, hsens);
250
0
          group->flags |= GROUP_HAS_SENSORS;
251
0
        }
252
0
        list = list->next;
253
0
      }
254
0
    }
255
0
  }
256
  /*not parent (eg form, layout...) sub-tree not dirty and getting bounds, direct copy */
257
0
  else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
258
0
    tr_state->bounds = group->bounds;
259
0
    return;
260
0
  }
261
262
#ifdef GF_SR_USE_VIDEO_CACHE
263
  group_cached = group_2d_cache_traverse(node, group, tr_state);
264
#endif
265
266
  /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't
267
  perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent
268
  graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing
269
  bounds otherwise we'll never re-invalidate the subgraph anymore*/
270
0
  gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
271
272
273
#ifdef GF_SR_USE_VIDEO_CACHE
274
  if (group_cached) return;
275
#endif
276
277
  /*picking: collect sensors*/
278
0
  sensor_backup = NULL;
279
0
  if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) {
280
    /*reset sensor stack if any sensors at this level*/
281
0
    sensor_backup = tr_state->vrml_sensors;
282
0
    tr_state->vrml_sensors = group->sensors;
283
0
  }
284
285
0
  if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
286
0
    list = ((GF_ParentNode *)node)->children;
287
0
    backup = tr_state->text_split_mode;
288
0
    if (tr_state->text_split_mode && (gf_node_list_get_count(list)>1) ) tr_state->text_split_mode = 0;
289
0
    group->flags &= ~GROUP_SKIP_CULLING;
290
0
    group->bounds.width = group->bounds.height = 0;
291
0
    tr_state->bounds.width = tr_state->bounds.height = 0;
292
#ifndef GPAC_DISABLE_3D
293
    tr_state->bbox.is_set = 0;
294
#endif
295
0
    count = gf_node_list_get_count(list);
296
0
    for (i=0; i<count; i++) {
297
0
      child = gf_node_list_get_child(list, positions[i]);
298
0
      gf_node_traverse(child, tr_state);
299
0
      if (tr_state->disable_cull) {
300
0
        group->flags |= GROUP_SKIP_CULLING;
301
0
        tr_state->disable_cull = 0;
302
0
      }
303
      /*handle 3D nodes in 2D groups*/
304
#ifndef GPAC_DISABLE_3D
305
      if (tr_state->bbox.is_set) {
306
        gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox);
307
        tr_state->bbox.is_set = 0;
308
      }
309
#endif
310
0
      gf_rect_union(&group->bounds, &tr_state->bounds);
311
0
      tr_state->bounds.width = tr_state->bounds.height = 0;
312
0
    }
313
0
    tr_state->bounds = group->bounds;
314
315
0
    if (group->flags & GROUP_SKIP_CULLING)
316
0
      tr_state->disable_cull = 1;
317
0
    tr_state->text_split_mode = backup;
318
319
    /*TRAVERSE_SORT */
320
0
  } else if (tr_state->traversing_mode==TRAVERSE_SORT) {
321
0
    Bool prev_inv = tr_state->invalidate_all;
322
#ifdef GF_SR_USE_VIDEO_CACHE
323
    DrawableContext *first_ctx = tr_state->visual->cur_context;
324
    u32 cache_too_small = 0;
325
    Bool skip_first_ctx = (first_ctx && first_ctx->drawable) ? 1 : 0;
326
    u32 traverse_time = gf_sys_clock();
327
    u32 last_cache_idx = gf_list_count(tr_state->visual->compositor->cached_groups_queue);
328
    tr_state->cache_too_small = 0;
329
#endif
330
331
0
    if (backup & GF_SG_VRML_COLOR_DIRTY) {
332
0
      tr_state->invalidate_all = 1;
333
0
      gf_node_dirty_clear(node, GF_SG_VRML_COLOR_DIRTY);
334
0
    }
335
336
0
    list = ((GF_ParentNode *)node)->children;
337
0
    count = gf_node_list_get_count(list);
338
0
    for (i=0; i<count; i++) {
339
0
      child = gf_node_list_get_child(list, positions[i]);
340
0
      gf_node_traverse(child, tr_state);
341
#ifdef GF_SR_USE_VIDEO_CACHE
342
      if (tr_state->cache_too_small)
343
        cache_too_small++;
344
#endif
345
0
    }
346
0
    tr_state->invalidate_all = prev_inv;
347
348
#ifdef GF_SR_USE_VIDEO_CACHE
349
    if (cache_too_small) {
350
      tr_state->cache_too_small = 1;
351
    } else {
352
      /*get the traversal time for each group*/
353
      traverse_time = gf_sys_clock() - traverse_time;
354
      group->traverse_time += traverse_time;
355
      /*record the traversal information and turn cache on if possible*/
356
      group_2d_cache_evaluate(node, group, tr_state, first_ctx, skip_first_ctx, last_cache_idx);
357
    }
358
#endif
359
360
0
    drawable_check_focus_highlight(node, tr_state, NULL);
361
0
  } else {
362
0
    list = ((GF_ParentNode *)node)->children;
363
0
    count = gf_node_list_get_count(list);
364
0
    for (i=0; i<count; i++) {
365
0
      child = gf_node_list_get_child(list, positions[i]);
366
0
      gf_node_traverse(child, tr_state);
367
0
    }
368
0
  }
369
370
0
  if (sensor_backup) {
371
    /*restore previous traversing state sensors*/
372
0
    tr_state->vrml_sensors = sensor_backup;
373
0
  }
374
0
}
375
376
/*
377
 *  3D Grouping tools
378
 */
379
380
#ifndef GPAC_DISABLE_3D
381
382
void group_3d_delete(GF_Node *node)
383
{
384
  GroupingNode *group = (GroupingNode *)gf_node_get_private(node);
385
386
  gf_free(group);
387
}
388
389
GroupingNode *group_3d_new(GF_Node *node)
390
{
391
  GroupingNode *st;
392
  GF_SAFEALLOC(st, GroupingNode);
393
  gf_node_set_private(node, st);
394
  return st;
395
}
396
397
/*returns 2 if local light, 1 if global light and 0 if not a light*/
398
static u32 get_light_type(GF_Node *n)
399
{
400
  switch (gf_node_get_tag(n)) {
401
  case TAG_MPEG4_DirectionalLight:
402
#ifndef GPAC_DISABLE_X3D
403
  case TAG_X3D_DirectionalLight:
404
#endif
405
    return 2;
406
  case TAG_MPEG4_PointLight:
407
  case TAG_MPEG4_SpotLight:
408
    return 1;
409
  default:
410
    return 0;
411
  }
412
}
413
/*This is the generic routine for child traversing*/
414
void group_3d_traverse(GF_Node *node, GroupingNode *group, GF_TraverseState *tr_state)
415
{
416
  u32 mode_back;
417
  Bool split_text_backup, do_lights;
418
  DirectionalLightContext *dl;
419
  GF_List *sensor_backup;
420
  GF_SensorHandler *hsens;
421
  GF_ChildNodeItem *l;
422
423
  if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) {
424
    //we are drawing 3D object but configured for 2D, force 3D
425
    if (!tr_state->visual->type_3d && tr_state->visual->compositor->hybrid_opengl) {
426
      tr_state->visual->compositor->root_visual_setup=0;
427
      tr_state->visual->compositor->force_type_3d=1;
428
    }
429
    
430
    /*need to recompute bounds*/
431
    if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) {
432
      /*traverse subtree to recompute bounds*/
433
      mode_back = tr_state->traversing_mode;
434
      tr_state->traversing_mode=TRAVERSE_GET_BOUNDS;
435
      group_3d_traverse(node, group, tr_state);
436
      tr_state->traversing_mode = mode_back;
437
    }
438
    /*we're recomputing bounds*/
439
    else {
440
      u32 ntag = gf_node_get_tag(node);
441
      group->flags &= ~(GROUP_HAS_SENSORS | GROUP_HAS_LIGHTS);
442
443
      /*special case for anchor which is a parent node acting as a sensor*/
444
      if ((ntag==TAG_MPEG4_Anchor)
445
#ifndef GPAC_DISABLE_X3D
446
              || (ntag==TAG_X3D_Anchor)
447
#endif
448
         ) group->flags |= GROUP_HAS_SENSORS;
449
450
      l = ((GF_ParentNode*)node)->children;
451
      while (l) {
452
        hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE);
453
        if (hsens) {
454
          group->flags |= GROUP_HAS_SENSORS;
455
        }
456
        else if (get_light_type(l->node)) {
457
          group->flags |= GROUP_HAS_LIGHTS;
458
        }
459
        l = l->next;
460
      }
461
462
      /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't
463
      perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent
464
      graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing
465
      bounds otherwise we'll never re-invalidate the subgraph anymore*/
466
      gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
467
    }
468
  }
469
  /*not parent (eg form, layout...) sub-tree not dirty and getting bounds, direct copy */
470
  else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
471
    tr_state->bbox = group->bbox;
472
    if (!tr_state->bbox.is_set) tr_state->bbox.radius=-FIX_ONE;
473
    gf_node_dirty_clear(node, 0);
474
    return;
475
  }
476
477
  gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
478
479
  mode_back=tr_state->cull_flag;
480
  /*if culling not disabled*/
481
  if (!(group->flags & GROUP_SKIP_CULLING)
482
          /*for geometry AND lights*/
483
          && (tr_state->traversing_mode==TRAVERSE_SORT)
484
          /*do cull*/
485
          && !visual_3d_node_cull(tr_state, &group->bbox, 0)) {
486
487
    tr_state->cull_flag = mode_back;
488
    return;
489
  }
490
491
492
  /*picking: collect sensors*/
493
  sensor_backup = NULL;
494
  if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) {
495
    /*reset sensor stack if any sensors at this level*/
496
    sensor_backup = tr_state->vrml_sensors;
497
    tr_state->vrml_sensors = gf_list_new();
498
499
    l = ((GF_ParentNode*)node)->children;
500
    while (l) {
501
      hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE);
502
      if (hsens && hsens->IsEnabled(l->node))
503
        gf_list_add(tr_state->vrml_sensors, hsens);
504
505
      l = l->next;
506
    }
507
  }
508
509
  /*turn on local lights and global ones*/
510
  do_lights = 0;
511
  if (group->flags & GROUP_HAS_LIGHTS) {
512
    /*turn on global lights*/
513
    if (tr_state->traversing_mode==TRAVERSE_LIGHTING) {
514
      l = ((GF_ParentNode*)node)->children;
515
      while (l) {
516
        if (get_light_type(l->node)==2) gf_node_traverse(l->node, tr_state);
517
        l = l->next;
518
      }
519
    }
520
    /*turn on local lights*/
521
    else if (tr_state->traversing_mode==TRAVERSE_SORT) {
522
      do_lights = 1;
523
      tr_state->traversing_mode = TRAVERSE_DRAW_3D;
524
      tr_state->local_light_on = 1;
525
526
      l = ((GF_ParentNode*)node)->children;
527
      while (l) {
528
        if (get_light_type(l->node)==1) {
529
          /*store lights for alpha draw*/
530
          dl = (DirectionalLightContext*)gf_malloc(sizeof(DirectionalLightContext));
531
          dl->dlight = l->node;
532
          memcpy(&dl->light_matrix, &tr_state->model_matrix, sizeof(GF_Matrix));
533
          gf_list_add(tr_state->local_lights, dl);
534
          /*and turn them on for non-alpha draw*/
535
          gf_node_traverse(dl->dlight, tr_state);
536
        }
537
        l = l->next;
538
      }
539
      tr_state->traversing_mode = TRAVERSE_SORT;
540
    }
541
  }
542
543
544
  if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
545
    l = ((GF_ParentNode*)node)->children;
546
    split_text_backup = tr_state->text_split_mode;
547
    if (tr_state->text_split_mode && (gf_node_list_get_count(l)>1) ) tr_state->text_split_mode = 0;
548
    group->bbox.is_set = tr_state->bbox.is_set = 0;
549
    tr_state->bounds.width = 0;
550
    group->flags &= ~GROUP_SKIP_CULLING;
551
552
    while (l) {
553
      gf_node_traverse(l->node, tr_state);
554
      if (tr_state->disable_cull) {
555
        group->flags |= GROUP_SKIP_CULLING;
556
        tr_state->disable_cull = 0;
557
      }
558
      /*handle 2D nodes in 3D groups*/
559
      if (tr_state->bounds.width) {
560
        gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds);
561
        tr_state->bounds.width = 0;
562
      }
563
564
      if (tr_state->bbox.is_set) {
565
        gf_bbox_union(&group->bbox, &tr_state->bbox);
566
      }
567
      tr_state->bbox.is_set = 0;
568
      l = l->next;
569
    }
570
    tr_state->bbox = group->bbox;
571
    if (group->flags & GROUP_SKIP_CULLING)
572
      tr_state->disable_cull = 1;
573
    tr_state->text_split_mode = split_text_backup;
574
  } else {
575
    l = ((GF_ParentNode*)node)->children;
576
    while (l) {
577
      gf_node_traverse(l->node, tr_state);
578
      l = l->next;
579
    }
580
581
    if (tr_state->traversing_mode==TRAVERSE_SORT)
582
      drawable3d_check_focus_highlight(node, tr_state, NULL);
583
  }
584
  tr_state->cull_flag = mode_back;
585
586
  if (sensor_backup) {
587
    /*destroy current traversing state sensors and restore previous*/
588
    gf_list_del(tr_state->vrml_sensors);
589
    tr_state->vrml_sensors = sensor_backup;
590
  }
591
592
  /*remove dlights*/
593
  if (do_lights) {
594
    u32 lcount;
595
    tr_state->traversing_mode = TRAVERSE_DRAW_3D;
596
    tr_state->local_light_on = 0;
597
    while ( (lcount = gf_list_count(tr_state->local_lights)) ) {
598
      dl = (DirectionalLightContext*)gf_list_get(tr_state->local_lights, lcount-1);
599
      gf_list_rem(tr_state->local_lights, lcount-1);
600
      gf_node_traverse(dl->dlight, tr_state);
601
      gf_free(dl);
602
    }
603
    /*and back to sort mode*/
604
    tr_state->traversing_mode = TRAVERSE_SORT;
605
  }
606
}
607
608
609
#endif
610
611
612
/*
613
 *  2D ParentNode tools - used by all nodes performing children layout
614
 */
615
616
617
void parent_node_setup(ParentNode2D *group)
618
0
{
619
0
  group->groups = gf_list_new();
620
0
}
621
622
void parent_node_predestroy(ParentNode2D *group)
623
0
{
624
  /*just in case*/
625
0
  parent_node_reset(group);
626
0
  gf_list_del(group->groups);
627
0
}
628
629
void parent_node_reset(ParentNode2D *group)
630
0
{
631
0
  while (gf_list_count(group->groups)) {
632
0
    ChildGroup *cg = (ChildGroup *)gf_list_get(group->groups, 0);
633
0
    gf_list_rem(group->groups, 0);
634
0
    gf_free(cg);
635
0
  }
636
0
}
637
638
void parent_node_start_group(ParentNode2D *group, GF_Node *n, Bool discardable)
639
0
{
640
0
  ChildGroup *cg;
641
0
  if (!n) {
642
0
    cg = gf_list_last(group->groups);
643
0
    if (!cg) return;
644
0
    n = cg->child;
645
0
  }
646
0
  GF_SAFEALLOC(cg, ChildGroup);
647
0
  if (!cg) {
648
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate child group\n"));
649
0
    return;
650
0
  }
651
0
  cg->child = n;
652
0
  cg->text_type = discardable;
653
0
  gf_list_add(group->groups, cg);
654
0
}
655
656
void parent_node_end_group(ParentNode2D *group, GF_Rect *bounds)
657
0
{
658
0
  ChildGroup *cg = (ChildGroup *)gf_list_last(group->groups);
659
0
  if (!cg) return;
660
  /*don't override splitted text info*/
661
0
  if (cg->ascent || cg->descent) return;
662
0
  cg->original = *bounds;
663
0
  cg->final = cg->original;
664
0
}
665
666
void parent_node_end_text_group(ParentNode2D *group, GF_Rect *bounds, Fixed ascent, Fixed descent, u32 text_split_idx)
667
0
{
668
0
  ChildGroup *cg = (ChildGroup *)gf_list_last(group->groups);
669
0
  if (!cg) return;
670
0
  cg->text_split_idx = text_split_idx;
671
0
  cg->ascent = ascent;
672
0
  cg->descent = descent;
673
0
  cg->final = cg->original = *bounds;
674
0
}
675
676
677
678
void parent_node_traverse(GF_Node *node, ParentNode2D *group, GF_TraverseState *tr_state)
679
0
{
680
0
  Bool split_text_backup;
681
0
  GF_List *sensor_backup;
682
0
  GF_ChildNodeItem *l;
683
684
0
  if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) {
685
0
    Bool check_anchor=0;
686
    /*parent groups must recompute their bounds themselves since they modify children layout*/
687
0
    u32 ntag = gf_node_get_tag(node);
688
0
    group->flags &= ~GROUP_HAS_SENSORS;
689
    /*special case for anchor which is a parent node acting as a sensor*/
690
0
    if (ntag==TAG_MPEG4_Anchor) check_anchor=1;
691
0
#ifndef GPAC_DISABLE_X3D
692
0
    else if (ntag==TAG_X3D_Anchor) check_anchor=1;
693
0
#endif
694
0
    if (check_anchor) {
695
0
      group->flags |= GROUP_HAS_SENSORS | GROUP_IS_ANCHOR;
696
0
    } else {
697
0
      l = ((GF_ParentNode *)node)->children;
698
0
      while (l) {
699
0
        GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE);
700
0
        if (hsens) {
701
0
          group->flags |= GROUP_HAS_SENSORS;
702
0
          break;
703
0
        }
704
0
        l = l->next;
705
0
      }
706
0
    }
707
708
    /*now here is the trick: ExternProtos may not be loaded at this point, in which case we can't
709
    perform proper culling. Unloaded ExternProto signal themselves by invalidating their parent
710
    graph to get a new traversal. We must therefore reset the CHILD_DIRTY flag before computing
711
    bounds otherwise we'll never re-invalidate the subgraph anymore*/
712
0
    gf_node_dirty_clear(node, GF_SG_CHILD_DIRTY);
713
0
  }
714
0
  gf_node_dirty_clear(node, GF_SG_NODE_DIRTY);
715
716
  /*no culling in 2D*/
717
718
  /*picking: collect sensors*/
719
0
  sensor_backup = NULL;
720
0
  if ((tr_state->traversing_mode==TRAVERSE_PICK) && (group->flags & GROUP_HAS_SENSORS) ) {
721
    /*reset sensor stack if any sensors at this level*/
722
0
    sensor_backup = tr_state->vrml_sensors;
723
0
    tr_state->vrml_sensors = gf_list_new();
724
725
726
    /*add sensor(s) to traversing state*/
727
0
    l = ((GF_ParentNode *)node)->children;
728
0
    while (l) {
729
0
      GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE);
730
0
      if (hsens) gf_list_add(tr_state->vrml_sensors, hsens);
731
0
      l = l->next;
732
0
    }
733
0
  }
734
735
736
0
  split_text_backup = tr_state->text_split_mode;
737
738
0
  group->flags &= ~GROUP_SKIP_CULLING;
739
0
  tr_state->bounds.width = tr_state->bounds.height = 0;
740
#ifndef GPAC_DISABLE_3D
741
  tr_state->bbox.is_set = 0;
742
#endif
743
744
0
  l = ((GF_ParentNode *)node)->children;
745
0
  while (l) {
746
0
    parent_node_start_group(group, l->node, 0);
747
748
0
    tr_state->bounds.width = tr_state->bounds.height = 0;
749
750
0
    gf_node_traverse(l->node, tr_state);
751
752
    /*handle 3D nodes in 2D groups*/
753
#ifndef GPAC_DISABLE_3D
754
    if (tr_state->bbox.is_set) {
755
      gf_rect_from_bbox(&tr_state->bounds, &tr_state->bbox);
756
      tr_state->bbox.is_set = 0;
757
    }
758
#endif
759
0
    parent_node_end_group(group, &tr_state->bounds);
760
0
    l = l->next;
761
0
  }
762
0
  tr_state->text_split_mode = split_text_backup;
763
764
0
  if (sensor_backup) {
765
    /*destroy current traversing state sensors and restore previous*/
766
0
    gf_list_del(tr_state->vrml_sensors);
767
0
    tr_state->vrml_sensors = sensor_backup;
768
0
  }
769
0
}
770
771
/*final drawing of each group*/
772
void parent_node_child_traverse(ChildGroup *cg, GF_TraverseState *tr_state)
773
0
{
774
0
  Fixed dx, dy;
775
776
0
  dx = cg->final.x - cg->original.x + cg->scroll_x;
777
0
  dy = cg->final.y - cg->original.y + cg->scroll_y;
778
0
  tr_state->text_split_idx = cg->text_split_idx;
779
780
#ifndef GPAC_DISABLE_3D
781
  if (tr_state->visual->type_3d) {
782
    GF_Matrix mx_bckup;
783
784
    gf_mx_copy(mx_bckup, tr_state->model_matrix);
785
    gf_mx_add_translation(&tr_state->model_matrix, dx, dy, 0);
786
    gf_node_traverse(cg->child, tr_state);
787
    gf_mx_copy(tr_state->model_matrix, mx_bckup);
788
  } else
789
#endif
790
0
  {
791
0
    GF_Matrix2D mx2d;
792
0
    gf_mx2d_copy(mx2d, tr_state->transform);
793
0
    gf_mx2d_init(tr_state->transform);
794
0
    gf_mx2d_add_translation(&tr_state->transform, dx, dy);
795
0
    gf_mx2d_add_matrix(&tr_state->transform, &mx2d);
796
0
    gf_node_traverse(cg->child, tr_state);
797
0
    gf_mx2d_copy(tr_state->transform, mx2d);
798
0
  }
799
0
  tr_state->text_split_idx = 0;
800
0
}
801
802
void parent_node_child_traverse_matrix(ChildGroup *cg, GF_TraverseState *tr_state, GF_Matrix2D *mat2D)
803
0
{
804
0
  if (!mat2D) return;
805
806
0
  tr_state->text_split_idx = cg->text_split_idx;
807
#ifndef GPAC_DISABLE_3D
808
  if (tr_state->visual->type_3d) {
809
    GF_Matrix mx, mx_bckup;
810
    gf_mx_from_mx2d(&mx, mat2D);
811
    gf_mx_copy(mx_bckup, tr_state->model_matrix);
812
    gf_mx_add_matrix(&tr_state->model_matrix, &mx);
813
    gf_node_traverse(cg->child, tr_state);
814
    gf_mx_copy(tr_state->model_matrix, mx_bckup);
815
  } else
816
#endif
817
0
  {
818
0
    GF_Matrix2D mx2d;
819
820
0
    gf_mx2d_copy(mx2d, tr_state->transform);
821
0
    gf_mx2d_pre_multiply(&tr_state->transform, mat2D);
822
0
    gf_node_traverse(cg->child, tr_state);
823
0
    gf_mx2d_copy(tr_state->transform, mx2d);
824
0
  }
825
0
  tr_state->text_split_idx = 0;
826
0
}
827
828
#endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)