godot: Curve3D get_closest_point broken
Godot version
4.0.dev commit f9fa182abc5209671cb4fbadc2dc173157d6f939
System information
windows 7 and windows 11
Issue description
The Curve3D.get_closest_point function now returns weirdly discrete answers, instead of the closest points.
Steps to reproduce
Make a node with a script and put this in the ready function.
func _ready():
var frog : Curve3D = Curve3D.new()
frog.add_point(Vector3(0.0, 0.0, 0.0), Vector3(0.0, -10.0, 0.0), Vector3(0.0, 10.0, 0.0))
frog.add_point(Vector3(0.0, 100.0, 0.0), Vector3(0.0, -10.0, 0.0), Vector3(0.0, 10.0, 0.0))
var pos = Vector3(0.2, 0.0, 0.0)
for y in range(0.0, 100.0):
pos.y = y
var found = frog.get_closest_point(pos)
print("at ", y, " => ", found)
The curve is just a straight line so it should return points with a Y value matching the Y value of the query. Instead what you get is:
at 0 => (0, 0, 0)
at 1 => (0, 50, 0)
at 2 => (0, 50, 0)
at 3 => (0, 50, 0)
at 4 => (0, 50, 0)
at 5 => (0, 50, 0)
.
.
.
at 74 => (0, 50, 0)
at 75 => (0, 50, 0)
at 76 => (0, 100, 0)
at 77 => (0, 100, 0)
.
.
.
I’m 99% sure the commit f9fa182abc5209671cb4fbadc2dc173157d6f939 introduced this one.
Minimal reproduction project
N/A
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 16 (8 by maintainers)
Commits related to this issue
- Fix #69220 Call wrong version when doing recursive baking — committed to xiongyaohua/godot by xiongyaohua 2 years ago
- Fix #69220 Curve3D::get_closest_point() broken The problem is calling adaptive tessellation by mistake, which produce too few points. Calling even length tessellation fix the problem. — committed to xiongyaohua/godot by xiongyaohua 2 years ago
- Fix #69220 Curve3D::get_closest_point() broken The problem is calling adaptive tessellation by mistake, which produce too few points. Calling even length tessellation fix the problem. — committed to xiongyaohua/godot by xiongyaohua 2 years ago
- Fix #69220 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 len... — committed to xiongyaohua/godot by xiongyaohua 2 years ago
Found some time, and fix the problem(again). Hope I get it right this time.
Turns out the original
get_closest_*
assumes the distance between the baked points are always the same, which is no longer true.After this fix, the test code
produces this result
According to the docs
Curve3D.get_closest_point
is supposed to return the closest baked point. If that’s indeed the intended behavior then the “weirdly discrete” nature of the results is correct (as the baked points are discrete). However, even in such case this:seems to be a bug as (0, 49.67346, 0) is indeed closer to (0.2, 49.7, 0) than (0, 49.78468, 0) is. Approximately:
|(0.2, 49.7, 0) - (0, 49.67346, 0)| = 0.201753 |(0.2, 49.7, 0) - (0, 49.78468, 0)| = 0.217188
Yes, the old doc was misleading. These methods actually find point on baked polyline. So I updated the doc in my PR.
Not yet. I think the backporting needs to wait for now, because the Curve refactoring is still in progress. After the release of 4.0 I’ll look into backport if there is enough demand.
That is exactly why I started the whole refactoring effort. Now there is a method called
sample_backed_with_rotation()
on both Curve2D and Curve3D. Give it an offset alone the curve, it returns the local transform frame at that point, including position, tangent vector, up vector, side vector. See https://github.com/godotengine/godot/pull/64212@Skwint @CousCous4 Actually after rethinking I guess it’s probably
get_closest_point()
's docs which are simply unclear/misleading. I suspect by “the closest baked point” the author meant any point on the baked polyline (polyline composed from the baked points), not only the actual baked points. That’s just a guess but I don’t think it’d actually make any sense forget_closest_point()
to return baked points only, I don’t see how having just the position of the closest baked point could be useful. If anything, one would rather want to get the index of such closest baked point instead as it would allow to obtain neighbor baked points, or in/out control points. Moreover, if it were meant to return baked points then it should rather be calledget_closest_baked_point()
instead.@xiongyaohua So I agree with that. The docs needs to be clarified of course (it’s fine in your PR). Have you been backporting your changes/PRs to 3.x? Cause if not then it would probably be a good idea to potentially just clarify the docs in there as well in a dedicated PR.
For reference it was changed in #63394.
Hi, I think these two problems are caused by my PR which introduce a new baking method. There is an subtile behaviour change. Bake interval “0.2” no longer produce points exactly “0.2” apart, but more or less “0.2” apart. This change is for simpler code and better baking performance. I am also quite new to godot code so I don’t realize it create problems for other parts. Sorry.
I think the proper fix is instead of finding the closest baked point, these two
find_closest_*
functions should find the geometrically closest point on the baked line segments. Then the result will be baking method independent(at least for straight lines). I am a bit occupied recently, so I am going to do that on this weekend.Thanks for your appreciation 😄.
That is strange. I suspect it is an old bug in the
get_closest_point
, as the code is not touched by my PR. I will investigate on weekend.I’d hold off until this one is merged and then see, but it doesn’t really hurt. If you do raise it, link this one to make the devs life a bit easier.
Ah good point! The docs aren’t too clear on it, but I think that in 3.x it was the equivalent of the PathFollow
offset
not theunit_offset
(which is now calledprogress
andunit_progress
in 4.x). Given something doesn’t seem right withget_closest_offset()
, do you think it needs its own issue report added?hmmm … well, the code definitely tries to interpolate between the baked points, and I’m pretty sure it used to succeed because I used to get smoother surfaces, but fair enough. I think I’m going to end up writing my own version instead, but since these curves are followed by path finders (I think?) doesn’t this make them stutter?
Increasing the bake interval from the default 0.2 to 1.0 improves the results a bit, although they still have repeating results at the 0,50,0 point, but it shows something else weird as well - I don’t know if this helps:
The result for y=49.6 is closer to 49.7 than the result found for 49.7. That can’t be right. Infact, I’m pretty sure I should be getting almost exactly equal Y values for both of these.