Page MenuHome

Geometry Nodes: Curve Fill
ClosedPublic

Authored by Erik Abrahamsson (erik85) on Jul 7 2021, 11:30 PM.
Subscribers
None
Tokens
"Love" token, awarded by PrettyFireNOI7."Love" token, awarded by gilberto_rodrigues."Love" token, awarded by gaonirico."Love" token, awarded by asmitty."Love" token, awarded by wilBr."Love" token, awarded by RC12."Love" token, awarded by HooglyBoogly.

Details

Summary

This node takes a Curve Geometry input and creates a fill on Z=0 using a constrained Delaunay triangulation algorithm.

A single spline segment can be added anywhere to improve triangulation like this:

Diff Detail

Repository
rB Blender

Event Timeline

Erik Abrahamsson (erik85) requested review of this revision.Jul 7 2021, 11:30 PM
Erik Abrahamsson (erik85) created this revision.
Hans Goudey (HooglyBoogly) requested changes to this revision.Jul 12 2021, 9:47 PM

This works quite well! When there are a bunch of splines, performance can get very slow though. I haven't profiled this yet, maybe there are some things to optimize. It may be worth filtering into buckets based on bounding boxes at least.

source/blender/makesrna/intern/rna_nodetree.c
10108

"TRIANGLE_FAN" seems a bit odd, what about "TRIANGLES"?

10112

"BMesh-valid" isn't how it should be presented to users, I don't think. Generally users are expected to know the difference between triangles and N-Gons anyway.

source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
49
/home/hans/Blender-Git/blender/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc:48:75: warning: unused parameter ‘C’ [-Wunused-parameter]
   48 | static void geo_node_curve_fill_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
      |                                                          ~~~~~~~~~~~~~~~~~^
67

This function is quite long, and will only grow in the future. It could be split into a couple sub-functions:

  1. Create grouped spline vectors
  2. Fill CDT_inputs and do triangulation.
  3. Convert the result to a mesh
  4. Return the mesh
67

The curve component should be a reference, since it's always expected to be non-null.

89

Looks like the size of the result vector is always the size of the number of keys. What about just declaring this after filling "keys"?

Vector<blender::meshintersect::CDT_result<double>> results(keys.size());

101

This naming is a bit redundant IMO, "i" is almost always an index already.
i_index -> i

135–139
const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ?
                                        CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES :
                                        CDT_INSIDE_WITH_HOLES;

Also, we can move this out of the loop.

141–147

With the change mentioned above, this can be simply results[i_index] = delaunay_2d_calc(input, output_type);

156

I was looking into removing the use of "Auto" here. It looks like using the same CDT_result in C and C++ for different structs is causing issues. Instead of defining the C result in C++ with extern "C", I think we should only define the C++ result struct.

202

Is this call really necessary? You just filled the edge vector above. It would be nice to avoid this, since it's usually the most expensive part of creating a mesh.

210

Instances should either be handled like the point distribute node, or we should realize them with bke::geometry_set_realize_instances

233

There should be a newline at the end of the file.

This revision now requires changes to proceed.Jul 12 2021, 9:47 PM
Erik Abrahamsson (erik85) marked 12 inline comments as done.Jul 13 2021, 2:25 AM
Erik Abrahamsson (erik85) added inline comments.
source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
202

Yes unfortunately it breaks completely without it. I haven't investigated, but probably the delaunay code doesn't create all edges.

Erik Abrahamsson (erik85) marked an inline comment as done.

Rebased and fixed according to review comments.

Hans Goudey (HooglyBoogly) requested changes to this revision.EditedJul 14 2021, 4:45 AM

I think I found an issue with non-cyclic interior splines:

The selected spline isn't cyclic, and I think it's missing the last vertex somehow.
However, I remembered you talking about how interior non-cyclic splines could just influence topology and not cause a hole. I think that would be preferable if it's possible.

source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
35–37

Unused now.

85

I think you could add a size() method to MultiValueMap so you could reserve the proper amount here.

95

Theoretically if you moved this up above the inner for loop you could avoid reallocating the input arrays for each group index in some cases. What do you think about that?

102

Okay, I see how there are two indices you're iterating through. Keeping track of an index variable manually isn't ideal though. How about this:

for (const int i_group : range) {
  splines = groups.lookup(keys[i_group]);
  for (const int i_spline : splines.index_range()) {
    ...
  }
}

I know this is a bit nitpicky, but I think it's worth it.

111

Maybe this is subjective, but I don't really see the need for _ptr at the end of this name, it's clear that spline is a pointer from its type.

186

Let's add what we talked about as a comment here, something like:
The delaunay triangulation doesn't seem to return all of the necessary edges, even in triangulation mode.

IMO this still doesn't make complete sense, but if we don't understand it we should at least document that uncertainty somewhere for the future.

This revision now requires changes to proceed.Jul 14 2021, 4:45 AM
Erik Abrahamsson (erik85) marked 6 inline comments as done.

Fixed according to review comments.

source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
95

Not sure about this. I think I need to reinitialize the arrays anyway unless they are exactly the same size?

102

Yes, that makes perfect sense to me and made the code easier to read.

Only use cyclic splines.

Use normal and point on a calculated plane from the 2D spline to fill rotated and translated 2D splines.

Hans Goudey (HooglyBoogly) requested changes to this revision.Aug 10 2021, 4:49 AM

After thinking about it more, looking at the code, and testing, I think this node shouldn't handle the different differing projection projection directions per-group. Calculation of a 2D "normal" for each spline could make sense, it just doesn't fit in this node in my opinion, so it would be better to try to generalize some of that functionality and split it out.

I also still unsure about the hard-coded group index. I understand filtering into groups by bounds automatically might now work out, but as it is, it's more of a feature for the text nodes. While that may make total sense, it should probably be part of a separate patch.

These thoughts match up with the feedback I got when I presented these ideas to the rest of the team. But it's also easier to discuss those more complex ideas in the future if the base is already there.

If you're not excited about stripping away some of these things from the patch that's totally fair-- I could help get this patch to a commit-able state if you weren't interested.

This revision now requires changes to proceed.Aug 10 2021, 4:49 AM
Erik Abrahamsson (erik85) edited the summary of this revision. (Show Details)

Stripped away everything but the bare minimum needed to get output.

Thanks Eric, this is working well! I'll do some cleanups when committing, I mentioned the more significant ones inline.

source/blender/blenlib/BLI_multi_value_map.hh
158

This wasn't necessary anymore.

source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
49

I used UI_ITEM_R_EXPAND here, since there are only two options.

70–77

This can be replaced with Array<int> offsets = curve.evaluated_point_offsets(); :)

101

Since this wasn't a reference, it was probably copying the entire result. Easy to miss that &!

This revision is now accepted and ready to land.Aug 30 2021, 5:43 AM
This revision was automatically updated to reflect the committed changes.