1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-09 12:50:35 +00:00

Cleanup and simplify camera override API

- Harmonise the camera override 2D and 3D APIs
- Switch to using Camera2D/3D nodes to provide override functionality. This makes for simpler code, gets rid of much of copy-pasted camera code and makes code that relies on current viewport camera such as VisibleOnScreenNotifiers and object picking work out of the box.
- Make camera override code only accesible within DEBUG_ENABLED builds
This commit is contained in:
rxlecky
2025-05-30 21:42:11 +02:00
parent 825ef2387f
commit 02e1919514
4 changed files with 241 additions and 328 deletions

View File

@@ -49,12 +49,14 @@
<return type="Camera2D" />
<description>
Returns the currently active 2D camera. Returns [code]null[/code] if there are no active cameras.
[b]Note:[/b] If called while the [i]Camera Override[/i] system is active in editor, this will return the internally managed override camera. It is therefore advised to avoid caching the return value, or to check that the cached value is still a valid instance and is the current camera before use. See [method @GlobalScope.is_instance_valid] and [method Camera2D.is_current].
</description>
</method>
<method name="get_camera_3d" qualifiers="const">
<return type="Camera3D" />
<description>
Returns the currently active 3D camera.
Returns the currently active 3D camera. Returns [code]null[/code] if there are no active cameras.
[b]Note:[/b] If called while the [i]Camera Override[/i] system is active in editor, this will return the internally managed override camera. It is therefore advised to avoid caching the return value, or to check that the cached value is a valid instance and is the current camera before use. See [method @GlobalScope.is_instance_valid] and [member Camera3D.current].
</description>
</method>
<method name="get_canvas_cull_mask_bit" qualifiers="const">

View File

