فهرست منبع

Merge pull request #69952 from lawnjelly/canvas_layer_ordering

Consistent render ordering for CanvasLayers
Rémi Verschelde 2 سال پیش
والد
کامیت
54e293bfcb
2فایلهای تغییر یافته به همراه44 افزوده شده و 9 حذف شده
  1. 41 9
      scene/main/canvas_layer.cpp
  2. 3 0
      scene/main/canvas_layer.h

+ 41 - 9
scene/main/canvas_layer.cpp

@@ -35,7 +35,11 @@
 void CanvasLayer::set_layer(int p_xform) {
 	layer = p_xform;
 	if (viewport.is_valid()) {
-		VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
+		// For the sublayer, we will use the order in which the layer occurs in the scene tree
+		// rather than just the child_id via get_position_in_parent().
+		// We have 32 bits to play with for the sublayer (or more likely 31, as sublayer is signed)
+		// (see Viewport::CanvasKey constructor in visual_server_viewport.h).
+		VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, _layer_order_in_tree);
 		vp->_gui_set_root_order_dirty();
 	}
 }
@@ -59,6 +63,34 @@ void CanvasLayer::set_visible(bool p_visible) {
 	}
 }
 
+// Make sure layer orders are up to date whenever moving layers in the tree.
+void CanvasLayer::_update_layer_orders() {
+	if (is_inside_tree() && get_tree()) {
+		Node *root = get_tree()->get_root();
+		if (root) {
+			uint32_t layer_order_count = 0;
+			_calculate_layer_orders_in_tree(root, layer_order_count);
+		}
+	}
+}
+
+// When rendering layers, if the layer id (set by user) and sublayer (child id) is the same
+// we need something else sensible which we can sort repeatably to determine which layers should render first,
+// so we will simply calculate the order that the layers occur throughout the scene tree.
+void CanvasLayer::_calculate_layer_orders_in_tree(Node *p_node, uint32_t &r_order) {
+	CanvasLayer *layer = Object::cast_to<CanvasLayer>(p_node);
+	if (layer) {
+		layer->_layer_order_in_tree = r_order++;
+
+		// Force an update of the layer order in the VisualServer
+		set_layer(get_layer());
+	}
+
+	for (int n = 0; n < p_node->get_child_count(); n++) {
+		_calculate_layer_orders_in_tree(p_node->get_child(n), r_order);
+	}
+}
+
 void CanvasLayer::show() {
 	set_visible(true);
 }
@@ -183,25 +215,24 @@ void CanvasLayer::_notification(int p_what) {
 			viewport = vp->get_viewport_rid();
 
 			VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
-			VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
 			VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
 			_update_follow_viewport();
-
+			_update_layer_orders();
 		} break;
 		case NOTIFICATION_EXIT_TREE: {
 			ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
+			_update_layer_orders();
 
 			vp->_canvas_layer_remove(this);
 			VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
 			viewport = RID();
 			_update_follow_viewport(false);
-
 		} break;
 		case NOTIFICATION_MOVED_IN_PARENT: {
-			if (is_inside_tree()) {
-				VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
-			}
-
+			// Note: As this step requires traversing the entire scene tree, it is thus expensive
+			// to move the canvas layer multiple times. Take special care when deleting / moving
+			// multiple nodes to prevent multiple NOTIFICATION_MOVED_IN_PARENT occurring.
+			_update_layer_orders();
 		} break;
 	}
 }
@@ -248,8 +279,8 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) {
 		viewport = vp->get_viewport_rid();
 
 		VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
-		VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent());
 		VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
+		_update_layer_orders();
 	}
 }
 
@@ -379,6 +410,7 @@ CanvasLayer::CanvasLayer() {
 	visible = true;
 	follow_viewport = false;
 	follow_viewport_scale = 1.0;
+	_layer_order_in_tree = 0;
 }
 
 CanvasLayer::~CanvasLayer() {

+ 3 - 0
scene/main/canvas_layer.h

@@ -45,6 +45,7 @@ class CanvasLayer : public Node {
 	int layer;
 	Transform2D transform;
 	RID canvas;
+	uint32_t _layer_order_in_tree;
 
 	ObjectID custom_viewport_id; // to check validity
 	Viewport *custom_viewport;
@@ -61,6 +62,8 @@ class CanvasLayer : public Node {
 	void _update_xform();
 	void _update_locrotscale();
 	void _update_follow_viewport(bool p_force_exit = false);
+	void _calculate_layer_orders_in_tree(Node *p_node, uint32_t &r_order);
+	void _update_layer_orders();
 
 protected:
 	void _notification(int p_what);