From 47358bfaab8f1120a542c1cde0cb011760cb4a47 Mon Sep 17 00:00:00 2001
From: Relintai <relintai@protonmail.com>
Date: Fri, 12 Aug 2022 18:29:04 +0200
Subject: [PATCH] Backported:  add viewport.get_camera_2d()

* there is now a more clear distinction between camera_2d and camera_3d functions in the engine code
* simplified camera2d's exported interface - now everything happens directly with the 'current' variable and make_current and clear_current are no longer exposed- there were some situations where calling one instead of set_current would result in incomplete results
* rebased to current godot master
- verdog
https://github.com/godotengine/godot/commit/879f84d8f8e7d9467bb9049b445d576916c847c0
- Note that i did not rename the original Camera related methods.
---
 doc/classes/Camera2D.xml | 14 +------------
 doc/classes/Viewport.xml |  7 +++++++
 scene/2d/camera_2d.cpp   | 44 ++++++++++++++++++++++++++--------------
 scene/2d/camera_2d.h     |  2 +-
 scene/main/viewport.cpp  | 11 ++++++++++
 scene/main/viewport.h    |  5 +++++
 6 files changed, 54 insertions(+), 29 deletions(-)

diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml
index 76e60418e..739ef59a3 100644
--- a/doc/classes/Camera2D.xml
+++ b/doc/classes/Camera2D.xml
@@ -20,12 +20,6 @@
 				Aligns the camera to the tracked node.
 			</description>
 		</method>
-		<method name="clear_current">
-			<return type="void" />
-			<description>
-				Removes any [Camera2D] from the ancestor [Viewport]'s internal currently-assigned camera.
-			</description>
-		</method>
 		<method name="force_update_scroll">
 			<return type="void" />
 			<description>
@@ -60,12 +54,6 @@
 				Returns the specified camera limit. See also [member limit_bottom], [member limit_top], [member limit_left], and [member limit_right].
 			</description>
 		</method>
-		<method name="make_current">
-			<return type="void" />
-			<description>
-				Make this the current 2D camera for the scene (viewport and layer), in case there are many cameras in the scene.
-			</description>
-		</method>
 		<method name="reset_smoothing">
 			<return type="void" />
 			<description>
@@ -94,7 +82,7 @@
 		<member name="anchor_mode" type="int" setter="set_anchor_mode" getter="get_anchor_mode" enum="Camera2D.AnchorMode" default="1">
 			The Camera2D's anchor point. See [enum AnchorMode] constants.
 		</member>
-		<member name="current" type="bool" setter="_set_current" getter="is_current" default="false">
+		<member name="current" type="bool" setter="set_current" getter="is_current" default="false">
 			If [code]true[/code], the camera is the active camera for the current scene. Only one camera can be current, so setting a different camera [code]current[/code] will disable this one.
 		</member>
 		<member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport">
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index a70e4c43c..432bfb544 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -35,6 +35,13 @@
 				Returns the first valid [World2D] for this viewport, searching the [member world_2d] property of itself and any Viewport ancestor.
 			</description>
 		</method>