@@ -38,6 +38,7 @@
#include "core/object/script_language.h"
#include "core/os/time.h"
#include "core/templates/local_vector.h"
#include "scene/2d/camera_2d.h"
#include "scene/gui/popup_menu.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/scene_tree.h"
@@ -196,7 +197,7 @@ Error SceneDebugger::_msg_override_cameras(const Array &p_args) {
ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA);
bool enable = p_args[0];
bool from_editor = p_args[1];
SceneTree::get_singleton()->get_root()->enable_canvas_transform_override(enable);
SceneTree::get_singleton()->get_root()->enable_camera_2d_override(enable);
#ifndef _3D_DISABLED
SceneTree::get_singleton()->get_root()->enable_camera_3d_override(enable);
#endif // _3D_DISABLED
@@ -206,8 +207,9 @@ Error SceneDebugger::_msg_override_cameras(const Array &p_args) {
Error SceneDebugger::_msg_transform_camera_2d(const Array &p_args) {
ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA);
ERR_FAIL_COND_V(!SceneTree::get_singleton()->get_root()->is_camera_2d_override_enabled(), ERR_BUG);
Transform2D transform = p_args[0];
SceneTree::get_singleton()->get_root()->set_canvas_transform_override(transform);
SceneTree::get_singleton()->get_root()->get_override_camera_2d()->set_transform(transform);
RuntimeNodeSelect::get_singleton()->_queue_selection_update();
return OK;
}
@@ -215,17 +217,20 @@ Error SceneDebugger::_msg_transform_camera_2d(const Array &p_args) {
#ifndef _3D_DISABLED
Error SceneDebugger::_msg_transform_camera_3d(const Array &p_args) {
ERR_FAIL_COND_V(p_args.size() < 5, ERR_INVALID_DATA);
ERR_FAIL_COND_V(!SceneTree::get_singleton()->get_root()->is_camera_3d_override_enabled(), ERR_BUG);
Transform3D transform = p_args[0];
bool is_perspective = p_args[1];
float size_or_fov = p_args[2];
float depth_near = p_args[3];
float depth_far = p_args[4];
Camera3D *override_camera = SceneTree::get_singleton()->get_root()->get_override_camera_3d();
if (is_perspective) {
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(size_or_fov, depth_near, depth_far);
override_camera->set_perspective(size_or_fov, depth_near, depth_far);
} else {
SceneTree::get_singleton()->get_root()->set_camera_3d_override_orthogonal(size_or_fov, depth_near, depth_far);
override_camera->set_orthogonal(size_or_fov, depth_near, depth_far);
}
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(transform);
override_camera->set_transform(transform);
RuntimeNodeSelect::get_singleton()->_queue_selection_update();
return OK;
}
@@ -1598,24 +1603,25 @@ void RuntimeNodeSelect::_select_set_mode(SelectMode p_mode) {
void RuntimeNodeSelect::_set_camera_override_enabled(bool p_enabled) {
camera_override = p_enabled;
if (p_enabled) {
_update_view_2d();
}
#ifndef _3D_DISABLED
Window *root = SceneTree::get_singleton()->get_root();
if (camera_first_override) {
_reset_camera_2d();
#ifndef _3D_DISABLED
_reset_camera_3d();
#endif // _3D_DISABLED
camera_first_override = false;
} else if (p_enabled) {
_update_view_2d();
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
}
#ifndef _3D_DISABLED
ERR_FAIL_COND(!root->is_camera_3d_override_enabled());
Camera3D *override_camera = root->get_override_camera_3d();
override_camera->set_transform(_get_cursor_transform());
override_camera->set_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
#endif // _3D_DISABLED
}
}
void RuntimeNodeSelect::_root_window_input(const Ref<InputEvent> &p_event) {
Window *root = SceneTree::get_singleton()->get_root();
@@ -1756,13 +1762,16 @@ void RuntimeNodeSelect::_process_frame() {
}
if (direction != Vector3()) {
Window *root = SceneTree::get_singleton()->get_root();
ERR_FAIL_COND(!root->is_camera_3d_override_enabled());
// Calculate the process time manually, as the time scale is frozen.
const double process_time = (1.0 / Engine::get_singleton()->get_frames_per_second()) * Engine::get_singleton()->get_unfrozen_time_scale();
const Vector3 motion = direction * speed * process_time;
cursor.pos += motion;
cursor.eye_pos += motion;
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
root->get_override_camera_3d()->set_transform(_get_cursor_transform());
}
}
#endif // _3D_DISABLED
@@ -2091,8 +2100,6 @@ void RuntimeNodeSelect::_queue_selection_update() {
}
void RuntimeNodeSelect::_update_selection() {
Window *root = SceneTree::get_singleton()->get_root();
RS::get_singleton()->canvas_item_clear(sbox_2d_ci);
RS::get_singleton()->canvas_item_set_visible(sbox_2d_ci, selection_visible);
@@ -2109,13 +2116,7 @@ void RuntimeNodeSelect::_update_selection() {
continue;
}
Transform2D xform;
// Cameras (overridden or not) don't affect `CanvasLayer`s.
if (root->is_canvas_transform_override_enabled() && !(ci->get_canvas_layer_node() && !ci->get_canvas_layer_node()->is_following_viewport())) {
xform = root->get_canvas_transform_override() * ci->get_global_transform();
} else {
xform = ci->get_global_transform_with_canvas();
}
Transform2D xform = ci->get_global_transform_with_canvas();
// Fallback.
Rect2 rect = Rect2(Vector2(), Vector2(10, 10));
@@ -2247,27 +2248,8 @@ void RuntimeNodeSelect::_update_selection_drag(const Point2 &p_end_pos) {
return;
}
Window *root = SceneTree::get_singleton()->get_root();
Rect2 selection_drawing;
int thickness;
if (root->is_canvas_transform_override_enabled()) {
Transform2D xform = root->get_canvas_transform_override();
RS::get_singleton()->canvas_item_set_transform(sel_drag_ci, xform);
RS::get_singleton()->canvas_item_reset_physics_interpolation(sel_drag_ci);
selection_drawing.position = xform.affine_inverse().xform(selection_drag_area.position);
selection_drawing.size = xform.affine_inverse().xform(p_end_pos);
thickness = MAX(1, Math::ceil(1 / view_2d_zoom));
} else {
RS::get_singleton()->canvas_item_set_transform(sel_drag_ci, Transform2D());
RS::get_singleton()->canvas_item_reset_physics_interpolation(sel_drag_ci);
selection_drawing.position = selection_drag_area.position;
selection_drawing.size = p_end_pos;
thickness = 1;
}
selection_drawing.size -= selection_drawing.position;
selection_drawing = selection_drawing.abs();
Rect2 selection_drawing = selection_drag_area.abs();
int thickness = 1;
const Vector2 endpoints[4] = {
selection_drawing.position,
@@ -2352,9 +2334,10 @@ void RuntimeNodeSelect::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_n
Window *root = SceneTree::get_singleton()->get_root();
Point2 pos;
// Cameras (overridden or not) don't affect `CanvasLayer`s.
// Cameras don't affect `CanvasLayer`s.
if (!ci->get_canvas_layer_node() || ci->get_canvas_layer_node()->is_following_viewport()) {
pos = (root->is_canvas_transform_override_enabled() ? root->get_canvas_transform_override() : root->get_canvas_transform()).affine_inverse().xform(p_pos);
pos = root->get_canvas_transform().affine_inverse().xform(p_pos);
} else {
pos = p_pos;
}
@@ -2414,9 +2397,9 @@ void RuntimeNodeSelect::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_
Window *root = SceneTree::get_singleton()->get_root();
Rect2 rect;
// Cameras (overridden or not) don't affect `CanvasLayer`s.
// Cameras don't affect `CanvasLayer`s.
if (!ci->get_canvas_layer_node() || ci->get_canvas_layer_node()->is_following_viewport()) {
rect = (root->is_canvas_transform_override_enabled() ? root->get_canvas_transform_override() : root->get_canvas_transform()).affine_inverse().xform(p_rect);
rect = root->get_canvas_transform().affine_inverse().xform(p_rect);
} else {
rect = p_rect;
}
@@ -2475,18 +2458,30 @@ void RuntimeNodeSelect::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Re
}
void RuntimeNodeSelect::_reset_camera_2d() {
view_2d_offset = -SceneTree::get_singleton()->get_root()->get_canvas_transform().get_origin();
Window *root = SceneTree::get_singleton()->get_root();
Camera2D *game_camera = root->is_camera_2d_override_enabled() ? root->get_overridden_camera_2d() : root->get_camera_2d();
if (game_camera) {
// Ideally we should be using Camera2D::get_camera_transform() but it's not so this hack will have to do for now.
view_2d_offset = game_camera->get_camera_screen_center() - (0.5 * root->get_visible_rect().size);
} else {
view_2d_offset = Vector2();
}
view_2d_zoom = 1;
if (root->is_camera_2d_override_enabled()) {
_update_view_2d();
}
}
void RuntimeNodeSelect::_update_view_2d() {
Transform2D transform = Transform2D();
transform.scale_basis(Size2(view_2d_zoom, view_2d_zoom));
transform.columns[2] = -view_2d_offset * view_2d_zoom;
Window *root = SceneTree::get_singleton()->get_root();
ERR_FAIL_COND(!root->is_camera_2d_override_enabled());
SceneTree::get_singleton()->get_root()->set_canvas_transform_override(transform);
Camera2D *override_camera = root->get_override_camera_2d();
override_camera->set_anchor_mode(Camera2D::ANCHOR_MODE_FIXED_TOP_LEFT);
override_camera->set_zoom(Vector2(view_2d_zoom, view_2d_zoom));
override_camera->set_position(view_2d_offset);
_queue_selection_update();
}
@@ -2496,11 +2491,6 @@ void RuntimeNodeSelect::_find_3d_items_at_pos(const Point2 &p_pos, Vector<Select
Window *root = SceneTree::get_singleton()->get_root();
Vector3 ray, pos, to;
if (root->is_camera_3d_override_enabled()) {
ray = root->camera_3d_override_project_ray_normal(p_pos);
pos = root->camera_3d_override_project_ray_origin(p_pos);
to = pos + ray * root->get_camera_3d_override_properties()["z_far"];
} else {
Camera3D *camera = root->get_camera_3d();
if (!camera) {
return;
@@ -2509,7 +2499,6 @@ void RuntimeNodeSelect::_find_3d_items_at_pos(const Point2 &p_pos, Vector<Select
ray = camera->project_ray_normal(p_pos);
pos = camera->project_ray_origin(p_pos);
to = pos + ray * camera->get_far();
}
#ifndef PHYSICS_3D_DISABLED
// Start with physical objects.
@@ -2585,30 +2574,12 @@ void RuntimeNodeSelect::_find_3d_items_at_rect(const Rect2 &p_rect, Vector<Selec
return;
}
bool cam_override = root->is_camera_3d_override_enabled();
Vector3 cam_pos;
Vector3 dist_pos;
if (cam_override) {
cam_pos = root->get_camera_3d_override_transform().origin;
dist_pos = root->camera_3d_override_project_ray_origin(p_rect.position + p_rect.size / 2);
} else {
cam_pos = camera->get_global_position();
dist_pos = camera->project_ray_origin(p_rect.position + p_rect.size / 2);
}
Vector3 cam_pos = camera->get_global_position();
Vector3 dist_pos = camera->project_ray_origin(p_rect.position + p_rect.size / 2);
real_t znear, zfar = 0;
real_t zofs = 5.0;
if (cam_override) {
HashMap<StringName, real_t> override_props = root->get_camera_3d_override_properties();
znear = override_props["z_near"];
zfar = override_props["z_far"];
zofs -= znear;
} else {
znear = camera->get_near();
zfar = camera->get_far();
zofs -= znear;
}
zofs = MAX(0.0, zofs);
real_t znear = camera->get_near();
real_t zfar = camera->get_far();
real_t zofs = MAX(0.0, 5.0 - znear);
const Point2 pos_end = p_rect.position + p_rect.size;
Vector3 box[4] = {
@@ -2637,13 +2608,9 @@ void RuntimeNodeSelect::_find_3d_items_at_rect(const Rect2 &p_rect, Vector<Selec
frustum.push_back(Plane(a, b, cam_pos));
}
Plane near_plane;
// Get the camera normal.
if (cam_override) {
near_plane = Plane(root->get_camera_3d_override_transform().basis.get_column(2), cam_pos);
} else {
near_plane = Plane(camera->get_global_transform().basis.get_column(2), cam_pos);
}
Plane near_plane = Plane(camera->get_global_transform().basis.get_column(2), cam_pos);
near_plane.d -= znear;
frustum.push_back(near_plane);
@@ -2728,27 +2695,10 @@ Vector3 RuntimeNodeSelect::_get_screen_to_space(const Vector3 &p_vector3) {
Window *root = SceneTree::get_singleton()->get_root();
Camera3D *camera = root->get_camera_3d();
Transform3D camera_transform = camera->get_camera_transform();
Size2 size = root->get_size();
real_t znear = 0;
Projection cm;
Transform3D camera_transform;
if (root->is_camera_3d_override_enabled()) {
HashMap<StringName, real_t> override_props = root->get_camera_3d_override_properties();
znear = override_props["z_near"];
cm.set_perspective(override_props["fov"], size.aspect(), znear + p_vector3.z, override_props["z_far"]);
camera_transform.translate_local(cursor.pos);
camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot);
camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
camera_transform.translate_local(0, 0, cursor.distance);
} else {
znear = camera->get_near();
cm.set_perspective(camera->get_fov(), size.aspect(), znear + p_vector3.z, camera->get_far());
camera_transform = camera->get_camera_transform();
}
real_t znear = camera->get_near();
Projection cm = Projection::create_perspective(camera->get_fov(), size.aspect(), znear + p_vector3.z, camera->get_far());
Vector2 screen_he = cm.get_viewport_half_extents();
return camera_transform.xform(Vector3(((p_vector3.x / size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (p_vector3.y / size.height)) * 2.0 - 1.0) * screen_he.y, -(znear + p_vector3.z)));
}
@@ -2808,20 +2758,23 @@ bool RuntimeNodeSelect::_handle_3d_input(const Ref<InputEvent> &p_event) {
} else if (k->is_ctrl_pressed()) {
switch (k->get_physical_keycode()) {
case Key::EQUAL: {
ERR_FAIL_COND_V(!SceneTree::get_singleton()->get_root()->is_camera_3d_override_enabled(), false);
cursor.fov_scale = CLAMP(cursor.fov_scale - 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
SceneTree::get_singleton()->get_root()->get_override_camera_3d()->set_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
return true;
} break;
case Key::MINUS: {
ERR_FAIL_COND_V(!SceneTree::get_singleton()->get_root()->is_camera_3d_override_enabled(), false);
cursor.fov_scale = CLAMP(cursor.fov_scale + 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
SceneTree::get_singleton()->get_root()->get_override_camera_3d()->set_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
return true;
} break;
case Key::KEY_0: {
ERR_FAIL_COND_V(!SceneTree::get_singleton()->get_root()->is_camera_3d_override_enabled(), false);
cursor.fov_scale = 1;
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov, camera_znear, camera_zfar);
SceneTree::get_singleton()->get_root()->get_override_camera_3d()->set_perspective(camera_fov, camera_znear, camera_zfar);
return true;
} break;
@@ -2861,11 +2814,12 @@ void RuntimeNodeSelect::_set_camera_freelook_enabled(bool p_enabled) {
}
void RuntimeNodeSelect::_cursor_scale_distance(real_t p_scale) {
ERR_FAIL_COND(!SceneTree::get_singleton()->get_root()->is_camera_3d_override_enabled());
real_t min_distance = MAX(camera_znear * 4, VIEW_3D_MIN_ZOOM);
real_t max_distance = MIN(camera_zfar / 4, VIEW_3D_MAX_ZOOM);
cursor.distance = CLAMP(cursor.distance * p_scale, min_distance, max_distance);
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
SceneTree::get_singleton()->get_root()->get_override_camera_3d()->set_transform(_get_cursor_transform());
}
void RuntimeNodeSelect::_scale_freelook_speed(real_t p_scale) {
@@ -2880,6 +2834,8 @@ void RuntimeNodeSelect::_scale_freelook_speed(real_t p_scale) {
void RuntimeNodeSelect::_cursor_look(Ref<InputEventWithModifiers> p_event) {
Window *root = SceneTree::get_singleton()->get_root();
ERR_FAIL_COND(!root->is_camera_3d_override_enabled());
const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(), root->get_size()));
const Transform3D prev_camera_transform = _get_cursor_transform();
@@ -2900,11 +2856,13 @@ void RuntimeNodeSelect::_cursor_look(Ref<InputEventWithModifiers> p_event) {
Vector3 diff = prev_pos - pos;
cursor.pos += diff;
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
root->get_override_camera_3d()->set_transform(_get_cursor_transform());
}
void RuntimeNodeSelect::_cursor_pan(Ref<InputEventWithModifiers> p_event) {
Window *root = SceneTree::get_singleton()->get_root();
ERR_FAIL_COND(!root->is_camera_3d_override_enabled());
// Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen.
const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
const real_t pan_speed = translation_sensitivity / 150.0;
@@ -2919,11 +2877,13 @@ void RuntimeNodeSelect::_cursor_pan(Ref<InputEventWithModifiers> p_event) {
camera_transform.translate_local(translation);
cursor.pos = camera_transform.origin;
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
root->get_override_camera_3d()->set_transform(_get_cursor_transform());
}
void RuntimeNodeSelect::_cursor_orbit(Ref<InputEventWithModifiers> p_event) {
Window *root = SceneTree::get_singleton()->get_root();
ERR_FAIL_COND(!root->is_camera_3d_override_enabled());
// Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen.
const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
@@ -2941,7 +2901,7 @@ void RuntimeNodeSelect::_cursor_orbit(Ref<InputEventWithModifiers> p_event) {
cursor.y_rot += relative.x * orbit_sensitivity;
}
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
root->get_override_camera_3d()->set_transform(_get_cursor_transform());
}
Point2 RuntimeNodeSelect::_get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_event, Rect2 p_area) const {
@@ -2969,22 +2929,25 @@ void RuntimeNodeSelect::_reset_camera_3d() {
cursor = Cursor();
Window *root = SceneTree::get_singleton()->get_root();
Camera3D *camera = root->get_camera_3d();
if (camera) {
Transform3D transform = camera->get_global_transform();
Camera3D *game_camera = root->is_camera_3d_override_enabled() ? root->get_overridden_camera_3d() : root->get_camera_3d();
if (game_camera) {
Transform3D transform = game_camera->get_camera_transform();
transform.translate_local(0, 0, -cursor.distance);
cursor.pos = transform.origin;
cursor.x_rot = -camera->get_global_rotation().x;
cursor.y_rot = -camera->get_global_rotation().y;
cursor.x_rot = -game_camera->get_global_rotation().x;
cursor.y_rot = -game_camera->get_global_rotation().y;
cursor.fov_scale = CLAMP(camera->get_fov() / camera_fov, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
cursor.fov_scale = CLAMP(game_camera->get_fov() / camera_fov, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
} else {
cursor.fov_scale = 1.0;
}
SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
if (root->is_camera_3d_override_enabled()) {
Camera3D *override_camera = root->get_override_camera_3d();
override_camera->set_transform(_get_cursor_transform());
override_camera->set_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
}
}
#endif // _3D_DISABLED

View File

@@ -1189,50 +1189,41 @@ void Viewport::canvas_parent_mark_dirty(Node *p_node) {
}
}
void Viewport::enable_canvas_transform_override(bool p_enable) {
#if DEBUG_ENABLED
void Viewport::enable_camera_2d_override(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
if (override_canvas_transform == p_enable) {
return;
}
override_canvas_transform = p_enable;
if (p_enable) {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
camera_2d_override.enable(this, camera_2d);
} else {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
camera_2d_override.disable(camera_2d);
}
}
bool Viewport::is_canvas_transform_override_enabled() const {
bool Viewport::is_camera_2d_override_enabled() const {
ERR_READ_THREAD_GUARD_V(false);
return override_canvas_transform;
return camera_2d_override.is_enabled();
}
void Viewport::set_canvas_transform_override(const Transform2D &p_transform) {
ERR_MAIN_THREAD_GUARD;
if (canvas_transform_override == p_transform) {
return;
Camera2D *Viewport::get_overridden_camera_2d() const {
ERR_READ_THREAD_GUARD_V(nullptr);
ERR_FAIL_COND_V(!camera_2d_override.is_enabled(), nullptr);
return camera_2d_override.get_overridden_camera();
}
canvas_transform_override = p_transform;
if (override_canvas_transform) {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
}
}
Transform2D Viewport::get_canvas_transform_override() const {
ERR_READ_THREAD_GUARD_V(Transform2D());
return canvas_transform_override;
Camera2D *Viewport::get_override_camera_2d() const {
ERR_READ_THREAD_GUARD_V(nullptr);
ERR_FAIL_COND_V(!camera_2d_override.is_enabled(), nullptr);
return camera_2d_override.is_enabled() ? get_camera_2d() : nullptr;
}
#endif // DEBUG_ENABLED
void Viewport::set_canvas_transform(const Transform2D &p_transform) {
ERR_MAIN_THREAD_GUARD;
canvas_transform = p_transform;
if (!override_canvas_transform) {
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
}
}
Transform2D Viewport::get_canvas_transform() const {
ERR_READ_THREAD_GUARD_V(Transform2D());
@@ -4220,6 +4211,13 @@ void Viewport::_audio_listener_2d_remove(AudioListener2D *p_audio_listener) {
}
void Viewport::_camera_2d_set(Camera2D *p_camera_2d) {
#if DEBUG_ENABLED
if (is_camera_2d_override_enabled()) {
camera_2d_override.set_overridden_camera(p_camera_2d);
return;
}
#endif // DEBUG_ENABLED
camera_2d = p_camera_2d;
}
@@ -4454,19 +4452,24 @@ void Viewport::_camera_3d_set(Camera3D *p_camera) {
return;
}
#if DEBUG_ENABLED
if (is_camera_3d_override_enabled()) {
camera_3d_override.set_overridden_camera(p_camera);
return;
}
#endif // DEBUG_ENABLED
if (camera_3d) {
camera_3d->notification(Camera3D::NOTIFICATION_LOST_CURRENT);
}
camera_3d = p_camera;
if (!camera_3d_override) {
if (camera_3d) {
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
} else {
RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
}
}
if (camera_3d) {
camera_3d->notification(Camera3D::NOTIFICATION_BECAME_CURRENT);
@@ -4504,77 +4507,34 @@ void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) {
}
}
#if DEBUG_ENABLED
void Viewport::enable_camera_3d_override(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
if (p_enable == camera_3d_override) {
return;
}
if (p_enable) {
camera_3d_override.rid = RenderingServer::get_singleton()->camera_create();
camera_3d_override.enable(this, camera_3d);
} else {
RenderingServer::get_singleton()->free(camera_3d_override.rid);
camera_3d_override.rid = RID();
}
if (p_enable) {
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d_override.rid);
} else if (camera_3d) {
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
} else {
RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
camera_3d_override.disable(camera_3d);
}
}
void Viewport::set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
ERR_MAIN_THREAD_GUARD;
if (camera_3d_override) {
if (camera_3d_override.fov == p_fovy_degrees && camera_3d_override.z_near == p_z_near &&
camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_PERSPECTIVE) {
return;
bool Viewport::is_camera_3d_override_enabled() const {
ERR_READ_THREAD_GUARD_V(false);
return camera_3d_override.is_enabled();
}
camera_3d_override.fov = p_fovy_degrees;
camera_3d_override.z_near = p_z_near;
camera_3d_override.z_far = p_z_far;
camera_3d_override.projection = Camera3DOverrideData::PROJECTION_PERSPECTIVE;
RenderingServer::get_singleton()->camera_set_perspective(camera_3d_override.rid, camera_3d_override.fov, camera_3d_override.z_near, camera_3d_override.z_far);
}
Camera3D *Viewport::get_overridden_camera_3d() const {
ERR_READ_THREAD_GUARD_V(nullptr);
ERR_FAIL_COND_V(!camera_3d_override.is_enabled(), nullptr);
return camera_3d_override.get_overridden_camera();
}
void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
ERR_MAIN_THREAD_GUARD;
if (camera_3d_override) {
if (camera_3d_override.size == p_size && camera_3d_override.z_near == p_z_near &&
camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
return;
}
camera_3d_override.size = p_size;
camera_3d_override.z_near = p_z_near;
camera_3d_override.z_far = p_z_far;
camera_3d_override.projection = Camera3DOverrideData::PROJECTION_ORTHOGONAL;
RenderingServer::get_singleton()->camera_set_orthogonal(camera_3d_override.rid, camera_3d_override.size, camera_3d_override.z_near, camera_3d_override.z_far);
}
}
HashMap<StringName, real_t> Viewport::get_camera_3d_override_properties() const {
HashMap<StringName, real_t> props;
props["size"] = 0;
props["fov"] = 0;
props["z_near"] = 0;
props["z_far"] = 0;
ERR_READ_THREAD_GUARD_V(props);
props["size"] = camera_3d_override.size;
props["fov"] = camera_3d_override.fov;
props["z_near"] = camera_3d_override.z_near;
props["z_far"] = camera_3d_override.z_far;
return props;
Camera3D *Viewport::get_override_camera_3d() const {
ERR_READ_THREAD_GUARD_V(nullptr);
ERR_FAIL_COND_V(!camera_3d_override.is_enabled(), nullptr);
return get_camera_3d();
}
#endif //DEBUG_ENABLED
void Viewport::set_disable_3d(bool p_disable) {
ERR_MAIN_THREAD_GUARD;
@@ -4587,76 +4547,6 @@ bool Viewport::is_3d_disabled() const {
return disable_3d;
}
bool Viewport::is_camera_3d_override_enabled() const {
ERR_READ_THREAD_GUARD_V(false);
return camera_3d_override;
}
void Viewport::set_camera_3d_override_transform(const Transform3D &p_transform) {
ERR_MAIN_THREAD_GUARD;
if (camera_3d_override) {
camera_3d_override.transform = p_transform;
RenderingServer::get_singleton()->camera_set_transform(camera_3d_override.rid, p_transform);
}
}
Transform3D Viewport::get_camera_3d_override_transform() const {
ERR_READ_THREAD_GUARD_V(Transform3D());
if (camera_3d_override) {
return camera_3d_override.transform;
}
return Transform3D();
}
Vector3 Viewport::camera_3d_override_project_ray_normal(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Vector3 ray = camera_3d_override_project_local_ray_normal(p_pos);
return camera_3d_override.transform.basis.xform(ray).normalized();
}
Vector3 Viewport::camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Size2 viewport_size = get_camera_rect_size();
Vector2 cpos = get_camera_coords(p_pos);
Vector3 ray;
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
ray = Vector3(0, 0, -1);
} else {
Projection cm;
cm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false);
Vector2 screen_he = cm.get_viewport_half_extents();
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -camera_3d_override.z_near).normalized();
}
return ray;
}
Vector3 Viewport::camera_3d_override_project_ray_origin(const Point2 &p_pos) const {
ERR_READ_THREAD_GUARD_V(Vector3());
Size2 viewport_size = get_camera_rect_size();
Vector2 cpos = get_camera_coords(p_pos);
ERR_FAIL_COND_V(viewport_size.y == 0, Vector3());
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
Vector2 pos = cpos / viewport_size;
real_t vsize, hsize;
hsize = camera_3d_override.size * viewport_size.aspect();
vsize = camera_3d_override.size;
Vector3 ray;
ray.x = pos.x * (hsize)-hsize / 2;
ray.y = (1.0 - pos.y) * (vsize)-vsize / 2;
ray.z = -camera_3d_override.z_near;
ray = camera_3d_override.transform.xform(ray);
return ray;
} else {
return camera_3d_override.transform.origin;
};
}
Ref<World3D> Viewport::get_world_3d() const {
ERR_READ_THREAD_GUARD_V(Ref<World3D>());
return world_3d;
@@ -5594,3 +5484,64 @@ void SubViewport::_validate_property(PropertyInfo &p_property) const {
SubViewport::SubViewport() {
RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
}
/////////////////////////////////
#if DEBUG_ENABLED
template <class T>
bool Viewport::CameraOverride<T>::is_enabled() const {
return enabled;
}
template <class T>
void Viewport::CameraOverride<T>::enable(Viewport *p_viewport, const T *p_current_camera) {
if (enabled) {
return;
}
T *override_camera = memnew(T);
override_camera->set_name(vformat("Override%s", T ::get_class_static()));
p_viewport->add_child(override_camera, false, Node::INTERNAL_MODE_BACK);
override_camera->make_current();
set_overridden_camera(p_current_camera);
// Call to make the override camera current must happen before we enable the override to prevent the override mechanism from kicking in.
enabled = true;
}
template <class T>
void Viewport::CameraOverride<T>::disable(T *p_current_camera) {
if (!enabled) {
return;
}
// Call to make the overridden camera current must happen after we disable the override to prevent the override mechanism from kicking in.
enabled = false;
T *overridden_camera = get_overridden_camera();
if (overridden_camera) {
overridden_camera->make_current();
} else {
p_current_camera->clear_current();
}
p_current_camera->queue_free();
overridden_camera_id = ObjectID();
}
template <class T>
void Viewport::CameraOverride<T>::set_overridden_camera(const T *p_camera) {
overridden_camera_id = p_camera ? p_camera->get_instance_id() : ObjectID();
}
template <class T>
T *Viewport::CameraOverride<T>::get_overridden_camera() const {
return ObjectDB::get_instance<T>(overridden_camera_id);
}
// Explicit template instantiation to allow template definitions inside cpp file
// and prevent instantiation using other than the desired camera types.
template class Viewport::CameraOverride<Camera2D>;
template class Viewport::CameraOverride<Camera3D>;
#endif // DEBUG_ENABLED

View File

@@ -248,9 +248,6 @@ private:
RID current_canvas;
RID subwindow_canvas;
bool override_canvas_transform = false;
Transform2D canvas_transform_override;
Transform2D canvas_transform;
Transform2D global_canvas_transform;
Transform2D stretch_transform;
@@ -533,11 +530,12 @@ public:
Ref<World2D> get_world_2d() const;
Ref<World2D> find_world_2d() const;
void enable_canvas_transform_override(bool p_enable);
bool is_canvas_transform_override_enabled() const;
void set_canvas_transform_override(const Transform2D &p_transform);
Transform2D get_canvas_transform_override() const;
#if DEBUG_ENABLED
void enable_camera_2d_override(bool p_enable);
bool is_camera_2d_override_enabled() const;
Camera2D *get_overridden_camera_2d() const;
Camera2D *get_override_camera_2d() const;
#endif // DEBUG_ENABLED
void set_canvas_transform(const Transform2D &p_transform);
Transform2D get_canvas_transform() const;
@@ -737,6 +735,23 @@ public:
virtual bool is_sub_viewport() const { return false; }
private:
#if DEBUG_ENABLED
template <class T>
class CameraOverride {
private:
bool enabled = false;
ObjectID overridden_camera_id;
public:
bool is_enabled() const;
void enable(Viewport *p_viewport, const T *p_current_camera);
void disable(T *p_current_camera);
void set_overridden_camera(const T *p_camera);
T *get_overridden_camera() const;
};
#endif // DEBUG_ENABLED
// 2D audio, camera, and physics. (don't put World2D here because World2D is needed for Control nodes).
friend class AudioListener2D; // Needs _audio_listener_2d_set and _audio_listener_2d_remove
AudioListener2D *audio_listener_2d = nullptr;
@@ -747,6 +762,9 @@ private:
friend class Camera2D; // Needs _camera_2d_set
Camera2D *camera_2d = nullptr;
#if DEBUG_ENABLED
CameraOverride<Camera2D> camera_2d_override;
#endif // DEBUG_ENABLED
void _camera_2d_set(Camera2D *p_camera_2d);
#ifndef PHYSICS_2D_DISABLED
@@ -786,26 +804,11 @@ private:
void _collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape);
#endif // PHYSICS_3D_DISABLED
struct Camera3DOverrideData {
Transform3D transform;
enum Projection {
PROJECTION_PERSPECTIVE,
PROJECTION_ORTHOGONAL
};
Projection projection = Projection::PROJECTION_PERSPECTIVE;
real_t fov = 0.0;
real_t size = 0.0;
real_t z_near = 0.0;
real_t z_far = 0.0;
RID rid;
operator bool() const {
return rid != RID();
}
} camera_3d_override;
friend class Camera3D;
Camera3D *camera_3d = nullptr;
#if DEBUG_ENABLED
CameraOverride<Camera3D> camera_3d_override;
#endif // DEBUG_ENABLED
HashSet<Camera3D *> camera_3d_set;
void _camera_3d_transform_changed_notify();
void _camera_3d_set(Camera3D *p_camera);
@@ -825,19 +828,13 @@ public:
bool is_audio_listener_3d() const;
Camera3D *get_camera_3d() const;
#if DEBUG_ENABLED
void enable_camera_3d_override(bool p_enable);
bool is_camera_3d_override_enabled() const;
void set_camera_3d_override_transform(const Transform3D &p_transform);
Transform3D get_camera_3d_override_transform() const;
void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
HashMap<StringName, real_t> get_camera_3d_override_properties() const;
Vector3 camera_3d_override_project_ray_normal(const Point2 &p_pos) const;
Vector3 camera_3d_override_project_ray_origin(const Point2 &p_pos) const;
Vector3 camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const;
Camera3D *get_overridden_camera_3d() const;
Camera3D *get_override_camera_3d() const;
#endif // DEBUG_ENABLED
void set_disable_3d(bool p_disable);
bool is_3d_disabled() const;