How to get the editor's camera?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Zylann
:warning: Old Version Published before Godot 3 was released.

Is there a way to get the editor’s camera from a tool node or editor plugin? I need it to perform a raycast to my custom node.

Why don’t just try to use a camera node first?

shackra | 2016-07-24 01:34

Because I am making an editor tool. It must work even without camera in the scene.
The fact is, there is actually one (otherwise the 3D editor wouldn’t show anything).
But the problem is… where can I get it?

Zylann | 2016-07-24 01:36

Does the editor actually have a Camera attached? Or is it just the Viewport?

timoschwarzer | 2016-07-25 07:39

The editor is made with the engine itself, so I believe it has a Camera somewhere. I tried get_viewport().get_camera() but it returns null.

Zylann | 2016-07-25 23:01

Yes, I know. But why there should be a Camera then? You don’t need camera movement, zoom and/or rotation within the whole editor… Or do you mean the camera for the editor’s viewport?

timoschwarzer | 2016-07-26 04:35

I need the editor’s camera because I need to perform a raycast from the mouse to a custom 3D object in the scene, so yeah, I need the camera that is used to render the editor’s 3D viewport.

Zylann | 2016-07-26 21:48

I have the same problem, did you found something in the end Zylann?

kubecz3k | 2016-10-19 13:09

At the moment I only found access to the camera by overriding forward_spatial_input_event(), which is where you get input and the said camera: https://github.com/Zylann/godot_terrain_plugin/blob/master/addons/zylann.terrain/terrain_plugin.gd#L122

It’s not convenient to have it in process though, and it looks hacky to cache it from the input method :confused:

Zylann | 2016-10-19 21:35

Thanks for info! Maybe I will be able to hack something after all :slight_smile:

kubecz3k | 2016-10-20 08:12

I found a VERY hacky workaround for 2D, which can be adopted to 3D:
First we need to apply this diff to expose the transform matrix from CanvasItemEditor. (You can do the same with the camera for SpatialEditor). EditorCanvasTranform.diff · GitHub
Then we can get the editor canvas transform in our EditorPlugin using get_editor_viewport().get_child(0).get_canvas_transform_matrix(). get_child(1) is the SpatialEditor. Again: this should be exposed in another way since this relies on the Editor’s order in the EditorNode, which may change in the future.

timoschwarzer | 2016-10-28 22:22

I know it’s an old post, but didn’t found a solution elsewhere. Pretty sure it worked differently in 3.0 as compared to 3.1 (alpha), but… Anyway, another a bit less hackier approach to get the editor’s viewport is: get_tree().get_edited_scene_root().get_parent()

Edited:
Nevermind. This hack sometimes needs another .get_parent() (and at other times that’s the ViewportContainer).

takaturre | 2018-09-23 01:05

:bust_in_silhouette: Reply From: yikescloud

I found I can manually change editor camera by this way:

func get_editor_camera(camera_index):

  var e = get_editor_interface().get_editor_viewport()
  var cam = e.get_child(1).get_child(1).get_child(0).get_child(0).get_child(cam_index).get_child(0).get_child(0).get_camera()
  return cam

If the function not work,you can use code below to check(new version godot change the camera hierarchy)

#	var root = e.get_parent()
#	var all_nodes = []
#	var c = root.get_children()
#	while !c.empty():
#		var child_list = []                        
#		for i in c:
#			child_list += i.get_children()
#		all_nodes += child_list
#		c = child_list
#	for node in all_nodes:
#		if node.get_class() == "Camera":
#			print(node.transform)
#			print(node.get_path())
#			for i in range(deep):
#				node = node.get_parent()
#				print(node.get_class() + str(node.get_index()))
#	print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")

I know the camera (or anything else) can be obtained by forcing some code into the scene tree of the editor, but this is not what I’m after. A solution like this is a hack that can break any moment when new versions of Godot release. I wish there was an official API for it.

Zylann | 2021-04-03 17:01

Agree. Recently I make editor plugin for my own game, I found the plugin API still lacks a lot of thing, for example it seems cannot draw something only on certain editor viewport. I hope this will get improve later.

yikescloud | 2021-04-08 06:38

Do we have API for access to the editor camera for now as it is 4.0 beta?

godotlearner | 2022-11-24 15:23

:bust_in_silhouette: Reply From: kidinashell

For Godot 4, 3D Viewport Cameras

(Could probably be appropriated for 2D as well)


If you just want the code, you can scroll down to the full snippets (titled Implementation). I try to explain first, how this approach is flexible enough, so in case of a code change of Godot itself, there shouldn’t be too much change needed in the plugin itself.


Node Structure/Search for a decent starting point

First we have to look at the structure, how the viewport cameras are set up. In contrast to the in-scene cameras (you create yourself) the viewport cameras are separate (and yes, plural camera’s because there are ALWAYS four of them).

They exist in the editor interface control structure. So by iterating through the whole control tree, you can find out, that there is the following structure repeating itself four times:

Node3DEditorViewport
  SubViewportContainer
    SubViewport
      Camera3D

We see, that we have a class name which is easily identifiable in a full recursive tree search: Node3DEditorViewport. This class doesn’t exist anywhere else but in these four instances. And we also see, that further down the line, they contain a Camera3D Node. These nodes are the viewport cameras in the editor.


Why are there four cameras?:

Since the editor supports the option to display four different viewports at once (via the View menu tab in the 3D viewport editor), there are four such viewports (and their cameras) prepared in advance, in case you want to view more than one viewport. The interesting thing to note here, is that those four viewports/cameras are created in the interface at the start of Godot. So whether you actually use one or all of them (you don’t even have to open a 3D scene for them to be created), you can get those nodes once at the starting point which will be relevant for later, when we decide how we want to manage those cameras in our plugin.


Implementation:

(This example is a use case of a dock which will be used in the editor. You can of course use everything in the plugin class itself, but most likely you want the viewport camera information for a reason to use in the editor, so you’ll most likely know for yourself, how you want to handle this)

First you have your typical editor plugin:

get_editor_camera.gd

@tool
extends EditorPlugin


const GET_EDITOR_CAMERA_DOCK_PATH = \
    "res://addons/get_editor_camera/get_editor_camera_dock.tscn"


var _dock


func _enter_tree():
	_dock = preload(GET_EDITOR_CAMERA_DOCK_PATH).instantiate()
	_dock.init(get_editor_interface())
	
	add_control_to_dock(EditorPlugin.DOCK_SLOT_RIGHT_BL, _dock)


func _exit_tree():
	remove_control_from_docks(_dock)

In this case I use a custom init function to be able to pass the editor interface to the subsequent dock object, since I use the camera information in that dock.

Next, we have our subsequent class:
(as mentioned before you can do this in the plugin script as well, though I believe you probably want to be able to interact with some kind of button or other custom interface in the editor, so this use case is most likely the default one)

get_editor_camera_dock.gd (this script is of course attached to our dock .tscn)

@tool
extends Control


const NODE_3D_VIEWPORT_CLASS_NAME = "Node3DEditorViewport"


var _editor_interface : EditorInterface
var _editor_viewports : Array = []
var _editor_cameras : Array = []


func init(editor_interface : EditorInterface):
	_editor_interface = editor_interface


func _ready():
	_find_viewports(_editor_interface.get_base_control())
	for v in _editor_viewports:
		_find_cameras(v)
	
	#--- Connect signals to buttons etc., whatever you want to do, 
         depending on your use case.


func _find_viewports(n : Node):
	if n.get_class() == NODE_3D_VIEWPORT_CLASS_NAME:
		_editor_viewports.append(n)
	
	for c in n.get_children():
		_find_viewports(c)


func _find_cameras(n : Node):
	if n is Camera3D:
		_editor_cameras.append(n)
		return
	
	for c in n.get_children():
		_find_cameras(c)


func _on_btn_create_camera_from_viewport_pressed():
	var camera = _editor_cameras[0] # index 0 -> top-left
#	var camera = _editor_cameras[1] # index 1 -> top-right
#	var camera = _editor_cameras[2] # index 2 -> bottom-left
#	var camera = _editor_cameras[3] # index 3 -> bottom-right
	
	
	#--- do with the camera information whatever you want

1) Now first make sure, that subsequent scripts you use in an editor plugin are flagged as a tool script as well with the initial @tool

2) The constant NODE_3D_VIEWPORT_CLASS_NAME defines the class name Node3DEditorViewport of the top most node we want to find in our editor interface node tree, from which on we can be as sure as possible, that under that node, we can find the viewport camera nodes.
Since this class name will probably change the least (but it’s possible of course in future versions) we define it as a constant, to be able to change just this value if a change like that should occur.

