Skip to content

Conversation

@aidandavey
Copy link
Contributor

@aidandavey aidandavey commented Jun 9, 2025

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.

Copy link
Owner

@TokisanGames TokisanGames left a 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.

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);
Copy link
Owner

@TokisanGames TokisanGames Jun 9, 2025

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.

Copy link
Contributor Author

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.

@aidandavey
Copy link
Contributor Author

aidandavey commented Jun 9, 2025

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.

@aidandavey aidandavey force-pushed the Optimise-get_intersection branch 2 times, most recently from 7dfd538 to 7804e51 Compare June 10, 2025 11:58
@aidandavey
Copy link
Contributor Author

aidandavey commented Jun 10, 2025

I had a conversation with grok about it who suggested a different algorithm, Möller-Trumbore ray-triangle intersection. Peruse it here.

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.

@aidandavey
Copy link
Contributor Author

aidandavey commented Jun 15, 2025

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:

image

@TokisanGames TokisanGames self-assigned this Jun 24, 2025
@TokisanGames TokisanGames added the enhancement New feature or request label Jun 27, 2025
@TokisanGames TokisanGames added this to the 1.1 milestone Jun 27, 2025
Compute using plane intersection

Stable speed over distance, low error (0.001m in 2 steps)

Compute intersection based on triangles

Fixup
@aidandavey aidandavey force-pushed the Optimise-get_intersection branch from 7804e51 to 8d17f60 Compare October 23, 2025 11:12
@aidandavey aidandavey force-pushed the Optimise-get_intersection branch from 8d17f60 to 4a6a2f6 Compare November 3, 2025 17:09
@TokisanGames TokisanGames marked this pull request as draft November 10, 2025 00:49
@TokisanGames TokisanGames modified the milestones: 1.1, 1.2 Dec 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants