Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Camera3D project_* methods not accounting for frustum offset #75806

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/classes/Camera3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</description>
</method>
<method name="get_camera_projection" qualifiers="const">
<return type="Projection" />
<description>
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.
</description>
</method>
<method name="get_camera_rid" qualifiers="const">
<return type="RID" />
<description>
Expand Down
68 changes: 33 additions & 35 deletions scene/3d/camera_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand All @@ -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) {
Expand All @@ -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;
};
};

Expand All @@ -333,15 +356,7 @@ bool Camera3D::is_position_behind(const Vector3 &p_pos) const {
Vector<Vector3> Camera3D::get_near_plane_points() const {
ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector<Vector3>(), "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);
Expand All @@ -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);

Expand All @@ -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();

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -653,13 +657,7 @@ bool Camera3D::get_cull_mask_value(int p_layer_number) const {
Vector<Plane> Camera3D::get_frustum() const {
ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());

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());
}
Expand Down
3 changes: 3 additions & 0 deletions scene/3d/camera_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down