# Curve/Hair Data Structure ## Goal * Define a data structure that can be used for all kinds of curves. * Decide how to integrate it in Blender. * Decide how to deal with `Text` and `Surface` objects. Also see https://developer.blender.org/T94193. ## Naming * Challenges for finding a good name: * Name requires two levels of plurality: * Levels: * Single Spline * Multiple Splines that form a "curve" * Multiple curves * `Curve` exists already in DNA and in the ui. * Renaming the existing `Curve` struct is probably not worth the effort. * So a different name is needed, at least for the internal data structure. * User level name: * Ideally there would just be a "Curve" object type (including for hair). * The difficulty is that we will probably have two different object types, which both represent curves (at least during a transistion period). * Calling both types "Curve Objects" is confusing, especially if they have different features. * There can still be a hair edit mode that is available on curve objects. * Similar to how there is edit and sculpt mode for meshes. * Possible Names: * Single word names that could refer to a list of splines: * Hair * Curve * Spline * Strand * Grass * Fiber * String * Cord * Maybe it's reasonable to have a two-word-name to solve problem of having two levels of plurality. * Possible suffixes that can be used with the names above. * Group * Set * s (as in `CurveS` or `SplineS`) * Doesn't really solve the plural-problem. * How to name a variable that contains multiple `Curves`? * Should individual curves now be called `Spline` or `Curve`? * Batch * Collection * Array * Bundle * Cluster * Pack * List * Sequence * If the new type is supposed to be called `Curve` later on, maybe it should have `Curve` in the name now already, to avoid introducing new terminology. * `NewCurve` * `Curve` (and [rename](https://developer.blender.org/P2673) existing `Curve` to `LegacyCurve`) * Unclear if it is feasible to repurpose an existing ID struct name like this. * `NCurve` (`N` for `New`) ## Data Structure * Basic structure conceptually: * A curve has an ordered list of splines. * A spline has an ordered list of points. * A spline can have zero, one or more points. * Two main approaches: * Store data for each spline separately or store data for multiple splines together. * Given that we want to support large amounts of hair (many splines), continuous arrays that contain data for multiple splines is probably the only viable option. * General data structure should follow the approach used by `Hair` in `DNA_hair_types.h`. * Attribute Domains: * Point: * Different value for every control point. * Can be interpolated along a spline segment. * Spline: * Different value for every spline. * Optional domains: * Segment: * Segment is the part between two control points. * Segment Corner: * Two values for every segment, one for the left and one for the right point. * Similar to Face Corner attributes. * Useful especially for uvs on cyclic splines. * Both segment domains are a bit more complex than the other ones, because their domain size changes when splines become (non-)cyclic. * At least when we don't want to store unnecessary values for non-cyclic splines. * For example, one could store segment data on the point domain, but then there would be an additional unused value. * Furthermore, segment corner attributes could be stored as two separate point attributes. * The memory overhead could be very large when there are very many very short splines (as is common for hair). * Built-in Attributes: * Obligatory: * `position` on points (`float3`) * `handle_left`, `handle_right` on points (`float3`) * Only available when `type` is `bezier`. * Optional: * `cyclic` on splines (`bool`) * Default: `false`. * `type` on splines (`enum`, `uint8`) * Default: `poly`. * Values: `poly`, `bezier`, `nurbs`, `catmull-rom` * `radius` on points (`float`) * Default: tbd. * Probably zero, or it should be an obligatory attribute. * `resolution` on splines (`int`) * Default: zero? * Available even on poly splines? * `id` on points (`int`, later maybe some bigger integer) * Default: index. * `spline_id` on splines (`int`, later maybe some bigger integer) * Default: index. * `tilt` on points (`float`) * Default: 0. * `handle_type_left`, `handle_type_right` on points (`enum`, `int8`) * Default: `free`. * Values: `free`, `auto`, `vector`, `align`. * Only available when `type` is `bezier`. * `knots_mode` on splines (`enum`, `int8`) * Default: `normal` * Values: `normal`, `end_point`, `bezier`. * Making the knot vector available directly is probably preferrable for proper NURB spline support * `normal_mode` (`enum`, `int8`) * Default: `minimum_twist` * Values: `minimum_twist`, `custom` * See [these](https://devtalk.blender.org/t/2021-12-6-2021-12-10-geometry-nodes-sub-module-meetings/21859) meeting notes about custom normals on splines. * `up` on points (`float3`) * Default `(0, 0, 1)`. * Used with normal calculation. * Maybe mapping information, see `HairMapping`. * Unclear whether this really should be a built-in attribute or whether it should just be a naming convention. * There seem to be many ways two encode a position on a mesh. * Instead of the poly index, one could also use an id. * Instead of uv coordinates, one could use barycentric coordinates on the tesselated triangles. * Maybe we could introduce a new more generic `Surface Hook` that is implementation defined. * Experimented with it while working on particle nodes: https://devtalk.blender.org/t/particle-nodes-ui/8808/319 * It's quite useful to have a concept or a "location on a surface" without worrying too much about its implementation. * Arbitrary fields could be sampled at such a location. * This also simplifies e.g. the raycast node a lot. * Concerns with storing point attribute for multiple splines in a continuous array: * Operations like adding or removing a point require possibly expensive array operations. * In practice we probably have to iterate over all points anyway after such a change. * The much more common case is to process all splines/points at the same time. * Shouldn't optimize the case that happens 1% of the times at the cost of the other 99%. * If it really becomes a problem, we could think about splitting up a single long array into multiple shorter ones, each of which may contain multiple splines. * This optimization isn't really worth it (yet). * Runtime/Derived data: * Positions, tangent, normal, ... of evaluated points (which take resolution into account). * Conversion to Poly Splines: * Renderers and exporters might require poly splines when they can't work with e.g. bezier splines directly. * There should be an easy way to get all splines as poly splines through rna. * Maybe similar to `Object.to_mesh`. ## Possible Steps Assuming we use the name `NCurve`. Still needs to be decided. 1. Add new `NCurve` id struct (no corresponding object type yet). * Could rename `Hair` to `NCurve`. 1. Change geometry nodes to use `NCurve` instead of `CurveEval`. 1. Add object type for `NCurve` as experimental feature. 1. Port curve edit mode from `Curve` to `NCurve`. 1. Add versioning to convert all "normal" curves to `NCurve`. 1. Add data blocks for `TextGeometry` (`Text` is used already) and `Surface`. 1. Add versioning to convert remaining `Curve` to text and surface objects. 1. Remove `Curve` data block and object. 1. Remove `NCurve` from experimental feature set. ## Questions * Under what circumstances can render engines benefit from rendering curves directly instead of a mesh generated from a curve? * Do we already need a new object type in 3.1 or is it enough to be able to import the data into a `GeometrySet`? * Can a catmul-rom spline be cyclic? * How does grease pencil fit into this?