예제 #1
0
static void RenderLayer2D(GF_Node *node, void *rs, Bool is_destroy)
{
	u32 i;
	GF_List *prevback, *prevviews;
	GF_Rect clip;
	M_Viewport *vp;
	ChildGroup2D *cg;
	GF_Matrix2D gf_mx2d_bck;
	GroupingNode2D *parent_bck;
	DrawableContext *back_ctx;
	Bool bool_bck;
	DrawableContext *ctx;
	M_Background2D *back;
	M_Layer2D *l = (M_Layer2D *)node;
	Layer2DStack *l2D = (Layer2DStack *) gf_node_get_private(node);
	RenderEffect2D *eff;

	if (is_destroy) {
		DeleteGroupingNode2D((GroupingNode2D *)l2D);
		gf_list_del(l2D->backs);
		gf_list_del(l2D->views);
		free(l2D);
		return;
	}

	eff = (RenderEffect2D *) rs;
	gf_mx2d_copy(gf_mx2d_bck, eff->transform);
	parent_bck = eff->parent;
	eff->parent = (GroupingNode2D *) l2D;
	gf_mx2d_init(eff->transform);
	bool_bck = eff->draw_background;
	prevback = eff->back_stack;
	prevviews = eff->view_stack;
	eff->back_stack = l2D->backs;
	eff->view_stack = l2D->views;

	if (l2D->first) {
		/*render on back first to register with stack*/
		if (l->background) {
			eff->draw_background = 0;
			gf_node_render((GF_Node*) l->background, eff);
			group2d_reset_children((GroupingNode2D*) l2D);
			eff->draw_background = 1;
		}
		vp = (M_Viewport*)l->viewport;
		if (vp) {
			gf_list_add(l2D->views, vp);
			if (!vp->isBound) {
				vp->isBound = 1;
				gf_node_event_out_str((GF_Node*)vp, "isBound");
			}
		}
	}

	back = NULL;
	if (gf_list_count(l2D->backs) ) {
		back = (M_Background2D*)gf_list_get(l2D->backs, 0);
		if (!back->isBound) back = NULL;
	}
	vp = NULL;
	if (gf_list_count(l2D->views)) {
		vp = (M_Viewport*)gf_list_get(l2D->views, 0);
		if (!vp->isBound) vp = NULL;
	}
	if (!eff->is_pixel_metrics) gf_mx2d_add_scale(&eff->transform, eff->min_hsize, eff->min_hsize);
	l2D->clip = R2D_ClipperToPixelMetrics(eff, l->size);

	/*apply viewport*/
	if (vp) {
		clip = l2D->clip;
		vp_setup((GF_Node *) vp, eff, &clip);
	}


	back_ctx = NULL;
	if (back) {
		/*setup back size and render*/
		group2d_start_child((GroupingNode2D *)l2D);
		
		eff->draw_background = 1;
		ctx = b2D_GetContext(back, l2D->backs);
		ctx->bi->unclip = l2D->clip;
		ctx->bi->clip = gf_rect_pixelize(&ctx->bi->unclip);
		gf_mx2d_init(ctx->transform);
		gf_node_render((GF_Node *) back, eff);
		eff->draw_background = 0;
	
		/*we need a trick since we're not using a dedicated surface for layer rendering, 
		we emulate the back context: remove previous context and insert fake one*/
		if (!(eff->trav_flags & TF_RENDER_DIRECT) && (gf_list_count(l2D->groups)==1)) {
			ChildGroup2D *cg = (ChildGroup2D *)gf_list_get(l2D->groups, 0);
			back_ctx = VS2D_GetDrawableContext(eff->surface);
			gf_list_rem(cg->contexts, 0);
			gf_list_add(cg->contexts, back_ctx);
			back_ctx->h_texture = ctx->h_texture;
			back_ctx->flags = ctx->flags;
			back_ctx->flags &= ~CTX_IS_TRANSPARENT;
			back_ctx->flags |= CTX_IS_BACKGROUND;
			back_ctx->aspect = ctx->aspect;
			back_ctx->drawable = ctx->drawable;
			drawable_check_bounds(back_ctx, eff->surface);
			back_ctx->bi->clip = ctx->bi->clip;
			back_ctx->bi->unclip = ctx->bi->unclip;
		}
		group2d_end_child((GroupingNode2D *)l2D);
	}

	group2d_traverse((GroupingNode2D *)l2D, l->children, eff);
	/*restore effect*/
	eff->draw_background = bool_bck;
	gf_mx2d_copy(eff->transform, gf_mx2d_bck);
	eff->parent = parent_bck;
	eff->back_stack = prevback;
	eff->view_stack = prevviews;

	/*check bindables*/
	if (l2D->first) {
		Bool redraw = 0;
		l2D->first = 0;
		if (!back && gf_list_count(l2D->backs)) redraw = 1;
		if (!vp && gf_list_count(l2D->views) ) redraw = 1;

		/*we missed background or viewport (was not declared as bound during traversal, and is bound now)*/
		if (redraw) {
			group2d_reset_children((GroupingNode2D*)l2D);
			gf_sr_invalidate(l2D->compositor, NULL);
			return;
		}
	}

	i=0;
	while ((cg = (ChildGroup2D *)gf_list_enum(l2D->groups, &i))) {
		child2d_render_done(cg, eff, &l2D->clip);
	}
	group2d_reset_children((GroupingNode2D*)l2D);

	group2d_force_bounds(eff->parent, &l2D->clip);
}
예제 #2
0
Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_state)
{
	u32 k, i, count, num_nodes, num_changed;
	GF_IRect refreshRect;
	Bool redraw_all;
	Bool hyb_force_redraw=GF_FALSE;
#ifndef GPAC_DISABLE_VRML
	M_Background2D *bck = NULL;
	DrawableContext *bck_ctx = NULL;
#endif
	DrawableContext *ctx;
	struct _drawable_store *it, *prev;
	DrawableContext *first_opaque = NULL;
	Bool has_clear = 0;
	Bool has_changed = 0;
	Bool redraw_all_on_background_change = GF_TRUE;

	/*in direct mode the visual is always redrawn*/
	if (tr_state->immediate_draw) {
		/*flush pending contexts due to overlays*/
		visual_2d_flush_overlay_areas(visual, tr_state);

		visual_2d_release_raster(visual);
		visual_clean_contexts(visual);
		visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
		return 1;
	}

	num_changed = 0;

	/*if the aspect ratio has changed redraw everything*/
	redraw_all = tr_state->invalidate_all;

#ifndef GPAC_DISABLE_3D
	if (visual->compositor->hybrid_opengl && !visual->offscreen) redraw_all_on_background_change = GF_FALSE;
#endif
	/*check for background changes for transparent nodes*/
#ifndef GPAC_DISABLE_VRML
	bck = (M_Background2D*)gf_list_get(visual->back_stack, 0);
	if (bck) {
		if (!bck->isBound) {
			if (visual->last_had_back) {
				if (redraw_all_on_background_change) redraw_all = 1;
				else hyb_force_redraw = 1;
			}
			visual->last_had_back = 0;
		} else {
			bck_ctx = b2d_get_context(bck, visual->back_stack);
			if (!visual->last_had_back || (bck_ctx->flags & CTX_REDRAW_MASK) ) {
				if (redraw_all_on_background_change) redraw_all = 1;
				else hyb_force_redraw = 1;
			}
			visual->last_had_back = (bck_ctx->aspect.fill_texture && !bck_ctx->aspect.fill_texture->transparent) ? 2 : 1;
		}
	} else
#endif
		if (visual->last_had_back) {
			visual->last_had_back = 0;
			if (redraw_all_on_background_change) redraw_all = 1;
			else hyb_force_redraw = 1;
		}

	num_nodes = 0;
	ctx = visual->context;
	while (ctx && ctx->drawable) {
		num_nodes++;

		drawctx_update_info(ctx, visual);
		if (!redraw_all) {
			u32 res;
			assert( gf_irect_inside(&visual->top_clipper, &ctx->bi->clip) );
			res = register_context_rect(&visual->to_redraw, ctx, num_nodes, &first_opaque);
			if (res) {
				num_changed ++;
				if (res==2)
					hyb_force_redraw=GF_TRUE;
			}

		}
		ctx = ctx->next;
	}

	/*garbage collection*/

	/*clear all remaining bounds since last frames (the ones that moved or that are not drawn this frame)*/
	prev = NULL;
	it = visual->prev_nodes;
	while (it) {
		while (drawable_get_previous_bound(it->drawable, &refreshRect, visual)) {
			if (!redraw_all) {
				//assert( gf_irect_inside(&visual->top_clipper, &refreshRect) );
				gf_irect_intersect(&refreshRect, &visual->top_clipper);
				register_dirty_rect(&visual->to_redraw, &refreshRect);
				has_clear=1;
			}
		}
		/*if node is marked as undrawn, remove from visual*/
		if (!(it->drawable->flags & DRAWABLE_DRAWN_ON_VISUAL)) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Node %s no longer on visual - unregistering it\n", gf_node_get_class_name(it->drawable->node)));

			/*remove all bounds info related to this visual and unreg node */
			drawable_reset_bounds(it->drawable, visual);

			it->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL;

			if (it->drawable->flags & DRAWABLE_IS_OVERLAY) {
				visual->compositor->video_out->Blit(visual->compositor->video_out, NULL, NULL, NULL, 1);
			}

			if (prev) prev->next = it->next;
			else visual->prev_nodes = it->next;
			if (!it->next) visual->last_prev_entry = prev;
			gf_free(it);
			it = prev ? prev->next : visual->prev_nodes;
		} else {
			prev = it;
			it = it->next;
		}
	}

	if (redraw_all) {
		ra_clear(&visual->to_redraw);
		ra_add(&visual->to_redraw, &visual->surf_rect);
#ifdef TRACK_OPAQUE_REGIONS
		visual->to_redraw.list[0].opaque_node_index=0;
#endif
	} else {
		ra_refresh(&visual->to_redraw);

		if (visual->compositor->debug_defer) {
			visual->ClearSurface(visual, &visual->top_clipper, 0);
		}
	}

	/*nothing to redraw*/
	if (!hyb_force_redraw && ra_is_empty(&visual->to_redraw) ) {
#ifndef GPAC_DISABLE_3D
		//force canvas draw
		visual->nb_objects_on_canvas_since_last_ogl_flush = 1;
#endif
		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] No changes found since last frame - skipping redraw\n"));
		goto exit;
	}
	has_changed = 1;
	tr_state->traversing_mode = TRAVERSE_DRAW_2D;

	if (first_opaque && (visual->to_redraw.count==1) && gf_rect_equal(first_opaque->bi->clip, visual->to_redraw.list[0].rect)) {
		visual->has_modif=0;
		goto skip_background;
	}

	/*redraw everything*/
