Skip to content

Add Curve3DMesh primitive mesh class #108861

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

AxelStrem
Copy link

Partially addresses #6151 and #7082

Adds a new Curve3DMesh primitive mesh class, enabling the generation of 3D geometry along a Curve3D path.

  • Multiple mesh profiles: Flat, Cross, Tube.
  • Width control (constant or via a Curve resource).
  • Adaptive tessellation and advanced configuration (orientation, up vector, edge extension, corner handling).

TODO:

  • if Add two bake options to Curve3D #80753 is merged, curve orientation logic can be removed
  • optionally, remove overlap filtering and vertex interleaving logic, which will greatly reduce the code bloat
2025-07-20.17-15-15_2.mp4

@Calinou
Copy link
Member

Calinou commented Jul 22, 2025

I would name the node just CurveMesh, since you can also use meshes in 2D with the MeshInstance2D node. This would better match the naming of other PrimitiveMeshes.

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally, it works as expected. This is a great start 🙂

Testing project: truck_town.zip
Code changes to the demo are limited to vehicle.gd (used by car_base.tscn). Trails are only visible with the base red car.

truck_town_trails.webm

Some feedback:

  • If you want to edit a curve mesh in the editor, you need to create a Path3D node, save its Curve3D resource to disk (using Copy in the resource dropdown won't work), and load this Curve3D resource in the Curve3DMesh. This is a bit cumbersome; I'm not sure if this workflow could be streamlined somehow. Maybe we can provide a "Create Curve Mesh Child" option that does all this in the Path3D editor plugin's menu?
  • To get the use case for the testing project working (car light trails), I had to add at least one point in the editor to the Curve3D resource that's built into the Curve3DMesh. Attempting to create a new resource (or use an existing resource with no points) won't work, even if you are adding new points at runtime that should logically be visible.
    • This also causes an interpolation glitch to be visible unless I clear the points in _ready(), but if I do that, then the trail won't be visible until I start removing points from it (to get rid of the oldest points).
  • Tessellation Mode should be in this order: Disabled, Baked, Adaptive, so that the fastest option is listed first. (Adaptive can get pretty slow with high point counts if you're modifying the curve at runtime, so the documentation should warn about it.)
  • UV mapping looks incorrect on a small portion of the mesh when using the Tube mode. This occurs with all tessellation modes. This is more obvious at high segment counts:
tube_uv_mapping.mp4
  • Lightmapping works on Curve3DMesh, at least in Tube mode. Lightmap size hint is based on the curve length and mesh width. The UVs could probably be improved though (compare lighting with the PlaneMesh besides the curve):
image image

@kaganguru
Copy link

We should mind adding a tangent mode that will bake tangent info to normals. This is especially useful for shaders that regard the curve shape.

@MajorMcDoom
Copy link
Contributor

MajorMcDoom commented Jul 23, 2025

Bringing in some comments from a related proposal to consolidate. :)

I think the majority of use cases for lines in games will be non-stationary. And in the cases where there is "curving", the curvedness comes usually not from a Bézier, but rather from gravity or otherwise derived from the motion of an object. In these cases, modifying a Bézier would introduce unnecessary overhead, especially since most of these cases, you'd be changing every point, every frame.

Furthermore, the use of a nested curve resource makes less sense for dynamically updating lines because in those cases, you would not need to reuse the curve resource - it would be transient state that is unique to the node that is rendering it.

An alternative would be to dispense of the need for a nested Curve3D resource at all, and make a more agnostic LineMesh resource that simply stores line data locally. It would be completely agnostic in terms of how it "derives" this data, and that way it remains performant, light and flexible. If a user wants to use a Curve3D to drive the shape, they can do that easily on their own by setting the points, which a small price to pay for what I believe is ultimately a more niche use case for lines. In the same way, they can use a sine wave, or audio bus data, etc. to drive the points.

An agnostic LineMesh would also create less user expectation for a type of authoring UX. e.g., for a Bézier, you could use a Path3D for authoring and pipe its curve data into the LineMesh. For a laser pointer, you could simply edit the arrays on the mesh. For dangling rope, you would have a tool script that generates the points from authored endpoints, etc.

To aid with that, I think it would make sense to have utility functions. A good example of this is Mesh.get_faces(). It effectively removes the need for a tight coupling between Mesh and ConcavePolygonShape3D by providing a way for the former to fetch data that is consumable by the latter. In our case here, we could for example have a LineMesh.copy_curve() function or something similar, which could even expose parameters to control whether you copy the roll, the sampling frequency, etc. etc.

And finally, this would create a smaller PR that would be easier to test/approve/merge. It opens a lot of doors, and more specific subclasses could always be proposed later if there is a need.

In summary:
WIthin the scope of visual lines in games, Béziers are not a common enough use case to justify making the most abstract form of a line mesh being a CurveMesh driven by a Curve3D. It creates overhead for the much more common use cases that require efficient hotpath execution. Recommend creating a more abstract LineMesh instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants