Curve Support in Geometry Nodes¶
A design task created in March 2021, discussing supporting curves in addition to the existing point, mesh, and volume operations
Background¶
Background Currently, "curve data" in Blender actually refers to two or three different kinds of data:
- Bezier / NURBS / Poly Path The curve spline itself, before any mesh data is generated. However, note that the segments of a curve are also generated data. The control points are the interactive data.
- Surface This is a different object type, but it's quite similar to the other two types above, with a separation between the input data (control points and edges) and the generated surface. In general this design document won't discuss "Surface" data.
- Mesh This data is generated by a combination of the original curve
data and some values, like the bevel size and its resolution.
Currently this is stored as a
DispList
in Blender, but to the user it is basically mesh data.
Because of this ambiguity, the most important thing to figure out is at which level to apply operations, similar to the existing "Apply on Spline" toggle on the curve modifier stack.
- It's possible to imagine a few different "position" domains on a curve: the control points, points on the calculated spline, and points on any generated mesh data.
- In any case, if generated data like the bevel geometry or the smoothed curve is written to, the curve must be converted to a mesh.
A conversion from curve to mesh changes what kind of operations are possible on the input data, so conversion from curve to mesh is an important step in the node tree.
- The conversion is made simpler by the flexibility of geometry sets; a curve object can evaluate to mesh data in a nodes modifier (or theoretically even the other way around).
- If any mesh modifiers are used before the nodes modifier on a curve object, the input geometry to the nodes modifier will only contain a mesh.
Most settings in the curve object data panel in the property editor describe how to evaluate the curve. However, generally the curve is actually evaluated somewhere inside the modifier stack-- just before the first "Generate" modifier that needs a mesh input. Generalizing evaluation to nodes brings up some issues because of the increased flexibility. Shouldn't the settings for the "Curve to Mesh" operation be closer to the operation itself? There are a few options:
- Don't store the settings at all, instead provide them as inputs
whenever they are needed for an operation on the curve. This is
tempting because it feels low-level, but it might require repeating
many of these settings for use in different operations.
- The node can be added with versioning to existing curve objects, or it could be implicit if necessary.
- This the "everything nodes" solution, visible, flexible, potentially low-level.
- The utility of the settings in the property editor curve tab is not clear with this solution.
- Since the options are stored in the curve, they could simply be
controlled by a "Set Curve Info" node in the tree. A "Curve to Mesh"
node then wouldn't have any settings, it would just use the settings
already in the curve.
- The settings in the property editor can be stored in the curve, so they are still useful.
- Harder to control conversion settings in the node tree.
- It may be possible to combine the two options above somehow. For
example, the spline could be implicitly calculated as part of the
curve, but a mesh surface like a beveled curve or a filled 2D curve
could require a node.
- The spline resolution would then be stored in the curve data, like it is currently. However, that does conflict with the resolution data stored per-spline.
- The conversion to mesh using the settings in the property editor could happen implicitly after modifiers if there is any curve data left.
Conclusions¶
- The curve geometry component control points use the existing "Point"
domain instead of a new "Control Point" domain.
- The point domain should be write-able without converting the curve to a mesh.
- Leverage the differences between curves and meshes and actually treat the curve differently than a mesh.
- A "Convert to Mesh" node is needed to access the evaluated data with
attributes.
- Making the evaluated spline available before conversion may be useful, but that mix of evaluated and original data might be a problem.
- It's possible we would want this conversion to be implicit, but since you lose the ability to control how it's done, that may not be idea.
- Curves have two domains, spline and points. The built-in attributes are:
Name | Domain | Type | Notes |
---|---|---|---|
Length | Spline | Float | Read-only |
Shade Smooth | Spline | Boolean | |
Cyclic | Spline | Boolean | |
Radius | Point | Float | |
Tilt | Point | Float | Only user-applied tilt? |
Weight | Point | Float | NURBS only, Only values > 0 |
Position | Point | Vector | Read-only |
Normal | Point | Vector | Read-only, also evaluated data? |
- If there is still curve data left at the end of the modifier evaluation (including nodes modifiers), it will be implicitly converted to a mesh for display, just like how curves already work in Blender.
Node Mockups¶
While there are many opportunities to expand functionality when curves can be handled with nodes, but these mockups focus on exposing existing functionality.
Implementation Details¶
The current curve data structures are confusing and not very suited to be used in this context, and improving them is on the radar of the modeling module anyway. Improved C++ data structures could significantly ease the process of implementing these features.
With these data structures, a few operations would be necessary as a minimum before they are useful at all:
- Conversion from curve DNA structs to these data structures.
- Evaluation of the structures (calculate spline, bevel geometry), with
a
Mesh
result.
Some of the data stored below depends on decisions made in the previous sections.
enum class BezierHandleType {
Free,
Auto,
Vector,
Align,
};
struct ControlPoint {
float3 position;
float radius;
float tilt;
};
struct ControlPointBezier : ControlPoint {
float3 handle_position_a;
float3 handle_position_b;
BezierHandleType handle_type_a;
BezierHandleType handle_type_b;
};
struct ControlPointNURBS : ControlPoint {
float weight;
};
enum class SplineType {
Bezier,
Poly,
NURBS,
};
struct Spline {
SplineType type;
};
struct SplineBezier : Spline {
Vector<ControlPointBezier> control_points;
int32_t flag; /* Cyclic, smooth. */
int32_t resolution_u;
int32_t resolution_v;
};
struct SplineNURBS : Spline {
Vector<ControlPointNURBS> control_points;
int32_t flag; /* Cyclic, smooth. */
int32_t resolution_u;
int32_t resolution_v;
uint8_t order;
};
/* Proposed name to be different from DNA type. */
struct EditCurve {
Vector<Spline *> splines;
AttributeStorage attributes;
int32_t flag; /* 2D. */
/* Attributes. */
CustomData *control_point_data;
CustomData *spline_data;
/* Then maybe whatever caches are necessary, etc. */
Vector<float3> evaluated_spline_cache;
};