#ifndef GPAC_DISABLE_VRML
	if (bck_ctx) {
		drawable_check_bounds(bck_ctx, visual);
		tr_state->ctx = bck_ctx;
		visual->draw_node_index = 0;

		/*force clearing entire zone, not just viewport, when using color. If texture, we MUST
		use the VP clipper in order to compute offsets when blitting bitmaps*/
		if (bck_ctx->aspect.fill_texture && bck_ctx->aspect.fill_texture->stream) {
			bck_ctx->bi->clip = visual->top_clipper;
		} else {
			bck_ctx->bi->clip = visual->surf_rect;
		}
		bck_ctx->bi->unclip = gf_rect_ft(&bck_ctx->bi->clip);
		bck_ctx->next = visual->context;
		bck_ctx->flags |= CTX_BACKROUND_NOT_LAYER;
		gf_node_traverse(bck_ctx->drawable->node, tr_state);
		bck_ctx->flags &= ~CTX_BACKROUND_NOT_LAYER;
	} else
#endif /*GPAC_DISABLE_VRML*/
	{
		count = visual->to_redraw.count;
		for (k=0; k<count; k++) {
			GF_IRect rc;
			/*opaque area, skip*/
#ifdef TRACK_OPAQUE_REGIONS
			if (visual->to_redraw.list[k].opaque_node_index > 0) continue;
#endif
			rc = visual->to_redraw.list[k].rect;
			visual->ClearSurface(visual, &rc, 0);
		}
#ifndef GPAC_DISABLE_3D
		if (!count && hyb_force_redraw) {
			compositor_2d_hybgl_clear_surface_ex(tr_state->visual, NULL, 0, GF_FALSE);
		}
#endif
	}
	if (!redraw_all && !has_clear) visual->has_modif=0;

skip_background:

#ifndef GPAC_DISABLE_LOG
	if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_INFO)) {
		GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s - %d dirty rects\n)", num_changed, num_nodes, redraw_all ? "yes" : "no", visual->to_redraw.count));
		if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n"));

		for (i=0; i<visual->to_redraw.count; i++) {
			GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].rect.x, visual->to_redraw.list[i].rect.y, visual->to_redraw.list[i].rect.width, visual->to_redraw.list[i].rect.height));
			assert(visual->to_redraw.list[i].rect.width);
		}
	}
#endif

	visual->draw_node_index = 0;

	ctx = visual->context;
	while (ctx && ctx->drawable) {

		visual->draw_node_index ++;
		tr_state->ctx = ctx;

		/*if overlay we cannot remove the context and cannot draw directly*/
		if (! visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) {

			if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) {
				gf_node_traverse(ctx->drawable->node, tr_state);
			} else {
				drawable_draw(ctx->drawable, tr_state);
			}
		}
		ctx = ctx->next;
	}
	/*flush pending contexts due to overlays*/
	visual_2d_flush_overlay_areas(visual, tr_state);
#ifndef GPAC_DISABLE_VRML
	if (bck_ctx) bck_ctx->next = NULL;
#endif

	if (visual->direct_flush) {
		GF_DirtyRectangles dr;
		u32 i;
		dr.count = visual->to_redraw.count;
		dr.list = gf_malloc(sizeof(GF_IRect)*dr.count);
		for (i=0; i<dr.count; i++) {
			dr.list[i] = visual->to_redraw.list[i].rect;
		}
		visual->compositor->video_out->FlushRectangles(visual->compositor->video_out, &dr);
		visual->compositor->skip_flush=1;
		gf_free(dr.list);
	}

exit:
	/*clear dirty rects*/
	ra_clear(&visual->to_redraw);
	visual_2d_release_raster(visual);
	visual_clean_contexts(visual);
	visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
	return has_changed;
}
예제 #3
0
static void TraverseLayer2D(GF_Node *node, void *rs, Bool is_destroy)
{
	GF_List *oldb, *oldv;
	GF_Node *viewport;
	GF_Node *back;
	Bool prev_layer;
	GF_Matrix2D backup;
	SFVec2f prev_vp;

#ifndef GPAC_DISABLE_3D
	GF_Matrix mx3d;
	GF_List *oldf, *oldn;
	GF_List *node_list_backup;
	GF_Rect prev_clipper;
	Bool had_clip;
#endif
	
	M_Layer2D *l = (M_Layer2D *)node;
	Layer2DStack *st = (Layer2DStack *) gf_node_get_private(node);
	GF_TraverseState *tr_state = (GF_TraverseState *) rs;
	
	if (is_destroy) {
		gf_list_del(st->backs);
		gf_list_del(st->views);
		group_2d_destroy(node, (GroupingNode2D*)st);
		gf_free(st);
		return;
	}

	/*layers can only be used in a 2D context*/
#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d && tr_state->camera->is_3D) return;
#endif

	/*layer2D maintains its own stacks*/
	oldb = tr_state->backgrounds;
	oldv = tr_state->viewpoints;
	tr_state->backgrounds = st->backs;
	tr_state->viewpoints = st->views;
	prev_layer = tr_state->is_layer;
	tr_state->is_layer = 1;
#ifndef GPAC_DISABLE_3D
	oldf = tr_state->fogs;
	oldn = tr_state->navigations;
	tr_state->fogs = tr_state->navigations = NULL;
#endif

	l2d_CheckBindables(node, tr_state, st->first);

	back = (GF_Node*)gf_list_get(st->backs, 0);

	viewport = (GF_Node*)gf_list_get(st->views, 0);

	if ((tr_state->traversing_mode == TRAVERSE_SORT) || (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS)) {
		/*override group bounds*/
		visual_get_size_info(tr_state, &st->clip.width, &st->clip.height);
		/*setup bounds in local coord system*/
		if (l->size.x>=0) st->clip.width = l->size.x;
		if (l->size.y>=0) st->clip.height = l->size.y;
		st->clip = gf_rect_center(st->clip.width, st->clip.height);
		st->bounds = st->clip;
	}
	
	prev_vp = tr_state->vp_size;
	tr_state->vp_size.x = st->clip.width;
	tr_state->vp_size.y = st->clip.height;

	switch (tr_state->traversing_mode) {
	case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
		if (tr_state->visual->type_3d) {
			tr_state->layer_clipper = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 1);

			visual_3d_matrix_push(tr_state->visual);
			gf_mx_copy(mx3d, tr_state->model_matrix);

			/*setup clipping*/
			visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper);
			
			/*apply background BEFORE viewport*/
			if (back) {
				tr_state->traversing_mode = TRAVERSE_BINDABLE;
				gf_bbox_from_rect(&tr_state->bbox, &st->clip);
				gf_node_traverse(back, tr_state);
			}

			/*sort all children without transform, and use current transform when flushing contexts*/
			gf_mx_init(tr_state->model_matrix);

			/*apply viewport*/
			if (viewport) {
				tr_state->traversing_mode = TRAVERSE_BINDABLE;
				tr_state->bounds = st->clip;
				gf_node_traverse(viewport, tr_state);
				visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m);
			}


			node_list_backup = tr_state->visual->alpha_nodes_to_draw;
			tr_state->visual->alpha_nodes_to_draw = gf_list_new();
			tr_state->traversing_mode = TRAVERSE_SORT;
			/*reset cull flag*/
			tr_state->cull_flag = 0;
			group_2d_traverse(node, (GroupingNode2D *)st, tr_state);

			visual_3d_flush_contexts(tr_state->visual, tr_state);
			tr_state->traversing_mode = TRAVERSE_SORT;

			assert(!gf_list_count(tr_state->visual->alpha_nodes_to_draw));
			gf_list_del(tr_state->visual->alpha_nodes_to_draw);
			tr_state->visual->alpha_nodes_to_draw = node_list_backup;

			
			visual_3d_matrix_pop(tr_state->visual);
			gf_mx_copy(tr_state->model_matrix, mx3d);

			visual_3d_reset_clipper_2d(tr_state->visual);

			tr_state->has_layer_clip = had_clip;
			if (had_clip) {
				tr_state->layer_clipper = prev_clipper;
				visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper);
			}
		} else 
#endif
		{
			GF_IRect prev_clip;
			GF_Rect rc;

			gf_mx2d_copy(backup, tr_state->transform);

			prev_clip = tr_state->visual->top_clipper;
			rc = st->clip;
			
			/*get clipper in world coordinate*/
			gf_mx2d_apply_rect(&tr_state->transform, &rc);

			if (viewport) {
				tr_state->traversing_mode = TRAVERSE_BINDABLE;
				tr_state->bounds = st->clip;
				gf_node_traverse(viewport, tr_state);
#if VIEWPORT_CLIPS
				/*move viewport box in world coordinate*/
				gf_mx2d_apply_rect(&backup, &tr_state->bounds);
				/*and intersect with layer clipper*/
				rect_intersect(&rc, &tr_state->bounds);
#endif
			}

			tr_state->visual->top_clipper = gf_rect_pixelize(&rc);
			gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip);
			tr_state->traversing_mode = TRAVERSE_SORT;

			if (tr_state->visual->top_clipper.width && tr_state->visual->top_clipper.height) {
				if (back && Bindable_GetIsBound(back) ) {
					DrawableContext *ctx;

					ctx = b2d_get_context((M_Background2D*) back, st->backs);
					gf_mx2d_init(ctx->transform);
					ctx->bi->clip = tr_state->visual->top_clipper;
					ctx->bi->unclip = rc;

					if (tr_state->immediate_draw) {
						tr_state->ctx = ctx;
						tr_state->traversing_mode = TRAVERSE_DRAW_2D;
						gf_node_traverse(back, tr_state);
						tr_state->traversing_mode = TRAVERSE_SORT;
						tr_state->ctx = NULL;
					} else {
						DrawableContext *back_ctx = visual_2d_get_drawable_context(tr_state->visual);

						gf_node_traverse(back, tr_state);

						back_ctx->flags = ctx->flags;
						back_ctx->flags &= ~CTX_IS_TRANSPARENT;
						back_ctx->flags |= CTX_IS_BACKGROUND;
						back_ctx->aspect = ctx->aspect;
						back_ctx->drawable = ctx->drawable;
						drawable_check_bounds(back_ctx, tr_state->visual);
						back_ctx->bi->clip = ctx->bi->clip;
						back_ctx->bi->unclip = ctx->bi->unclip;
					}
					/*keep track of node drawn*/
					if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) {
						struct _drawable_store *it;
						GF_SAFEALLOC(it, struct _drawable_store);
						it->drawable = ctx->drawable;
						if (tr_state->visual->last_prev_entry) {
							tr_state->visual->last_prev_entry->next = it;
							tr_state->visual->last_prev_entry = it;
						} else {
							tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it;
						}
						GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node)));
						ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL;
					}
				}

				group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
			}
			tr_state->visual->top_clipper = prev_clip;
			gf_mx2d_copy(tr_state->transform, backup);
		}
		break;
		
		/*check picking - we must fall in our 2D clipper*/
	case TRAVERSE_PICK:
		if (gf_sc_pick_in_clipper(tr_state, &st->clip)) {
		
#ifndef GPAC_DISABLE_3D
			if (tr_state->visual->type_3d) {
				/*apply viewport*/
				if (viewport) {
					gf_mx_copy(mx3d, tr_state->model_matrix);
					tr_state->traversing_mode = TRAVERSE_BINDABLE;
					tr_state->bounds = st->clip;
					gf_node_traverse(viewport, tr_state);
					tr_state->traversing_mode = TRAVERSE_PICK;
					group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
					gf_mx_copy(tr_state->model_matrix, mx3d);
				} else {
					group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
				}
			} else 
#endif
			{
				if (viewport) {
					gf_mx2d_copy(backup, tr_state->transform);
					tr_state->traversing_mode = TRAVERSE_BINDABLE;
					tr_state->bounds = st->clip;
					gf_node_traverse(viewport, tr_state);
					tr_state->traversing_mode = TRAVERSE_PICK;
					group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
					gf_mx2d_copy(tr_state->transform, backup);
				} else {
					group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
				}
			}	
		}
		break;
	case TRAVERSE_GET_BOUNDS:
		if (tr_state->for_node) {
			group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
		} else {
			tr_state->bounds = st->clip;
#ifndef GPAC_DISABLE_3D
			gf_bbox_from_rect(&tr_state->bbox, &st->clip);
#endif
		}
		break;

	case TRAVERSE_DRAW_2D:
		group_2d_traverse(node, (GroupingNode2D *)st, tr_state);
		break;

#ifndef GPAC_DISABLE_3D
	/*drawing a layer means drawing all sub-elements as a whole (no depth sorting with parents)*/
	case TRAVERSE_DRAW_3D:
		assert(0);
		break;
#endif
	}
	
	/*restore traversing state*/
	tr_state->vp_size = prev_vp;
	tr_state->backgrounds = oldb;
	tr_state->viewpoints = oldv;
	tr_state->is_layer = prev_layer;
#ifndef GPAC_DISABLE_3D
	tr_state->fogs = oldf;
	tr_state->navigations = oldn;
#endif

	/*in case we missed bindables*/
	if (st->first) {
		st->first = 0;
		gf_sc_invalidate(tr_state->visual->compositor, NULL);
	}
}