3) Next come the variables/properties of our dock class:

  • _editor_interface → initialized in our custom init function
  • _editor_viewports → holds all four 3d viewports of our editor
  • _editor_cameras → holds all four 3d cameras from our four 3d viewports

Even though we’ll only really need the camera information for further processing, we still define an array for the viewports outside a function, since we’ll be populating this array with a recursive function. There are other ways to do this, but it’s just the easiest so why not :wink:

4) In our init function we pass the editor interface object (from the plugin script before) to the dock variable.

5) We override the _ready function in which we will populate the _editor_viewports and _editor_cameras array. Since I mentioned before, that the viewport and camera nodes exist even if we haven’t opened any 3D scene in the editor, we can do this here once, and don’t have to do it everytime we want to do something with the viewport camera information.

We first populate the viewports array (via the recursive function _find_viewports) and then populate the cameras array by searching through the tree of each viewport node (it’s children). Since it’s possible that the tree structure might change in future version of Godot, this approach, in my opinion, is the most flexible at the moment.

6) Let’s get into details of the _find_viewports recursive function. The first time we call it from _ready we pass the base control of our editor interface. Think of the base control as the first node in the user interface tree of the whole editor itself. From there we search down the tree until we find our Node3DEditorViewport Node from the structure mentioned above, and add it to the viewports array. If we find one, we can just stop the recursion for this level in the node tree by simply return the function. If the node is not the right class, we just go further down the tree. In the end, we will have four viewports in our _editor_viewports array.

7) Let’s get into details of the _find_cameras recursive function. We call this function with each viewport node in the _editor_viewports array to search down that particular node tree until we find the 3d camera. We do it this way (and not with get_node(path)) since it’s possible, that future Godot versions may change that node tree layout. This way, we can make it at least a little fool proof and a bit more flexible.
At the end, we have now four camera nodes in the _editor_cameras array.

Which camera information to use?

Here is a little screenshot to show you, which index of the array contains which camera in the viewport (if you only use one it’s 0):
https://drive.google.com/file/d/1xEJyeLeS1vWbbEvIEA43bokVxd76bGwp/view?usp=share_link

Or in text:

  • 0 → Upper left viewport camera (or main camera if only one viewport visible)
  • 1 → Upper right viewport camera
  • 2 → Bottom left viewport camera
  • 3 → Bottom right viewport camera

Of course you can always define constants for those indeces in your script as well, so you can make absolutely sure, that you always use the right one for your situation.

And voila!
Now you can access the viewport camera information just by getting the correct camera node (whichever one you need), by using the different indeces.
(Example function body for quick camera creation from viewport):

var camera = _editor_cameras[0]

var new_scene_camera = Camera3D.new()

# defined somewhere else, doesn't really matter,
# this is just an example ;)
selected_node.add(new_scene_camera)

new_scene_camera.owner = _editor_interface.get_edited_scene_root()
new_scene_camera.global_position = camera.global_position
new_scene_camera.global_rotation = camera.global_rotation

Appendix

Isn’t iterating over each node in the editor interface node tree to find the viewports inefficient?:
Yes it is. But since you only need to do it once at the start of Godot (or the moment you activate your plugin) this doesn’t really matter. The nodes stay the same as long as Godot is open, so you only need to do it once. And this one time, it doesn’t even take half a second. Sometimes it’s just enough if something works. It’s not that important, since you won’t ship your editor plugin within your game to the end user anyway.

The information of the four cameras is dependent on the current scene
Even though the nodes exist permanently, the information of them (location, rotation, etc.) will depend upon the currently selected scene in the 3D editor. So make sure, before you click any buttons on your custom plugin dock (or however you want to handle it), that you open the scene/select it in your 3D editor tab list, on which you want to use the camera information on.

Somehow I can’t edit the post (I wanted to correct screenshot link to make it actually link and not an empty picture, the link is still correct, you can still copy and paste it into your browser url box)
It says the text is too long for an edit (even though the initial answer was ok xD)

kidinashell | 2023-03-30 15:58

not sure if this work with 2D cause I don’t see anything related

EmptySpace | 2023-05-29 01:13