-
-
Notifications
You must be signed in to change notification settings - Fork 232
Optimise get_intersection() #712
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
base: main
Are you sure you want to change the base?
Optimise get_intersection() #712
Conversation
TokisanGames
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the start on this. I edited editor_plugin.gd and changed the get_intersection line to not use GPU mode for testing and it works well in most cases.
-
If the camera is not above a region, it won't work. I don't know if this worked before, but it's something we should account for. I don't see any early exit for is_nan in the old code.
-
I made a mistake in assumptions. Plane assumes 4 vertices (ABCD) on the same plane, and that the two triangles forming the plane (ABC and ACD) have the same normal. But that is not correct. Each vertex has its own height, so each triangle has its own normal. So, we need to do a ray/triangle intersection instead, and we need the height of the third vertex.
I had a conversation with grok about it who suggested a different algorithm, Möller-Trumbore ray-triangle intersection. Peruse it here.
src/terrain_3d.cpp
Outdated
| Vector3 u_bound = point; | ||
| Vector3 l_bound = last_point; | ||
| Vector3 u_terrain = Vector3(point.x, _data->get_height(point), point.z); | ||
| Vector3 l_terrain = Vector3(last_point.x, _data->get_height(last_point), last_point.z); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like you to spell out the variables and comment what these are for. upper_bound is more clear than u_bound. u_terrain is unclear.
upper/lower_bound are within the cast ray, u/l_terrain are the vertices of the face the ray has hit. This isn't clear from the names, so needs to be explained in comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added explanation and renamed these variables.
upper/lower_ray_bound
upper/lower_triangle_point
Added a new variable to make clear where we create the third point of the triangle.
|
Thanks for reviewing, I'll work through the restructuring and naming and update soon. You are right about the early return on NaN. I didn't consider the ray might start outside a region, now I think about it we may want to cross an empty region too, islands for example. Grok really wants us to snap y! glad it saw sense in the end. I think you were right first time and planes work... there is a constructor to create a plane from 3 points. The normal is determined by the winding order. There are no triangles in a plane, it's infinite and only has a normal and distance from the origin. I am not using the vertices of the terrain grid, at the secondary stage we are using interpolated heights from the bounds which could easily be between vertices. Still maybe triangles are faster or something so happy to experiment. |
7dfd538 to
7804e51
Compare
I read your conversation and experimented with the ray-triangle intersection process Grok suggested. Used as shown, it takes ~40-100 us (depending on the distance) to find a valid or approximated intersection since it takes four cheap height checks and one expensive one per secondary step rather than a single expensive one. It also has to check two triangles rather than one plane. It also suffers when the intersection point is close to the vertex. There is no difference in the primary step section, so a fail takes the same length of time as the version I submitted. When refining it is exceeding max steps more often than not (~25% results within 0.001 tolerance). I then swapped out the plane intersection method in my version for the triangle intersection method and there was practically no speed difference (17-28 us within a similar distance range). Typically it takes max 3 steps to get a result within tolerance. On balance, using Godots built in plane methods seems like the better option to me. |
|
I wonder whether it's worth including a physics raycast before attempting raymarching. If we get a hit on the terrain we're done, if not proceed to ray marching. For mouse picking, this is slower for shorter results but stable and much faster for longer distance checks. Maybe not worthwhile, since you'll probably often be painting at relatively short distances. Though I think a physics based check could be useful for other issues, such as foliage instancing on collision objects. Like this: |
Compute using plane intersection Stable speed over distance, low error (0.001m in 2 steps) Compute intersection based on triangles Fixup
7804e51 to
8d17f60
Compare
8d17f60 to
4a6a2f6
Compare

This PR addresses issue #690
Run up to 800 primary steps at a step size of 5 to find the upper and lower bounds.
Run up to 5 secondary steps (in practice less than 4 are usually required) to find an intersection point with a height difference of less than 0.001
The secondary steps uses a plane intersection calculation to contract the U and L bounds accordingly.
At short ranges, we get a ~2x improvement. To detect no intersection (worst case) is ~10x faster.
Typically it returns a result between 0.01 and 0.001m from the result from a physics ray cast.