From 69f84563a33af5f0930cc97c1a4978fa6211b5aa Mon Sep 17 00:00:00 2001 From: Yaohua Xiong Date: Sun, 27 Nov 2022 10:23:27 +0800 Subject: [PATCH] Fix Curve3D::get_closest_point() broken The problem is caused by calling adaptive tessellation baking function by mistake, which produce too few points for straight lines. Calling the even length tessellation fix the problem. The code for `get_closest_point()` and `get_closest_offset()` are also updated. They used to assume bake interval to be exact, which is no longer true. The out dated document for `get_closest_point()` is also updated. --- doc/classes/Curve3D.xml | 2 +- scene/resources/curve.cpp | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index 67e4e45e520..851996131fc 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -64,7 +64,7 @@ - Returns the closest baked point (in curve's local space) to [param to_point]. + Returns the closest point on baked segments (in curve's local space) to [param to_point]. [param to_point] must be in this curve's local space. diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 9289c5da4af..93c3d4f8515 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -1407,15 +1407,15 @@ void Curve3D::_bake_segment3d_even_length(RBMap &r_bake, real_t Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); - size_t length = beg.distance_to(end); + real_t length = beg.distance_to(end); if (length > p_length && p_depth < p_max_depth) { real_t mp = (p_begin + p_end) * 0.5; Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); r_bake[mp] = mid; - _bake_segment3d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); - _bake_segment3d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment3d_even_length(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment3d_even_length(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); } } @@ -1839,10 +1839,11 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { real_t nearest_dist = -1.0f; for (int i = 0; i < pc - 1; i++) { + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; Vector3 origin = r[i]; - Vector3 direction = (r[i + 1] - origin) / bake_interval; + Vector3 direction = (r[i + 1] - origin) / interval; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector3 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1875,13 +1876,16 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { real_t nearest = 0.0f; real_t nearest_dist = -1.0f; - real_t offset = 0.0f; + real_t offset; for (int i = 0; i < pc - 1; i++) { - Vector3 origin = r[i]; - Vector3 direction = (r[i + 1] - origin) / bake_interval; + offset = baked_dist_cache[i]; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; + Vector3 origin = r[i]; + Vector3 direction = (r[i + 1] - origin) / interval; + + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector3 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1890,8 +1894,6 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { nearest = offset + d; nearest_dist = dist; } - - offset += bake_interval; } return nearest;