Blender  V3.3
node_geo_extrude_mesh.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BLI_disjoint_set.hh"
4 #include "BLI_task.hh"
5 #include "BLI_vector_set.hh"
6 
7 #include "DNA_mesh_types.h"
8 #include "DNA_meshdata_types.h"
9 
10 #include "BKE_attribute_math.hh"
11 #include "BKE_mesh.h"
12 #include "BKE_mesh_runtime.h"
13 
14 #include "UI_interface.h"
15 #include "UI_resources.h"
16 
17 #include "node_geometry_util.hh"
18 
20 
22 
24 {
25  b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH);
26  b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
27  b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value();
28  b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).supports_field();
29  b.add_input<decl::Bool>(N_("Individual")).default_value(true);
30  b.add_output<decl::Geometry>("Mesh");
31  b.add_output<decl::Bool>(N_("Top")).field_source();
32  b.add_output<decl::Bool>(N_("Side")).field_source();
33 }
34 
35 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
36 {
37  uiLayoutSetPropSep(layout, true);
38  uiLayoutSetPropDecorate(layout, false);
39  uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
40 }
41 
43 {
44  NodeGeometryExtrudeMesh *data = MEM_cnew<NodeGeometryExtrudeMesh>(__func__);
46  node->storage = data;
47 }
48 
50 {
51  const NodeGeometryExtrudeMesh &storage = node_storage(*node);
52  const GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
53 
54  bNodeSocket *individual_socket = (bNodeSocket *)node->inputs.last;
55 
56  nodeSetSocketAvailability(ntree, individual_socket, mode == GEO_NODE_EXTRUDE_MESH_FACES);
57 }
58 
62 };
63 
65  const AnonymousAttributeID *id,
66  const eAttrDomain domain,
67  const IndexMask selection)
68 {
69  BLI_assert(!component.attributes()->contains(id));
70 
72  component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain);
73  /* Rely on the new attribute being zeroed by default. */
74  BLI_assert(!attribute.span.as_span().contains(true));
75 
76  if (selection.is_range()) {
77  attribute.span.slice(selection.as_range()).fill(true);
78  }
79  else {
80  attribute.span.fill_indices(selection, true);
81  }
82 
83  attribute.finish();
84 }
85 
87 {
88  return {mesh.mvert, mesh.totvert};
89 }
91 {
92  return {mesh.medge, mesh.totedge};
93 }
95 {
96  return {mesh.mpoly, mesh.totpoly};
97 }
99 {
100  return {mesh.mpoly, mesh.totpoly};
101 }
103 {
104  return {mesh.mloop, mesh.totloop};
105 }
107 {
108  return {mesh.mloop, mesh.totloop};
109 }
110 
115 static void expand_mesh(Mesh &mesh,
116  const int vert_expand,
117  const int edge_expand,
118  const int poly_expand,
119  const int loop_expand)
120 {
121  if (vert_expand != 0) {
123  mesh.totvert += vert_expand;
125  }
126  else {
127  /* Even when the number of vertices is not changed, the mesh can still be deformed. */
129  }
130  if (edge_expand != 0) {
132  mesh.totedge += edge_expand;
134  }
135  if (poly_expand != 0) {
137  mesh.totpoly += poly_expand;
139  }
140  if (loop_expand != 0) {
142  mesh.totloop += loop_expand;
144  }
146 }
147 
149 {
150  switch (domain) {
151  case ATTR_DOMAIN_POINT:
152  return mesh.vdata;
153  case ATTR_DOMAIN_EDGE:
154  return mesh.edata;
155  case ATTR_DOMAIN_FACE:
156  return mesh.pdata;
157  case ATTR_DOMAIN_CORNER:
158  return mesh.ldata;
159  default:
161  return mesh.vdata;
162  }
163 }
164 
166 {
168  CustomData &custom_data = get_customdata(mesh, domain);
169  if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
170  return {orig_indices, attributes.domain_size(domain)};
171  }
172  return {};
173 }
174 
175 static MEdge new_edge(const int v1, const int v2)
176 {
177  MEdge edge;
178  edge.v1 = v1;
179  edge.v2 = v2;
180  edge.flag = (ME_EDGEDRAW | ME_EDGERENDER);
181  return edge;
182 }
183 
184 static MEdge new_loose_edge(const int v1, const int v2)
185 {
186  MEdge edge;
187  edge.v1 = v1;
188  edge.v2 = v2;
189  edge.flag = ME_LOOSEEDGE;
190  return edge;
191 }
192 
193 static MPoly new_poly(const int loopstart, const int totloop)
194 {
195  MPoly poly;
196  poly.loopstart = loopstart;
197  poly.totloop = totloop;
198  poly.flag = 0;
199  return poly;
200 }
201 
203 {
204  BLI_assert(dst.size() == indices.size());
205  for (const int i : dst.index_range()) {
206  dst[i] = src[indices[i]];
207  }
208 }
209 
210 template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask)
211 {
212  BLI_assert(dst.size() == mask.size());
213  threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
214  for (const int i : range) {
215  dst[i] = src[mask[i]];
216  }
217  });
218 }
219 
224 template<typename T, typename GetMixIndicesFn>
225 void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
226 {
227  threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
228  attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)};
229  for (const int i_dst : IndexRange(range.size())) {
230  for (const int i_src : get_mix_indices_fn(range[i_dst])) {
231  mixer.mix_in(i_dst, src[i_src]);
232  }
233  }
234  mixer.finalize();
235  });
236 }
237 
238 static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
239  Span<MEdge> edges,
240  const int vert_offset = 0)
241 {
242  Array<Vector<int>> vert_to_edge_map(vert_size);
243  for (const int i : edges.index_range()) {
244  vert_to_edge_map[edges[i].v1 - vert_offset].append(i);
245  vert_to_edge_map[edges[i].v2 - vert_offset].append(i);
246  }
247  return vert_to_edge_map;
248 }
249 
251  const Field<bool> &selection_field,
252  const Field<float3> &offset_field,
253  const AttributeOutputs &attribute_outputs)
254 {
255  Mesh &mesh = *component.get_for_write();
256  const int orig_vert_size = mesh.totvert;
257  const int orig_edge_size = mesh.totedge;
258 
260  FieldEvaluator evaluator{context, mesh.totvert};
261  evaluator.add(offset_field);
262  evaluator.set_selection(selection_field);
263  evaluator.evaluate();
264  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
265  const VArray<float3> offsets = evaluator.get_evaluated<float3>(0);
266 
267  /* This allows parallelizing attribute mixing for new edges. */
268  Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh));
269 
270  expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
271 
272  const IndexRange new_vert_range{orig_vert_size, selection.size()};
273  const IndexRange new_edge_range{orig_edge_size, selection.size()};
274 
275  MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
276  MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range);
277 
278  for (const int i_selection : selection.index_range()) {
279  new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
280  }
281 
282  MutableAttributeAccessor attributes = *component.attributes_for_write();
283 
284  attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
285  if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
286  return true;
287  }
289  id, meta_data.domain, meta_data.data_type);
290  attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
291  using T = decltype(dummy);
292  MutableSpan<T> data = attribute.span.typed<T>();
293  switch (attribute.domain) {
294  case ATTR_DOMAIN_POINT: {
295  /* New vertices copy the attribute values from their source vertex. */
296  copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
297  break;
298  }
299  case ATTR_DOMAIN_EDGE: {
300  /* New edge values are mixed from of all the edges connected to the source vertex. */
301  copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) {
302  return vert_to_edge_map[selection[i]].as_span();
303  });
304  break;
305  }
306  default:
307  BLI_assert_unreachable();
308  }
309  });
310 
311  attribute.finish();
312  return true;
313  });
314 
315  devirtualize_varray(offsets, [&](const auto offsets) {
316  threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
317  for (const int i : range) {
318  const float3 offset = offsets[selection[i]];
319  add_v3_v3(new_verts[i].co, offset);
320  }
321  });
322  });
323 
324  MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
325  vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
326 
327  if (attribute_outputs.top_id) {
329  component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
330  }
331  if (attribute_outputs.side_id) {
333  component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
334  }
335 
337 }
338 
340 {
341  Span<MPoly> polys = mesh_polys(mesh);
342  Span<MLoop> loops = mesh_loops(mesh);
343  Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
344 
345  for (const int i_poly : polys.index_range()) {
346  const MPoly &poly = polys[i_poly];
347  for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
348  polys_of_edge[loop.e].append(i_poly);
349  }
350  }
351 
352  return polys_of_edge;
353 }
354 
355 static void fill_quad_consistent_direction(Span<MLoop> other_poly_loops,
356  MutableSpan<MLoop> new_loops,
357  const int vert_connected_to_poly_1,
358  const int vert_connected_to_poly_2,
359  const int vert_across_from_poly_1,
360  const int vert_across_from_poly_2,
361  const int edge_connected_to_poly,
362  const int connecting_edge_1,
363  const int edge_across_from_poly,
364  const int connecting_edge_2)
365 {
366  /* Find the loop on the polygon connected to the new quad that uses the duplicate edge. */
367  bool start_with_connecting_edge = true;
368  for (const MLoop &loop : other_poly_loops) {
369  if (loop.e == edge_connected_to_poly) {
370  start_with_connecting_edge = loop.v == vert_connected_to_poly_1;
371  break;
372  }
373  }
374  if (start_with_connecting_edge) {
375  new_loops[0].v = vert_connected_to_poly_1;
376  new_loops[0].e = connecting_edge_1;
377  new_loops[1].v = vert_across_from_poly_1;
378  new_loops[1].e = edge_across_from_poly;
379  new_loops[2].v = vert_across_from_poly_2;
380  new_loops[2].e = connecting_edge_2;
381  new_loops[3].v = vert_connected_to_poly_2;
382  new_loops[3].e = edge_connected_to_poly;
383  }
384  else {
385  new_loops[0].v = vert_connected_to_poly_1;
386  new_loops[0].e = edge_connected_to_poly;
387  new_loops[1].v = vert_connected_to_poly_2;
388  new_loops[1].e = connecting_edge_2;
389  new_loops[2].v = vert_across_from_poly_2;
390  new_loops[2].e = edge_across_from_poly;
391  new_loops[3].v = vert_across_from_poly_1;
392  new_loops[3].e = connecting_edge_1;
393  }
394 }
395 
396 template<typename T>
397 static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices)
398 {
399  static_assert(is_same_any_v<T, int, int64_t>);
400 
401  VectorSet<int> vert_indices;
402  vert_indices.reserve(edge_indices.size());
403  for (const T i_edge : edge_indices) {
404  const MEdge &edge = mesh.medge[i_edge];
405  vert_indices.add(edge.v1);
406  vert_indices.add(edge.v2);
407  }
408  return vert_indices;
409 }
410 
412  const Field<bool> &selection_field,
413  const Field<float3> &offset_field,
414  const AttributeOutputs &attribute_outputs)
415 {
416  Mesh &mesh = *component.get_for_write();
417  const int orig_vert_size = mesh.totvert;
418  Span<MEdge> orig_edges = mesh_edges(mesh);
419  Span<MPoly> orig_polys = mesh_polys(mesh);
420  const int orig_loop_size = mesh.totloop;
421 
423  FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
424  edge_evaluator.set_selection(selection_field);
425  edge_evaluator.add(offset_field);
426  edge_evaluator.evaluate();
427  const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
428  const VArray<float3> edge_offsets = edge_evaluator.get_evaluated<float3>(0);
429  if (edge_selection.is_empty()) {
430  return;
431  }
432 
433  const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
434 
435  /* Find the offsets on the vertex domain for translation. This must be done before the mesh's
436  * custom data layers are reallocated, in case the virtual array references on of them. */
437  Array<float3> vert_offsets;
438  if (!edge_offsets.is_single()) {
439  vert_offsets.reinitialize(orig_vert_size);
441  for (const int i_edge : edge_selection) {
442  const MEdge &edge = orig_edges[i_edge];
443  const float3 offset = edge_offsets[i_edge];
444  mixer.mix_in(edge.v1, offset);
445  mixer.mix_in(edge.v2, offset);
446  }
447  mixer.finalize();
448  }
449 
450  const VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, edge_selection.indices());
451 
452  const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
453  /* The extruded edges connect the original and duplicate edges. */
454  const IndexRange connect_edge_range{orig_edges.size(), new_vert_range.size()};
455  /* The duplicate edges are extruded copies of the selected edges. */
456  const IndexRange duplicate_edge_range = connect_edge_range.after(edge_selection.size());
457  /* There is a new polygon for every selected edge. */
458  const IndexRange new_poly_range{orig_polys.size(), edge_selection.size()};
459  /* Every new polygon is a quad with four corners. */
460  const IndexRange new_loop_range{orig_loop_size, new_poly_range.size() * 4};
461 
463  new_vert_range.size(),
464  connect_edge_range.size() + duplicate_edge_range.size(),
465  new_poly_range.size(),
466  new_loop_range.size());
467 
468  MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
469  MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range);
470  MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range);
472  MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
474  MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
475 
476  for (const int i : connect_edges.index_range()) {
477  connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
478  }
479 
480  for (const int i : duplicate_edges.index_range()) {
481  const MEdge &orig_edge = mesh.medge[edge_selection[i]];
482  const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
483  const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
484  duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
485  }
486 
487  for (const int i : new_polys.index_range()) {
488  new_polys[i] = new_poly(new_loop_range[i * 4], 4);
489  }
490 
491  for (const int i : edge_selection.index_range()) {
492  const int orig_edge_index = edge_selection[i];
493 
494  const MEdge &duplicate_edge = duplicate_edges[i];
495  const int new_vert_1 = duplicate_edge.v1;
496  const int new_vert_2 = duplicate_edge.v2;
497  const int extrude_index_1 = new_vert_1 - orig_vert_size;
498  const int extrude_index_2 = new_vert_2 - orig_vert_size;
499 
500  Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
501 
502  /* When there was a single polygon connected to the new polygon, we can use the old one to keep
503  * the face direction consistent. When there is more than one connected edge, the new face
504  * direction is totally arbitrary and the only goal for the behavior is to be deterministic. */
505  Span<MLoop> connected_poly_loops = {};
506  if (connected_polys.size() == 1) {
507  const MPoly &connected_poly = polys[connected_polys.first()];
508  connected_poly_loops = loops.slice(connected_poly.loopstart, connected_poly.totloop);
509  }
510  fill_quad_consistent_direction(connected_poly_loops,
511  new_loops.slice(4 * i, 4),
512  new_vert_indices[extrude_index_1],
513  new_vert_indices[extrude_index_2],
514  new_vert_1,
515  new_vert_2,
516  orig_edge_index,
517  connect_edge_range[extrude_index_1],
518  duplicate_edge_range[i],
519  connect_edge_range[extrude_index_2]);
520  }
521 
522  /* Create a map of indices in the extruded vertices array to all of the indices of edges
523  * in the duplicate edges array that connect to that vertex. This can be used to simplify the
524  * mixing of attribute data for the connecting edges. */
525  const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
526  new_vert_range.size(), duplicate_edges, orig_vert_size);
527 
528  MutableAttributeAccessor attributes = *component.attributes_for_write();
529 
530  attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
532  id, meta_data.domain, meta_data.data_type);
533  if (!attribute) {
534  return true; /* Impossible to write the "normal" attribute. */
535  }
536 
537  attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
538  using T = decltype(dummy);
539  MutableSpan<T> data = attribute.span.typed<T>();
540  switch (attribute.domain) {
541  case ATTR_DOMAIN_POINT: {
542  /* New vertices copy the attribute values from their source vertex. */
543  copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
544  break;
545  }
546  case ATTR_DOMAIN_EDGE: {
547  /* Edges parallel to original edges copy the edge attributes from the original edges. */
548  MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
549  copy_with_mask(duplicate_data, data.as_span(), edge_selection);
550 
551  /* Edges connected to original vertices mix values of selected connected edges. */
552  MutableSpan<T> connect_data = data.slice(connect_edge_range);
553  copy_with_mixing(connect_data, duplicate_data.as_span(), [&](const int i_new_vert) {
554  return new_vert_to_duplicate_edge_map[i_new_vert].as_span();
555  });
556  break;
557  }
558  case ATTR_DOMAIN_FACE: {
559  /* Attribute values for new faces are a mix of the values of faces connected to the its
560  * original edge. */
561  copy_with_mixing(data.slice(new_poly_range), data.as_span(), [&](const int i) {
562  return edge_to_poly_map[edge_selection[i]].as_span();
563  });
564 
565  break;
566  }
567  case ATTR_DOMAIN_CORNER: {
568  /* New corners get the average value of all adjacent corners on original faces connected
569  * to the original edge of their face. */
570  MutableSpan<T> new_data = data.slice(new_loop_range);
571  threading::parallel_for(edge_selection.index_range(), 256, [&](const IndexRange range) {
572  for (const int i_edge_selection : range) {
573  const int orig_edge_index = edge_selection[i_edge_selection];
574 
575  Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
576  if (connected_polys.is_empty()) {
577  /* If there are no connected polygons, there is no corner data to
578  * interpolate. */
579  new_data.slice(4 * i_edge_selection, 4).fill(T());
580  continue;
581  }
582 
583  /* Both corners on each vertical edge of the side polygon get the same value,
584  * so there are only two unique values to mix. */
585  Array<T> side_poly_corner_data(2);
586  attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data};
587 
588  const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
589  const int new_vert_1 = duplicate_edge.v1;
590  const int new_vert_2 = duplicate_edge.v2;
591  const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
592  const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
593 
594  /* Average the corner data from the corners that share a vertex from the
595  * polygons that share an edge with the extruded edge. */
596  for (const int i_connected_poly : connected_polys.index_range()) {
597  const MPoly &connected_poly = polys[connected_polys[i_connected_poly]];
598  for (const int i_loop :
599  IndexRange(connected_poly.loopstart, connected_poly.totloop)) {
600  const MLoop &loop = loops[i_loop];
601  if (loop.v == orig_vert_1) {
602  mixer.mix_in(0, data[i_loop]);
603  }
604  if (loop.v == orig_vert_2) {
605  mixer.mix_in(1, data[i_loop]);
606  }
607  }
608  }
609 
610  mixer.finalize();
611 
612  /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
613  * simpler (though probably slower) to just match the corner data based on the vertex
614  * indices. */
615  for (const int i : IndexRange(4 * i_edge_selection, 4)) {
616  if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
617  new_data[i] = side_poly_corner_data.first();
618  }
619  else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
620  new_data[i] = side_poly_corner_data.last();
621  }
622  }
623  }
624  });
625  break;
626  }
627  default:
628  BLI_assert_unreachable();
629  }
630  });
631 
632  attribute.finish();
633  return true;
634  });
635 
636  if (edge_offsets.is_single()) {
637  const float3 offset = edge_offsets.get_internal_single();
638  threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
639  for (const int i : range) {
640  add_v3_v3(new_verts[i].co, offset);
641  }
642  });
643  }
644  else {
645  threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
646  for (const int i : range) {
647  add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]);
648  }
649  });
650  }
651 
652  MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
653  vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
654 
655  MutableSpan<int> edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE);
656  edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE);
657  edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE);
658 
659  if (attribute_outputs.top_id) {
661  component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
662  }
663  if (attribute_outputs.side_id) {
665  component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
666  }
667 
669 }
670 
676  const Field<bool> &selection_field,
677  const Field<float3> &offset_field,
678  const AttributeOutputs &attribute_outputs)
679 {
680  Mesh &mesh = *component.get_for_write();
681  const int orig_vert_size = mesh.totvert;
682  Span<MEdge> orig_edges = mesh_edges(mesh);
683  Span<MPoly> orig_polys = mesh_polys(mesh);
684  Span<MLoop> orig_loops = mesh_loops(mesh);
685 
687  FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
688  poly_evaluator.set_selection(selection_field);
689  poly_evaluator.add(offset_field);
690  poly_evaluator.evaluate();
691  const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
692  const VArray<float3> poly_offsets = poly_evaluator.get_evaluated<float3>(0);
693  if (poly_selection.is_empty()) {
694  return;
695  }
696 
697  Array<bool> poly_selection_array(orig_polys.size(), false);
698  for (const int i_poly : poly_selection) {
699  poly_selection_array[i_poly] = true;
700  }
701 
702  /* Mix the offsets from the face domain to the vertex domain. Evaluate on the face domain above
703  * in order to be consistent with the selection, and to use the face normals rather than vertex
704  * normals as an offset, for example. */
705  Array<float3> vert_offsets;
706  if (!poly_offsets.is_single()) {
707  vert_offsets.reinitialize(orig_vert_size);
709  for (const int i_poly : poly_selection) {
710  const MPoly &poly = orig_polys[i_poly];
711  const float3 offset = poly_offsets[i_poly];
712  for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
713  mixer.mix_in(loop.v, offset);
714  }
715  }
716  mixer.finalize();
717  }
718 
719  /* All of the faces (selected and deselected) connected to each edge. */
720  const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
721 
722  /* All vertices that are connected to the selected polygons.
723  * Start the size at one vert per poly to reduce unnecessary reallocation. */
724  VectorSet<int> all_selected_verts;
725  all_selected_verts.reserve(orig_polys.size());
726  for (const int i_poly : poly_selection) {
727  const MPoly &poly = orig_polys[i_poly];
728  for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
729  all_selected_verts.add(loop.v);
730  }
731  }
732 
733  /* Edges inside of an extruded region that are also attached to deselected edges. They must be
734  * duplicated in order to leave the old edge attached to the unchanged deselected faces. */
735  VectorSet<int> new_inner_edge_indices;
736  /* Edges inside of an extruded region. Their vertices should be translated
737  * with the offset, but the edges themselves should not be duplicated. */
738  Vector<int> inner_edge_indices;
739  /* The extruded face corresponding to each boundary edge (and each boundary face). */
740  Vector<int> edge_extruded_face_indices;
741  /* Edges on the outside of selected regions, either because there are no
742  * other connected faces, or because all of the other faces aren't selected. */
743  VectorSet<int> boundary_edge_indices;
744  for (const int i_edge : orig_edges.index_range()) {
745  Span<int> polys = edge_to_poly_map[i_edge];
746 
747  int i_selected_poly = -1;
748  int deselected_poly_count = 0;
749  int selected_poly_count = 0;
750  for (const int i_other_poly : polys) {
751  if (poly_selection_array[i_other_poly]) {
752  selected_poly_count++;
753  i_selected_poly = i_other_poly;
754  }
755  else {
756  deselected_poly_count++;
757  }
758  }
759 
760  if (selected_poly_count == 1) {
761  /* If there is only one selected polygon connected to the edge,
762  * the edge should be extruded to form a "side face". */
763  boundary_edge_indices.add_new(i_edge);
764  edge_extruded_face_indices.append(i_selected_poly);
765  }
766  else if (selected_poly_count > 1) {
767  /* The edge is inside an extruded region of faces. */
768  if (deselected_poly_count > 0) {
769  /* Add edges that are also connected to deselected edges to a separate list. */
770  new_inner_edge_indices.add_new(i_edge);
771  }
772  else {
773  /* Otherwise, just keep track of edges inside the region so that
774  * we can reattach them to duplicated vertices if necessary. */
775  inner_edge_indices.append(i_edge);
776  }
777  }
778  }
779 
780  VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, boundary_edge_indices.as_span());
781  /* Before adding the rest of the new vertices from the new inner edges, store the number
782  * of new vertices from the boundary edges, since this is the number of connecting edges. */
783  const int extruded_vert_size = new_vert_indices.size();
784 
785  /* The vertices attached to duplicate inner edges also have to be duplicated. */
786  for (const int i_edge : new_inner_edge_indices) {
787  const MEdge &edge = mesh.medge[i_edge];
788  new_vert_indices.add(edge.v1);
789  new_vert_indices.add(edge.v2);
790  }
791 
792  /* New vertices forming the duplicated boundary edges and the ends of the new inner edges. */
793  const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
794  /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
795  const IndexRange connect_edge_range{orig_edges.size(), extruded_vert_size};
796  /* Each selected edge is duplicated to form a single edge on the extrusion. */
797  const IndexRange boundary_edge_range = connect_edge_range.after(boundary_edge_indices.size());
798  /* Duplicated edges inside regions that were connected to deselected faces. */
799  const IndexRange new_inner_edge_range = boundary_edge_range.after(new_inner_edge_indices.size());
800  /* Each edge selected for extrusion is extruded into a single face. */
801  const IndexRange side_poly_range{orig_polys.size(), boundary_edge_indices.size()};
802  /* The loops that form the new side faces. */
803  const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
804 
806  new_vert_range.size(),
807  connect_edge_range.size() + boundary_edge_range.size() + new_inner_edge_range.size(),
808  side_poly_range.size(),
809  side_loop_range.size());
810 
812  MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
813  MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range);
814  MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range);
816  MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
818  MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
819 
820  /* Initialize the edges that form the sides of the extrusion. */
821  for (const int i : connect_edges.index_range()) {
822  connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
823  }
824 
825  /* Initialize the edges that form the top of the extrusion. */
826  for (const int i : boundary_edges.index_range()) {
827  const MEdge &orig_edge = edges[boundary_edge_indices[i]];
828  const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
829  const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
830  boundary_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
831  }
832 
833  /* Initialize the new edges inside of extrude regions. */
834  for (const int i : new_inner_edge_indices.index_range()) {
835  const MEdge &orig_edge = edges[new_inner_edge_indices[i]];
836  const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
837  const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
838  new_inner_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
839  }
840 
841  /* Initialize the new side polygons. */
842  for (const int i : new_polys.index_range()) {
843  new_polys[i] = new_poly(side_loop_range[i * 4], 4);
844  }
845 
846  /* Connect original edges inside face regions to any new vertices, if necessary. */
847  for (const int i : inner_edge_indices) {
848  MEdge &edge = edges[i];
849  const int i_new_vert_1 = new_vert_indices.index_of_try(edge.v1);
850  const int i_new_vert_2 = new_vert_indices.index_of_try(edge.v2);
851  if (i_new_vert_1 != -1) {
852  edge.v1 = new_vert_range[i_new_vert_1];
853  }
854  if (i_new_vert_2 != -1) {
855  edge.v2 = new_vert_range[i_new_vert_2];
856  }
857  }
858 
859  /* Connect the selected faces to the extruded or duplicated edges and the new vertices. */
860  for (const int i_poly : poly_selection) {
861  const MPoly &poly = polys[i_poly];
862  for (MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
863  const int i_new_vert = new_vert_indices.index_of_try(loop.v);
864  if (i_new_vert != -1) {
865  loop.v = new_vert_range[i_new_vert];
866  }
867  const int i_boundary_edge = boundary_edge_indices.index_of_try(loop.e);
868  if (i_boundary_edge != -1) {
869  loop.e = boundary_edge_range[i_boundary_edge];
870  /* Skip the next check, an edge cannot be both a boundary edge and an inner edge. */
871  continue;
872  }
873  const int i_new_inner_edge = new_inner_edge_indices.index_of_try(loop.e);
874  if (i_new_inner_edge != -1) {
875  loop.e = new_inner_edge_range[i_new_inner_edge];
876  }
877  }
878  }
879 
880  /* Create the faces on the sides of extruded regions. */
881  for (const int i : boundary_edge_indices.index_range()) {
882  const MEdge &boundary_edge = boundary_edges[i];
883  const int new_vert_1 = boundary_edge.v1;
884  const int new_vert_2 = boundary_edge.v2;
885  const int extrude_index_1 = new_vert_1 - orig_vert_size;
886  const int extrude_index_2 = new_vert_2 - orig_vert_size;
887 
888  const MPoly &extrude_poly = polys[edge_extruded_face_indices[i]];
889 
890  fill_quad_consistent_direction(loops.slice(extrude_poly.loopstart, extrude_poly.totloop),
891  new_loops.slice(4 * i, 4),
892  new_vert_1,
893  new_vert_2,
894  new_vert_indices[extrude_index_1],
895  new_vert_indices[extrude_index_2],
896  boundary_edge_range[i],
897  connect_edge_range[extrude_index_1],
898  boundary_edge_indices[i],
899  connect_edge_range[extrude_index_2]);
900  }
901 
902  /* Create a map of indices in the extruded vertices array to all of the indices of edges
903  * in the duplicate edges array that connect to that vertex. This can be used to simplify the
904  * mixing of attribute data for the connecting edges. */
905  const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
906  new_vert_range.size(), boundary_edges, orig_vert_size);
907 
908  MutableAttributeAccessor attributes = *component.attributes_for_write();
909 
910  attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
912  id, meta_data.domain, meta_data.data_type);
913  if (!attribute) {
914  return true; /* Impossible to write the "normal" attribute. */
915  }
916 
917  attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
918  using T = decltype(dummy);
919  MutableSpan<T> data = attribute.span.typed<T>();
920  switch (attribute.domain) {
921  case ATTR_DOMAIN_POINT: {
922  /* New vertices copy the attributes from their original vertices. */
923  copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
924  break;
925  }
926  case ATTR_DOMAIN_EDGE: {
927  /* Edges parallel to original edges copy the edge attributes from the original edges. */
928  MutableSpan<T> boundary_data = data.slice(boundary_edge_range);
929  copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices);
930 
931  /* Edges inside of face regions also just duplicate their source data. */
932  MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range);
933  copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices);
934 
935  /* Edges connected to original vertices mix values of selected connected edges. */
936  MutableSpan<T> connect_data = data.slice(connect_edge_range);
937  copy_with_mixing(connect_data, boundary_data.as_span(), [&](const int i) {
938  return new_vert_to_duplicate_edge_map[i].as_span();
939  });
940  break;
941  }
942  case ATTR_DOMAIN_FACE: {
943  /* New faces on the side of extrusions get the values from the corresponding selected
944  * face. */
945  copy_with_indices(
946  data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices);
947  break;
948  }
949  case ATTR_DOMAIN_CORNER: {
950  /* New corners get the values from the corresponding corner on the extruded face. */
951  MutableSpan<T> new_data = data.slice(side_loop_range);
952  threading::parallel_for(
953  boundary_edge_indices.index_range(), 256, [&](const IndexRange range) {
954  for (const int i_boundary_edge : range) {
955  const MPoly &poly = polys[edge_extruded_face_indices[i_boundary_edge]];
956 
957  const MEdge &boundary_edge = boundary_edges[i_boundary_edge];
958  const int new_vert_1 = boundary_edge.v1;
959  const int new_vert_2 = boundary_edge.v2;
960  const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
961  const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
962 
963  /* Retrieve the data for the first two sides of the quad from the extruded
964  * polygon, which we generally expect to have just a small amount of sides. This
965  * loop could be eliminated by adding a cache of connected loops (which would
966  * also simplify some of the other code to find the correct loops on the extruded
967  * face). */
968  T data_1;
969  T data_2;
970  for (const int i_loop : IndexRange(poly.loopstart, poly.totloop)) {
971  if (loops[i_loop].v == new_vert_1) {
972  data_1 = data[i_loop];
973  }
974  if (loops[i_loop].v == new_vert_2) {
975  data_2 = data[i_loop];
976  }
977  }
978 
979  /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
980  * simpler (though probably slower) to just match the corner data based on the
981  * vertex indices. */
982  for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
983  if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
984  new_data[i] = data_1;
985  }
986  else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
987  new_data[i] = data_2;
988  }
989  }
990  }
991  });
992  break;
993  }
994  default:
995  BLI_assert_unreachable();
996  }
997  });
998 
999  attribute.finish();
1000  return true;
1001  });
1002 
1003  /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will
1004  * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might
1005  * still need an offset, but it was reused on the inside of a region of extruded faces. */
1006  if (poly_offsets.is_single()) {
1007  const float3 offset = poly_offsets.get_internal_single();
1009  IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
1010  for (const int i_orig : all_selected_verts.as_span().slice(range)) {
1011  const int i_new = new_vert_indices.index_of_try(i_orig);
1012  MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
1013  add_v3_v3(vert.co, offset);
1014  }
1015  });
1016  }
1017  else {
1019  IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
1020  for (const int i_orig : all_selected_verts.as_span().slice(range)) {
1021  const int i_new = new_vert_indices.index_of_try(i_orig);
1022  const float3 offset = vert_offsets[i_orig];
1023  MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
1024  add_v3_v3(vert.co, offset);
1025  }
1026  });
1027  }
1028 
1029  MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
1030  vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
1031 
1032  MutableSpan<int> edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE);
1033  edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE);
1034  edge_orig_indices.slice(new_inner_edge_range).fill(ORIGINDEX_NONE);
1035  edge_orig_indices.slice(boundary_edge_range).fill(ORIGINDEX_NONE);
1036 
1037  MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE);
1038  poly_orig_indices.slice(side_poly_range).fill(ORIGINDEX_NONE);
1039 
1040  if (attribute_outputs.top_id) {
1042  component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
1043  }
1044  if (attribute_outputs.side_id) {
1046  component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
1047  }
1048 
1050 }
1051 
1052 /* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */
1053 static IndexRange selected_corner_range(Span<int> offsets, const int index)
1054 {
1055  const int offset = offsets[index];
1056  const int next_offset = offsets[index + 1];
1057  return IndexRange(offset, next_offset - offset);
1058 }
1059 
1061  const Field<bool> &selection_field,
1062  const Field<float3> &offset_field,
1063  const AttributeOutputs &attribute_outputs)
1064 {
1065  Mesh &mesh = *component.get_for_write();
1066  const int orig_vert_size = mesh.totvert;
1067  const int orig_edge_size = mesh.totedge;
1068  Span<MPoly> orig_polys = mesh_polys(mesh);
1069  Span<MLoop> orig_loops = mesh_loops(mesh);
1070 
1071  /* Use a mesh for the result of the evaluation because the mesh is reallocated before
1072  * the vertices are moved, and the evaluated result might reference an attribute. */
1073  Array<float3> poly_offset(orig_polys.size());
1075  FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
1076  poly_evaluator.set_selection(selection_field);
1077  poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span());
1078  poly_evaluator.evaluate();
1079  const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
1080 
1081  /* Build an array of offsets into the new data for each polygon. This is used to facilitate
1082  * parallelism later on by avoiding the need to keep track of an offset when iterating through
1083  * all polygons. */
1084  int extrude_corner_size = 0;
1085  Array<int> index_offsets(poly_selection.size() + 1);
1086  for (const int i_selection : poly_selection.index_range()) {
1087  const MPoly &poly = orig_polys[poly_selection[i_selection]];
1088  index_offsets[i_selection] = extrude_corner_size;
1089  extrude_corner_size += poly.totloop;
1090  }
1091  index_offsets.last() = extrude_corner_size;
1092 
1093  const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
1094  /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
1095  const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
1096  /* Each selected edge is duplicated to form a single edge on the extrusion. */
1097  const IndexRange duplicate_edge_range = connect_edge_range.after(extrude_corner_size);
1098  /* Each edge selected for extrusion is extruded into a single face. */
1099  const IndexRange side_poly_range{orig_polys.size(), duplicate_edge_range.size()};
1100  const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
1101 
1102  expand_mesh(mesh,
1103  new_vert_range.size(),
1104  connect_edge_range.size() + duplicate_edge_range.size(),
1105  side_poly_range.size(),
1106  side_loop_range.size());
1107 
1108  MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
1110  MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
1111  MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
1113  MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
1115 
1116  /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some
1117  * of this data like the new edges or polygons could be easily split into separate loops, which
1118  * may or may not be faster, and would involve more duplication. */
1119  threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
1120  for (const int i_selection : range) {
1121  const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
1122 
1123  const MPoly &poly = polys[poly_selection[i_selection]];
1124  Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
1125 
1126  for (const int i : IndexRange(poly.totloop)) {
1127  const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
1128  const MLoop &orig_loop = poly_loops[i];
1129  const MLoop &orig_loop_next = poly_loops[i_next];
1130 
1131  const int i_extrude = poly_corner_range[i];
1132  const int i_extrude_next = poly_corner_range[i_next];
1133 
1134  const int i_duplicate_edge = duplicate_edge_range[i_extrude];
1135  const int new_vert = new_vert_range[i_extrude];
1136  const int new_vert_next = new_vert_range[i_extrude_next];
1137 
1138  const int orig_edge = orig_loop.e;
1139 
1140  const int orig_vert = orig_loop.v;
1141  const int orig_vert_next = orig_loop_next.v;
1142 
1143  duplicate_edges[i_extrude] = new_edge(new_vert, new_vert_next);
1144 
1145  new_polys[i_extrude] = new_poly(side_loop_range[i_extrude * 4], 4);
1146 
1147  MutableSpan<MLoop> side_loops = loops.slice(side_loop_range[i_extrude * 4], 4);
1148  side_loops[0].v = new_vert_next;
1149  side_loops[0].e = i_duplicate_edge;
1150  side_loops[1].v = new_vert;
1151  side_loops[1].e = connect_edge_range[i_extrude];
1152  side_loops[2].v = orig_vert;
1153  side_loops[2].e = orig_edge;
1154  side_loops[3].v = orig_vert_next;
1155  side_loops[3].e = connect_edge_range[i_extrude_next];
1156 
1157  connect_edges[i_extrude] = new_edge(orig_vert, new_vert);
1158  }
1159  }
1160  });
1161 
1162  MutableAttributeAccessor attributes = *component.attributes_for_write();
1163 
1164  attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
1165  GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
1166  id, meta_data.domain, meta_data.data_type);
1167  if (!attribute) {
1168  return true; /* Impossible to write the "normal" attribute. */
1169  }
1170 
1171  attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
1172  using T = decltype(dummy);
1173  MutableSpan<T> data = attribute.span.typed<T>();
1174  switch (attribute.domain) {
1175  case ATTR_DOMAIN_POINT: {
1176  /* New vertices copy the attributes from their original vertices. */
1177  MutableSpan<T> new_data = data.slice(new_vert_range);
1178 
1179  threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
1180  for (const int i_selection : range) {
1181  const MPoly &poly = polys[poly_selection[i_selection]];
1182  Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
1183 
1184  const int corner_offset = index_offsets[i_selection];
1185  for (const int i : poly_loops.index_range()) {
1186  const int orig_index = poly_loops[i].v;
1187  new_data[corner_offset + i] = data[orig_index];
1188  }
1189  }
1190  });
1191  break;
1192  }
1193  case ATTR_DOMAIN_EDGE: {
1194  MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
1195  MutableSpan<T> connect_data = data.slice(connect_edge_range);
1196 
1197  threading::parallel_for(poly_selection.index_range(), 512, [&](const IndexRange range) {
1198  for (const int i_selection : range) {
1199  const MPoly &poly = polys[poly_selection[i_selection]];
1200  Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
1201 
1202  const IndexRange poly_corner_range = selected_corner_range(index_offsets,
1203  i_selection);
1204 
1205  /* The data for the duplicate edge is simply a copy of the original edge's data. */
1206  for (const int i : poly_loops.index_range()) {
1207  const int orig_index = poly_loops[i].e;
1208  duplicate_data[poly_corner_range[i]] = data[orig_index];
1209  }
1210 
1211  /* For the extruded edges, mix the data from the two neighboring original edges of
1212  * the extruded polygon. */
1213  for (const int i : poly_loops.index_range()) {
1214  const int i_loop_prev = (i == 0) ? poly.totloop - 1 : i - 1;
1215  const int orig_index = poly_loops[i].e;
1216  const int orig_index_prev = poly_loops[i_loop_prev].e;
1217  if constexpr (std::is_same_v<T, bool>) {
1218  /* Propagate selections with "or" instead of "at least half". */
1219  connect_data[poly_corner_range[i]] = data[orig_index] || data[orig_index_prev];
1220  }
1221  else {
1222  connect_data[poly_corner_range[i]] = attribute_math::mix2(
1223  0.5f, data[orig_index], data[orig_index_prev]);
1224  }
1225  }
1226  }
1227  });
1228  break;
1229  }
1230  case ATTR_DOMAIN_FACE: {
1231  /* Each side face gets the values from the corresponding new face. */
1232  MutableSpan<T> new_data = data.slice(side_poly_range);
1233  threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
1234  for (const int i_selection : range) {
1235  const int poly_index = poly_selection[i_selection];
1236  const IndexRange poly_corner_range = selected_corner_range(index_offsets,
1237  i_selection);
1238  new_data.slice(poly_corner_range).fill(data[poly_index]);
1239  }
1240  });
1241  break;
1242  }
1243  case ATTR_DOMAIN_CORNER: {
1244  /* Each corner on a side face gets its value from the matching corner on an extruded
1245  * face. */
1246  MutableSpan<T> new_data = data.slice(side_loop_range);
1247  threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
1248  for (const int i_selection : range) {
1249  const MPoly &poly = polys[poly_selection[i_selection]];
1250  Span<T> poly_loop_data = data.slice(poly.loopstart, poly.totloop);
1251  const IndexRange poly_corner_range = selected_corner_range(index_offsets,
1252  i_selection);
1253 
1254  for (const int i : IndexRange(poly.totloop)) {
1255  const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
1256  const int i_extrude = poly_corner_range[i];
1257 
1258  MutableSpan<T> side_loop_data = new_data.slice(i_extrude * 4, 4);
1259 
1260  /* The two corners on each side of the side polygon get the data from the matching
1261  * corners of the extruded polygon. This order depends on the loop filling the loop
1262  * indices. */
1263  side_loop_data[0] = poly_loop_data[i_next];
1264  side_loop_data[1] = poly_loop_data[i];
1265  side_loop_data[2] = poly_loop_data[i];
1266  side_loop_data[3] = poly_loop_data[i_next];
1267  }
1268  }
1269  });
1270  break;
1271  }
1272  default:
1273  BLI_assert_unreachable();
1274  }
1275  });
1276 
1277  attribute.finish();
1278  return true;
1279  });
1280 
1281  /* Offset the new vertices. */
1282  threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
1283  for (const int i_selection : range) {
1284  const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
1285  for (MVert &vert : new_verts.slice(poly_corner_range)) {
1286  add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]);
1287  }
1288  }
1289  });
1290 
1291  MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
1292  vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
1293 
1294  MutableSpan<int> edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE);
1295  edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE);
1296  edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE);
1297 
1298  MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE);
1299  poly_orig_indices.slice(side_poly_range).fill(ORIGINDEX_NONE);
1300 
1301  /* Finally update each extruded polygon's loops to point to the new edges and vertices.
1302  * This must be done last, because they were used to find original indices for attribute
1303  * interpolation before. Alternatively an original index array could be built for each domain. */
1304  threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
1305  for (const int i_selection : range) {
1306  const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
1307 
1308  const MPoly &poly = polys[poly_selection[i_selection]];
1309  MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
1310 
1311  for (const int i : IndexRange(poly.totloop)) {
1312  MLoop &loop = poly_loops[i];
1313  loop.v = new_vert_range[poly_corner_range[i]];
1314  loop.e = duplicate_edge_range[poly_corner_range[i]];
1315  }
1316  }
1317  });
1318 
1319  if (attribute_outputs.top_id) {
1321  component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
1322  }
1323  if (attribute_outputs.side_id) {
1325  component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
1326  }
1327 
1329 }
1330 
1332 {
1333  GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
1334  Field<bool> selection = params.extract_input<Field<bool>>("Selection");
1335  Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
1336  Field<float> scale_field = params.extract_input<Field<float>>("Offset Scale");
1337  const NodeGeometryExtrudeMesh &storage = node_storage(params.node());
1338  GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
1339 
1340  /* Create a combined field from the offset and the scale so the field evaluator
1341  * can take care of the multiplication and to simplify each extrude function. */
1343  "Scale",
1344  [](const float3 &offset, const float scale) { return offset * scale; },
1346  std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>(
1347  FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)}));
1348  const Field<float3> final_offset{std::move(multiply_op)};
1349 
1350  AttributeOutputs attribute_outputs;
1351  if (params.output_is_required("Top")) {
1352  attribute_outputs.top_id = StrongAnonymousAttributeID("Top");
1353  }
1354  if (params.output_is_required("Side")) {
1355  attribute_outputs.side_id = StrongAnonymousAttributeID("Side");
1356  }
1357 
1358  const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES &&
1359  params.extract_input<bool>("Individual");
1360 
1361  geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
1362  if (geometry_set.has_mesh()) {
1364  switch (mode) {
1366  extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
1367  break;
1369  extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
1370  break;
1372  if (extrude_individual) {
1373  extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs);
1374  }
1375  else {
1376  extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
1377  }
1378  break;
1379  }
1380  }
1381 
1382  BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
1383  }
1384  });
1385 
1386  params.set_output("Mesh", std::move(geometry_set));
1387  if (attribute_outputs.top_id) {
1388  params.set_output("Top",
1389  AnonymousAttributeFieldInput::Create<bool>(
1390  std::move(attribute_outputs.top_id), params.attribute_producer_name()));
1391  }
1392  if (attribute_outputs.side_id) {
1393  params.set_output("Side",
1394  AnonymousAttributeFieldInput::Create<bool>(
1395  std::move(attribute_outputs.side_id), params.attribute_producer_name()));
1396  }
1397 }
1398 
1399 } // namespace blender::nodes::node_geo_extrude_mesh_cc
1400 
1402 {
1403  namespace file_ns = blender::nodes::node_geo_extrude_mesh_cc;
1404 
1405  static bNodeType ntype;
1412  &ntype, "NodeGeometryExtrudeMesh", node_free_standard_storage, node_copy_standard_storage);
1414  nodeRegisterType(&ntype);
1415 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:29
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:28
#define ORIGINDEX_NONE
void * CustomData_get_layer(const struct CustomData *data, int type)
void CustomData_realloc(struct CustomData *data, int totelem)
Definition: customdata.cc:2307
void * CustomData_duplicate_referenced_layer(struct CustomData *data, int type, int totelem)
Definition: customdata.cc:2976
void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
Definition: customdata.cc:3020
@ GEO_COMPONENT_TYPE_MESH
bool BKE_mesh_is_valid(struct Mesh *me)
void BKE_mesh_update_customdata_pointers(struct Mesh *me, bool do_ensure_tess_cd)
Definition: mesh.cc:874
void BKE_mesh_runtime_clear_cache(struct Mesh *mesh)
This function clears runtime cache of the given mesh.
Definition: mesh_runtime.cc:99
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
#define GEO_NODE_EXTRUDE_MESH
Definition: BKE_node.h:1493
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
#define NODE_CLASS_GEOMETRY
Definition: BKE_node.h:359
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define UNUSED(x)
#define ELEM(...)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
@ CD_ORIGINDEX
@ CD_MVERT
@ ME_EDGEDRAW
@ ME_EDGERENDER
@ ME_LOOSEEDGE
GeometryNodeExtrudeMeshMode
@ GEO_NODE_EXTRUDE_MESH_FACES
@ GEO_NODE_EXTRUDE_MESH_VERTICES
@ GEO_NODE_EXTRUDE_MESH_EDGES
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
@ PROP_TRANSLATION
Definition: RNA_types.h:154
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
const T & last(const int64_t n=0) const
Definition: BLI_array.hh:284
void reinitialize(const int64_t new_size)
Definition: BLI_array.hh:387
MutableSpan< T > as_mutable_span()
Definition: BLI_array.hh:236
int64_t size() const
bool is_empty() const
bool is_range() const
Span< int64_t > indices() const
IndexRange index_range() const
IndexRange as_range() const
constexpr int64_t size() const
constexpr IndexRange after(int64_t n) const
constexpr int64_t size() const
Definition: BLI_span.hh:511
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition: BLI_span.hh:581
constexpr IndexRange index_range() const
Definition: BLI_span.hh:661
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr const T & first() const
Definition: BLI_span.hh:303
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
Span< Key > as_span() const
int64_t index_of(const Key &key) const
void reserve(const int64_t n)
bool add(const Key &key)
void add_new(const Key &key)
int64_t index_of_try(const Key &key) const
IndexRange index_range() const
int64_t size() const
void append(const T &value)
Definition: BLI_vector.hh:433
int domain_size(const eAttrDomain domain) const
bool for_all(const AttributeForeachCallback fn) const
GSpanAttributeWriter lookup_or_add_for_write_span(const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefault())
OperationNode * node
SyclQueue void void * src
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_gpu_kernel_postfix int ccl_global int * indices
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
#define T
typename DefaultPropatationMixerStruct< T >::type DefaultPropatationMixer
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
AttributeAccessor mesh_attributes(const Mesh &mesh)
OwnedAnonymousAttributeID< true > StrongAnonymousAttributeID
static void duplicate_edges(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static VectorSet< int > vert_indices_from_edges(const Mesh &mesh, const Span< T > edge_indices)
static MutableSpan< MLoop > mesh_loops(Mesh &mesh)
static CustomData & get_customdata(Mesh &mesh, const eAttrDomain domain)
static void node_declare(NodeDeclarationBuilder &b)
static MutableSpan< MEdge > mesh_edges(Mesh &mesh)
static Span< MLoop > mesh_loops(const Mesh &mesh)
static Span< MPoly > mesh_polys(const Mesh &mesh)
static void save_selection_as_attribute(MeshComponent &component, const AnonymousAttributeID *id, const eAttrDomain domain, const IndexMask selection)
static MEdge new_loose_edge(const int v1, const int v2)
void copy_with_mixing(MutableSpan< T > dst, Span< T > src, GetMixIndicesFn get_mix_indices_fn)
static void fill_quad_consistent_direction(Span< MLoop > other_poly_loops, MutableSpan< MLoop > new_loops, const int vert_connected_to_poly_1, const int vert_connected_to_poly_2, const int vert_across_from_poly_1, const int vert_across_from_poly_2, const int edge_connected_to_poly, const int connecting_edge_1, const int edge_across_from_poly, const int connecting_edge_2)
static void extrude_mesh_vertices(MeshComponent &component, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs)
static Array< Vector< int, 2 > > mesh_calculate_polys_of_edge(const Mesh &mesh)
static IndexRange selected_corner_range(Span< int > offsets, const int index)
void copy_with_indices(MutableSpan< T > dst, Span< T > src, Span< int > indices)
static void expand_mesh(Mesh &mesh, const int vert_expand, const int edge_expand, const int poly_expand, const int loop_expand)
static MutableSpan< MPoly > mesh_polys(Mesh &mesh)
static MutableSpan< MVert > mesh_verts(Mesh &mesh)
static void extrude_mesh_edges(MeshComponent &component, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs)
static void node_update(bNodeTree *ntree, bNode *node)
static Array< Vector< int > > create_vert_to_edge_map(const int vert_size, Span< MEdge > edges, const int vert_offset=0)
static MutableSpan< int > get_orig_index_layer(Mesh &mesh, const eAttrDomain domain)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
void copy_with_mask(MutableSpan< T > dst, Span< T > src, IndexMask mask)
static void node_geo_exec(GeoNodeExecParams params)
static void extrude_mesh_face_regions(MeshComponent &component, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs)
static MPoly new_poly(const int loopstart, const int totloop)
static void extrude_individual_mesh_faces(MeshComponent &component, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static MEdge new_edge(const int v1, const int v2)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
Definition: node.cc:1082
void register_node_type_geo_extrude_mesh()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
void modify_geometry_sets(ForeachSubGeometryCallback callback)
bool has_mesh() const
unsigned int v1
unsigned int v2
struct MEdge * medge
CustomData vdata
struct MVert * mvert
float size[3]
int totedge
int totvert
struct MLoop * mloop
CustomData pdata
int totpoly
CustomData edata
int totloop
struct MPoly * mpoly
CustomData ldata
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480