From 47e63bc55f611793c4b1f7ca878d9b8a02aa492e Mon Sep 17 00:00:00 2001 From: Bauke Conijn Date: Tue, 20 Jun 2023 20:44:50 +0200 Subject: [PATCH] Fix Camera3D project_* methods not accounting for frustum offset This does not fix Camera3D::project_ray_normal(). Adds Camera3D::get_camera_projection() and exposes it to GDScript --- doc/classes/Camera3D.xml | 6 ++++ scene/3d/camera_3d.cpp | 68 +++++++++++++++++++--------------------- scene/3d/camera_3d.h | 3 ++ 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index b0406c77a108..e0113cb9c0fa 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -17,6 +17,12 @@ If this is the current camera, remove it from being current. If [param enable_next] is [code]true[/code], request to make the next camera current, if any. + + + + Returns the projection matrix that this camera uses to render to its associated viewport. The camera must be part of the scene tree to function. + + diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 5a4761892d15..225b9b35b3fb 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -170,6 +170,30 @@ Transform3D Camera3D::get_camera_transform() const { return tr; } +Projection Camera3D::_get_camera_projection(real_t p_near) const { + Size2 viewport_size = get_viewport()->get_visible_rect().size; + Projection cm; + + switch (mode) { + case PROJECTION_PERSPECTIVE: { + cm.set_perspective(fov, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH); + } break; + case PROJECTION_ORTHOGONAL: { + cm.set_orthogonal(size, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH); + } break; + case PROJECTION_FRUSTUM: { + cm.set_frustum(size, viewport_size.aspect(), frustum_offset, p_near, far); + } break; + } + + return cm; +} + +Projection Camera3D::get_camera_projection() const { + ERR_FAIL_COND_V_MSG(!is_inside_tree(), Projection(), "Camera is not inside the scene tree."); + return _get_camera_projection(near); +} + void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) { if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) { return; @@ -286,8 +310,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { if (mode == PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); } else { - Projection cm; - cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); + Projection cm = _get_camera_projection(near); 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, -near).normalized(); } @@ -302,9 +325,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { Vector2 cpos = get_viewport()->get_camera_coords(p_pos); ERR_FAIL_COND_V(viewport_size.y == 0, Vector3()); - if (mode == PROJECTION_PERSPECTIVE) { - return get_camera_transform().origin; - } else { + if (mode == PROJECTION_ORTHOGONAL) { Vector2 pos = cpos / viewport_size; real_t vsize, hsize; if (keep_aspect == KEEP_WIDTH) { @@ -321,6 +342,8 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { ray.z = -near; ray = get_camera_transform().xform(ray); return ray; + } else { + return get_camera_transform().origin; }; }; @@ -333,15 +356,7 @@ bool Camera3D::is_position_behind(const Vector3 &p_pos) const { Vector Camera3D::get_near_plane_points() const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector(), "Camera is not inside scene."); - Size2 viewport_size = get_viewport()->get_visible_rect().size; - - Projection cm; - - if (mode == PROJECTION_ORTHOGONAL) { - cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } else { - cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } + Projection cm = _get_camera_projection(near); Vector3 endpoints[8]; cm.get_endpoints(Transform3D(), endpoints); @@ -361,13 +376,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm; - - if (mode == PROJECTION_ORTHOGONAL) { - cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } else { - cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } + Projection cm = _get_camera_projection(near); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -389,13 +398,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm; - - if (mode == PROJECTION_ORTHOGONAL) { - cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH); - } else { - cm.set_perspective(fov, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH); - } + Projection cm = _get_camera_projection(p_z_depth); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -508,6 +511,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current); ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform); + ClassDB::bind_method(D_METHOD("get_camera_projection"), &Camera3D::get_camera_projection); ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov); ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size); @@ -653,13 +657,7 @@ bool Camera3D::get_cull_mask_value(int p_layer_number) const { Vector Camera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector()); - Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm; - if (mode == PROJECTION_PERSPECTIVE) { - cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } else { - cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); - } + Projection cm = _get_camera_projection(near); return cm.get_projection_planes(get_camera_transform()); } diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 420d0cb93906..aa302ded4a3b 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -105,6 +105,8 @@ class Camera3D : public Node3D { static void _bind_methods(); + Projection _get_camera_projection(real_t p_near) const; + public: enum { NOTIFICATION_BECAME_CURRENT = 50, @@ -138,6 +140,7 @@ class Camera3D : public Node3D { void set_frustum_offset(Vector2 p_offset); virtual Transform3D get_camera_transform() const; + virtual Projection get_camera_projection() const; virtual Vector3 project_ray_normal(const Point2 &p_pos) const; virtual Vector3 project_ray_origin(const Point2 &p_pos) const;