+		<method name="get_camera_2d" qualifiers="const">
+			<return type="Camera2D">
+			</return>
+			<description>
+				Returns the currently active 2D camera. Returns null if there are no active cameras.
+			</description>
+		</method>
 		<method name="get_camera" qualifiers="const">
 			<return type="Camera" />
 			<description>
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index ebc921d87..1c168b549 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -256,7 +256,10 @@ void Camera2D::_notification(int p_what) {
 			// it should take over as the current camera, and mark
 			// all other cameras as non current
 			first = true;
-			_set_current(current);
+
+			if (is_current()) {
+				viewport->_camera_2d_set(this);
+			}
 
 		} break;
 		case NOTIFICATION_EXIT_TREE: {
@@ -264,6 +267,7 @@ void Camera2D::_notification(int p_what) {
 			if (is_current()) {
 				if (viewport && viewport_valid) {
 					viewport->set_canvas_transform(Transform2D());
+					clear_current();
 				}
 			}
 			if (viewport && viewport_valid) {
@@ -400,18 +404,29 @@ Camera2D::Camera2DProcessMode Camera2D::get_process_mode() const {
 void Camera2D::_make_current(Object *p_which) {
 	if (p_which == this) {
 		current = true;
+		if (is_inside_tree()) {
+			get_viewport()->_camera_2d_set(this);
+			update();
+		}
 	} else {
 		current = false;
+		if (is_inside_tree()) {
+			if (get_viewport()->get_camera_2d() == this) {
+				get_viewport()->_camera_2d_set(nullptr);
+			}
+			update();
+		}
 	}
 }
 
-void Camera2D::_set_current(bool p_current) {
+void Camera2D::set_current(bool p_current) {
 	if (p_current) {
 		make_current();
+	} else {
+		if (current) {
+			clear_current();
+		}
 	}
-
-	current = p_current;
-	update();
 }
 
 bool Camera2D::is_current() const {
@@ -419,18 +434,20 @@ bool Camera2D::is_current() const {
 }
 
 void Camera2D::make_current() {
-	if (!is_inside_tree()) {
-		current = true;
-	} else {
+	if (is_inside_tree()) {
 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this);
+	} else {
+		current = true;
 	}
+
 	_update_scroll();
 }
 
 void Camera2D::clear_current() {
-	current = false;
 	if (is_inside_tree()) {
 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr);
+	} else {
+		current = false;
 	}
 }
 
@@ -649,17 +666,14 @@ void Camera2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_rotating", "rotating"), &Camera2D::set_rotating);
 	ClassDB::bind_method(D_METHOD("is_rotating"), &Camera2D::is_rotating);
 
-	ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
-	ClassDB::bind_method(D_METHOD("clear_current"), &Camera2D::clear_current);
-	ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
-
 	ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll);
 
 	ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Camera2D::set_process_mode);
 	ClassDB::bind_method(D_METHOD("get_process_mode"), &Camera2D::get_process_mode);
 
-	ClassDB::bind_method(D_METHOD("_set_current", "current"), &Camera2D::_set_current);
+	ClassDB::bind_method(D_METHOD("set_current", "current"), &Camera2D::set_current);
 	ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
+	ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
 
 	ClassDB::bind_method(D_METHOD("set_limit", "margin", "limit"), &Camera2D::set_limit);
 	ClassDB::bind_method(D_METHOD("get_limit", "margin"), &Camera2D::get_limit);
@@ -713,7 +727,7 @@ void Camera2D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "_set_current", "is_current");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom"), "set_zoom", "get_zoom");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode");
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index 1ef1ef325..baff8f2a9 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -86,7 +86,7 @@ protected:
 	void _setup_viewport();
 
 	void _make_current(Object *p_which);
-	void _set_current(bool p_current);
+	void set_current(bool p_current);
 
 	bool screen_drawing_enabled;
 	bool limit_drawing_enabled;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 5910898ac..d3116bceb 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -34,6 +34,7 @@
 #include "core/os/input.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
+#include "scene/2d/camera_2d.h"
 #include "scene/2d/collision_object_2d.h"
 #include "scene/2d/listener_2d.h"
 #include "scene/3d/camera.h"
@@ -944,6 +945,10 @@ void Viewport::_camera_set(Camera *p_camera) {
 #endif
 }
 
+void Viewport::_camera_2d_set(Camera2D *p_camera_2d) {
+	camera_2d = p_camera_2d;
+}
+
 bool Viewport::_camera_add(Camera *p_camera) {
 	cameras.insert(p_camera);
 	return cameras.size() == 1;
@@ -1168,6 +1173,10 @@ Camera *Viewport::get_camera() const {
 	return camera;
 }
 
+Camera2D *Viewport::get_camera_2d() const {
+	return camera_2d;
+}
+
 void Viewport::enable_camera_override(bool p_enable) {
 #ifndef _3D_DISABLED
 	if (p_enable == camera_override) {
@@ -3266,6 +3275,7 @@ void Viewport::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("is_using_own_world"), &Viewport::is_using_own_world);
 
 	ClassDB::bind_method(D_METHOD("get_camera"), &Viewport::get_camera);
+	ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d);
 
 	ClassDB::bind_method(D_METHOD("set_as_audio_listener", "enable"), &Viewport::set_as_audio_listener);
 	ClassDB::bind_method(D_METHOD("is_audio_listener"), &Viewport::is_audio_listener);
@@ -3436,6 +3446,7 @@ Viewport::Viewport() {
 	parent = nullptr;
 	listener = nullptr;
 	camera = nullptr;
+	camera_2d = nullptr;
 	override_canvas_transform = false;
 	canvas_layers.insert(NULL); // This eases picking code (interpreted as the canvas of the Viewport)
 	arvr = false;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 50ff36eaf..33c8c55cb 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -181,6 +181,7 @@ private:
 	} camera_override;
 
 	Camera *camera;
+	Camera2D *camera_2d;
 	Set<Camera *> cameras;
 	Listener2D *listener_2d = nullptr;
 	Set<CanvasLayer *> canvas_layers;
@@ -406,6 +407,9 @@ private:
 	void _camera_remove(Camera *p_camera);
 	void _camera_make_next_current(Camera *p_exclude);
 
+	friend class Camera2D;
+	void _camera_2d_set(Camera2D *p_camera_2d);
+
 	friend class Listener2D;
 	void _listener_2d_set(Listener2D *p_listener);
 	void _listener_2d_remove(Listener2D *p_listener);
@@ -430,6 +434,7 @@ protected:
 public:
 	Listener *get_listener() const;
 	Camera *get_camera() const;
+	Camera2D *get_camera_2d() const;
 
 	void enable_camera_override(bool p_enable);
 	bool is_camera_override_enabled() const;