Blender  V3.3
node_geo_mesh_primitive_cone.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "DNA_mesh_types.h"
4 #include "DNA_meshdata_types.h"
5 
6 #include "BKE_material.h"
7 #include "BKE_mesh.h"
8 
9 #include "UI_interface.h"
10 #include "UI_resources.h"
11 
12 #include "node_geometry_util.hh"
13 
14 #include <cmath>
15 
16 namespace blender::nodes {
17 
18 struct ConeConfig {
19  float radius_top;
21  float height;
26 
29  /* The cone tip and a triangle fan filling are topologically identical.
30  * This simplifies the logic in some cases. */
33 
34  /* Helpful quantities. */
37  int tot_verts;
38  int tot_edges;
40  int tot_faces;
41 
42  /* Helpful vertex indices. */
46  int last_vert;
47 
48  /* Helpful edge indices. */
52  int last_edge;
53 
54  /* Helpful face indices. */
61 
63  float radius_bottom,
64  float depth,
65  int circle_segments,
66  int side_segments,
67  int fill_segments,
71  height(0.5f * depth),
76  {
77  this->top_is_point = this->radius_top == 0.0f;
78  this->bottom_is_point = this->radius_bottom == 0.0f;
79  this->top_has_center_vert = this->top_is_point ||
80  this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
81  this->bottom_has_center_vert = this->bottom_is_point ||
82  this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
83 
84  this->tot_quad_rings = this->calculate_total_quad_rings();
85  this->tot_edge_rings = this->calculate_total_edge_rings();
86  this->tot_verts = this->calculate_total_verts();
87  this->tot_edges = this->calculate_total_edges();
88  this->tot_corners = this->calculate_total_corners();
89 
90  this->first_vert = 0;
91  this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert;
92  this->last_vert = this->tot_verts - 1;
93  this->last_ring_verts_start = this->last_vert - this->circle_segments;
94 
95  this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0;
96  this->last_ring_edges_start = this->first_ring_edges_start +
97  this->tot_quad_rings * this->circle_segments * 2;
98  this->last_fan_edges_start = this->tot_edges - this->circle_segments;
99  this->last_edge = this->tot_edges - 1;
100 
101  this->top_faces_start = 0;
102  if (!this->top_is_point) {
103  this->top_faces_len = (fill_segments - 1) * circle_segments;
104  this->top_faces_len += this->top_has_center_vert ? circle_segments : 0;
105  this->top_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0;
106  }
107  else {
108  this->top_faces_len = 0;
109  }
110 
111  this->side_faces_start = this->top_faces_len;
112  if (this->top_is_point && this->bottom_is_point) {
113  this->side_faces_len = 0;
114  }
115  else {
116  this->side_faces_len = side_segments * circle_segments;
117  }
118 
119  if (!this->bottom_is_point) {
120  this->bottom_faces_len = (fill_segments - 1) * circle_segments;
121  this->bottom_faces_len += this->bottom_has_center_vert ? circle_segments : 0;
122  this->bottom_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0;
123  }
124  else {
125  this->bottom_faces_len = 0;
126  }
127  this->bottom_faces_start = this->side_faces_start + this->side_faces_len;
128 
129  this->tot_faces = this->top_faces_len + this->side_faces_len + this->bottom_faces_len;
130  }
131 
132  private:
133  int calculate_total_quad_rings();
134  int calculate_total_edge_rings();
135  int calculate_total_verts();
136  int calculate_total_edges();
137  int calculate_total_corners();
138 };
139 
140 int ConeConfig::calculate_total_quad_rings()
141 {
142  if (top_is_point && bottom_is_point) {
143  return 0;
144  }
145 
146  int quad_rings = 0;
147 
148  if (!top_is_point) {
149  quad_rings += fill_segments - 1;
150  }
151 
152  quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1);
153 
154  if (!bottom_is_point) {
155  quad_rings += fill_segments - 1;
156  }
157 
158  return quad_rings;
159 }
160 
161 int ConeConfig::calculate_total_edge_rings()
162 {
163  if (top_is_point && bottom_is_point) {
164  return 0;
165  }
166 
167  int edge_rings = 0;
168 
169  if (!top_is_point) {
170  edge_rings += fill_segments;
171  }
172 
173  edge_rings += side_segments - 1;
174 
175  if (!bottom_is_point) {
176  edge_rings += fill_segments;
177  }
178 
179  return edge_rings;
180 }
181 
182 int ConeConfig::calculate_total_verts()
183 {
184  if (top_is_point && bottom_is_point) {
185  return side_segments + 1;
186  }
187 
188  int vert_total = 0;
189 
190  if (top_has_center_vert) {
191  vert_total++;
192  }
193 
194  if (!top_is_point) {
195  vert_total += circle_segments * fill_segments;
196  }
197 
198  vert_total += circle_segments * (side_segments - 1);
199 
200  if (!bottom_is_point) {
201  vert_total += circle_segments * fill_segments;
202  }
203 
205  vert_total++;
206  }
207 
208  return vert_total;
209 }
210 
211 int ConeConfig::calculate_total_edges()
212 {
213  if (top_is_point && bottom_is_point) {
214  return side_segments;
215  }
216 
217  int edge_total = 0;
218  if (top_has_center_vert) {
219  edge_total += circle_segments;
220  }
221 
222  edge_total += circle_segments * (tot_quad_rings * 2 + 1);
223 
225  edge_total += circle_segments;
226  }
227 
228  return edge_total;
229 }
230 
231 int ConeConfig::calculate_total_corners()
232 {
233  if (top_is_point && bottom_is_point) {
234  return 0;
235  }
236 
237  int corner_total = 0;
238 
239  if (top_has_center_vert) {
240  corner_total += (circle_segments * 3);
241  }
243  corner_total += circle_segments;
244  }
245 
246  corner_total += tot_quad_rings * (circle_segments * 4);
247 
249  corner_total += (circle_segments * 3);
250  }
252  corner_total += circle_segments;
253  }
254 
255  return corner_total;
256 }
257 
258 static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
259 {
260  Array<float2> circle(config.circle_segments);
261  const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
262  float angle = 0.0f;
263  for (const int i : IndexRange(config.circle_segments)) {
264  circle[i].x = std::cos(angle);
265  circle[i].y = std::sin(angle);
266  angle += angle_delta;
267  }
268 
269  int vert_index = 0;
270 
271  /* Top cone tip or triangle fan center. */
272  if (config.top_has_center_vert) {
273  copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height);
274  }
275 
276  /* Top fill including the outer edge of the fill. */
277  if (!config.top_is_point) {
278  const float top_fill_radius_delta = config.radius_top /
279  static_cast<float>(config.fill_segments);
280  for (const int i : IndexRange(config.fill_segments)) {
281  const float top_fill_radius = top_fill_radius_delta * (i + 1);
282  for (const int j : IndexRange(config.circle_segments)) {
283  const float x = circle[j].x * top_fill_radius;
284  const float y = circle[j].y * top_fill_radius;
285  copy_v3_fl3(verts[vert_index++].co, x, y, config.height);
286  }
287  }
288  }
289 
290  /* Rings along the side. */
291  const float side_radius_delta = (config.radius_bottom - config.radius_top) /
292  static_cast<float>(config.side_segments);
293  const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments);
294  for (const int i : IndexRange(config.side_segments - 1)) {
295  const float ring_radius = config.radius_top + (side_radius_delta * (i + 1));
296  const float ring_height = config.height - (height_delta * (i + 1));
297  for (const int j : IndexRange(config.circle_segments)) {
298  const float x = circle[j].x * ring_radius;
299  const float y = circle[j].y * ring_radius;
300  copy_v3_fl3(verts[vert_index++].co, x, y, ring_height);
301  }
302  }
303 
304  /* Bottom fill including the outer edge of the fill. */
305  if (!config.bottom_is_point) {
306  const float bottom_fill_radius_delta = config.radius_bottom /
307  static_cast<float>(config.fill_segments);
308  for (const int i : IndexRange(config.fill_segments)) {
309  const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta);
310  for (const int j : IndexRange(config.circle_segments)) {
311  const float x = circle[j].x * bottom_fill_radius;
312  const float y = circle[j].y * bottom_fill_radius;
313  copy_v3_fl3(verts[vert_index++].co, x, y, -config.height);
314  }
315  }
316  }
317 
318  /* Bottom cone tip or triangle fan center. */
319  if (config.bottom_has_center_vert) {
320  copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height);
321  }
322 }
323 
324 static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConfig &config)
325 {
326  int edge_index = 0;
327 
328  /* Edges for top cone tip or triangle fan */
329  if (config.top_has_center_vert) {
330  for (const int i : IndexRange(config.circle_segments)) {
331  MEdge &edge = edges[edge_index++];
332  edge.v1 = config.first_vert;
333  edge.v2 = config.first_ring_verts_start + i;
334  edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
335  }
336  }
337 
338  /* Rings and connecting edges between the rings. */
339  for (const int i : IndexRange(config.tot_edge_rings)) {
340  const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments);
341  const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
342  /* Edge rings. */
343  for (const int j : IndexRange(config.circle_segments)) {
344  MEdge &edge = edges[edge_index++];
345  edge.v1 = this_ring_vert_start + j;
346  edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments);
347  edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
348  }
349  if (i == config.tot_edge_rings - 1) {
350  /* There is one fewer ring of connecting edges. */
351  break;
352  }
353  /* Connecting edges. */
354  for (const int j : IndexRange(config.circle_segments)) {
355  MEdge &edge = edges[edge_index++];
356  edge.v1 = this_ring_vert_start + j;
357  edge.v2 = next_ring_vert_start + j;
358  edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
359  }
360  }
361 
362  /* Edges for bottom triangle fan or tip. */
363  if (config.bottom_has_center_vert) {
364  for (const int i : IndexRange(config.circle_segments)) {
365  MEdge &edge = edges[edge_index++];
366  edge.v1 = config.last_ring_verts_start + i;
367  edge.v2 = config.last_vert;
368  edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
369  }
370  }
371 }
372 
373 static void calculate_cone_faces(const MutableSpan<MLoop> &loops,
374  const MutableSpan<MPoly> &polys,
375  const ConeConfig &config)
376 {
377  int loop_index = 0;
378  int poly_index = 0;
379 
380  if (config.top_has_center_vert) {
381  /* Top cone tip or center triangle fan in the fill. */
382  const int top_center_vert = 0;
383  const int top_fan_edges_start = 0;
384 
385  for (const int i : IndexRange(config.circle_segments)) {
386  MPoly &poly = polys[poly_index++];
387  poly.loopstart = loop_index;
388  poly.totloop = 3;
389 
390  MLoop &loop_a = loops[loop_index++];
391  loop_a.v = config.first_ring_verts_start + i;
392  loop_a.e = config.first_ring_edges_start + i;
393  MLoop &loop_b = loops[loop_index++];
394  loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments);
395  loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments);
396  MLoop &loop_c = loops[loop_index++];
397  loop_c.v = top_center_vert;
398  loop_c.e = top_fan_edges_start + i;
399  }
400  }
401  else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
402  /* Center n-gon in the fill. */
403  MPoly &poly = polys[poly_index++];
404  poly.loopstart = loop_index;
405  poly.totloop = config.circle_segments;
406  for (const int i : IndexRange(config.circle_segments)) {
407  MLoop &loop = loops[loop_index++];
408  loop.v = i;
409  loop.e = i;
410  }
411  }
412 
413  /* Quads connect one edge ring to the next one. */
414  if (config.tot_quad_rings > 0) {
415  for (const int i : IndexRange(config.tot_quad_rings)) {
416  const int this_ring_vert_start = config.first_ring_verts_start +
417  (i * config.circle_segments);
418  const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
419 
420  const int this_ring_edges_start = config.first_ring_edges_start +
421  (i * 2 * config.circle_segments);
422  const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments);
423  const int ring_connections_start = this_ring_edges_start + config.circle_segments;
424 
425  for (const int j : IndexRange(config.circle_segments)) {
426  MPoly &poly = polys[poly_index++];
427  poly.loopstart = loop_index;
428  poly.totloop = 4;
429 
430  MLoop &loop_a = loops[loop_index++];
431  loop_a.v = this_ring_vert_start + j;
432  loop_a.e = ring_connections_start + j;
433  MLoop &loop_b = loops[loop_index++];
434  loop_b.v = next_ring_vert_start + j;
435  loop_b.e = next_ring_edges_start + j;
436  MLoop &loop_c = loops[loop_index++];
437  loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments);
438  loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments);
439  MLoop &loop_d = loops[loop_index++];
440  loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments);
441  loop_d.e = this_ring_edges_start + j;
442  }
443  }
444  }
445 
446  if (config.bottom_has_center_vert) {
447  /* Bottom cone tip or center triangle fan in the fill. */
448  for (const int i : IndexRange(config.circle_segments)) {
449  MPoly &poly = polys[poly_index++];
450  poly.loopstart = loop_index;
451  poly.totloop = 3;
452 
453  MLoop &loop_a = loops[loop_index++];
454  loop_a.v = config.last_ring_verts_start + i;
455  loop_a.e = config.last_fan_edges_start + i;
456  MLoop &loop_b = loops[loop_index++];
457  loop_b.v = config.last_vert;
458  loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments;
459  MLoop &loop_c = loops[loop_index++];
460  loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments;
461  loop_c.e = config.last_ring_edges_start + i;
462  }
463  }
464  else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
465  /* Center n-gon in the fill. */
466  MPoly &poly = polys[poly_index++];
467  poly.loopstart = loop_index;
468  poly.totloop = config.circle_segments;
469 
470  for (const int i : IndexRange(config.circle_segments)) {
471  /* Go backwards to reverse surface normal. */
472  MLoop &loop = loops[loop_index++];
473  loop.v = config.last_vert - i;
474  loop.e = config.last_edge - ((i + 1) % config.circle_segments);
475  }
476  }
477 }
478 
480  const ConeConfig &config,
481  ConeAttributeOutputs &attribute_outputs)
482 {
484 
485  /* Populate "Top" selection output. */
486  if (attribute_outputs.top_id) {
487  const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
489  attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
490 
491  if (config.top_is_point) {
492  selection.span[config.first_vert] = true;
493  }
494  else {
495  selection.span.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
496  }
497  selection.finish();
498  }
499 
500  /* Populate "Bottom" selection output. */
501  if (attribute_outputs.bottom_id) {
502  const bool face = !config.bottom_is_point &&
505  attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
506 
507  if (config.bottom_is_point) {
508  selection.span[config.last_vert] = true;
509  }
510  else if (face) {
511  selection.span.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
512  }
513  else {
514  selection.span.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
515  }
516  selection.finish();
517  }
518 
519  /* Populate "Side" selection output. */
520  if (attribute_outputs.side_id) {
522  attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
523 
524  selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
525  selection.finish();
526  }
527 }
528 
537 static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
538 {
540 
542  "uv_map", ATTR_DOMAIN_CORNER);
543  MutableSpan<float2> uvs = uv_attribute.span;
544 
545  Array<float2> circle(config.circle_segments);
546  float angle = 0.0f;
547  const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments);
548  for (const int i : IndexRange(config.circle_segments)) {
549  circle[i].x = std::cos(angle) * 0.225f;
550  circle[i].y = std::sin(angle) * 0.225f;
551  angle += angle_delta;
552  }
553 
554  int loop_index = 0;
555 
556  /* Left circle of the UV representing the top fill or top cone tip. */
557  if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
558  const float2 center_left(0.25f, 0.25f);
559  const float radius_factor_delta = 1.0f / (config.top_is_point ?
560  static_cast<float>(config.side_segments) :
561  static_cast<float>(config.fill_segments));
562  const int left_circle_segment_count = config.top_is_point ? config.side_segments :
563  config.fill_segments;
564 
565  if (config.top_has_center_vert) {
566  /* Cone tip itself or triangle fan center of the fill. */
567  for (const int i : IndexRange(config.circle_segments)) {
568  uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
569  uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
570  center_left;
571  uvs[loop_index++] = center_left;
572  }
573  }
574  else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
575  /* N-gon at the center of the fill. */
576  for (const int i : IndexRange(config.circle_segments)) {
577  uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
578  }
579  }
580  /* The rest of the top fill is made out of quad rings. */
581  for (const int i : IndexRange(1, left_circle_segment_count - 1)) {
582  const float inner_radius_factor = i * radius_factor_delta;
583  const float outer_radius_factor = (i + 1) * radius_factor_delta;
584  for (const int j : IndexRange(config.circle_segments)) {
585  uvs[loop_index++] = inner_radius_factor * circle[j] + center_left;
586  uvs[loop_index++] = outer_radius_factor * circle[j] + center_left;
587  uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
588  center_left;
589  uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
590  center_left;
591  }
592  }
593  }
594 
595  if (!config.top_is_point && !config.bottom_is_point) {
596  /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */
597  const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
598  const float x_delta = 1.0f / static_cast<float>(config.circle_segments);
599  const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments);
600 
601  for (const int i : IndexRange(config.side_segments)) {
602  for (const int j : IndexRange(config.circle_segments)) {
603  uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom);
604  uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom);
605  uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom);
606  uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom);
607  }
608  }
609  }
610 
611  /* Right circle of the UV representing the bottom fill or bottom cone tip. */
612  if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
613  const float2 center_right(0.75f, 0.25f);
614  const float radius_factor_delta = 1.0f / (config.bottom_is_point ?
615  static_cast<float>(config.side_segments) :
616  static_cast<float>(config.fill_segments));
617  const int right_circle_segment_count = config.bottom_is_point ? config.side_segments :
618  config.fill_segments;
619 
620  /* The bottom circle has to be created outside in to match the loop order. */
621  for (const int i : IndexRange(right_circle_segment_count - 1)) {
622  const float outer_radius_factor = 1.0f - i * radius_factor_delta;
623  const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta;
624  for (const int j : IndexRange(config.circle_segments)) {
625  uvs[loop_index++] = outer_radius_factor * circle[j] + center_right;
626  uvs[loop_index++] = inner_radius_factor * circle[j] + center_right;
627  uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
628  center_right;
629  uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
630  center_right;
631  }
632  }
633 
634  if (config.bottom_has_center_vert) {
635  /* Cone tip itself or triangle fan center of the fill. */
636  for (const int i : IndexRange(config.circle_segments)) {
637  uvs[loop_index++] = radius_factor_delta * circle[i] + center_right;
638  uvs[loop_index++] = center_right;
639  uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
640  center_right;
641  }
642  }
643  else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
644  /* N-gon at the center of the fill. */
645  for (const int i : IndexRange(config.circle_segments)) {
646  /* Go backwards because of reversed face normal. */
647  uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] +
648  center_right;
649  }
650  }
651  }
652 
653  uv_attribute.finish();
654 }
655 
657 {
658  /* Returns a mesh with a single vertex at the origin. */
659  Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
660  copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
661  return mesh;
662 }
663 
664 Mesh *create_cylinder_or_cone_mesh(const float radius_top,
665  const float radius_bottom,
666  const float depth,
667  const int circle_segments,
668  const int side_segments,
669  const int fill_segments,
670  const GeometryNodeMeshCircleFillType fill_type,
671  ConeAttributeOutputs &attribute_outputs)
672 {
673  const ConeConfig config(
674  radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type);
675 
676  /* Handle the case of a line / single point before everything else to avoid
677  * the need to check for it later. */
678  if (config.top_is_point && config.bottom_is_point) {
679  if (config.height == 0.0f) {
680  return create_vertex_mesh();
681  }
682  const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments);
683  const float3 start(0.0f, 0.0f, config.height);
684  const float3 delta(0.0f, 0.0f, z_delta);
685  return create_line_mesh(start, delta, config.tot_verts);
686  }
687 
689  config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces);
691 
696 
698  calculate_cone_edges(edges, config);
699  calculate_cone_faces(loops, polys, config);
700  calculate_cone_uvs(mesh, config);
701  calculate_selection_outputs(mesh, config, attribute_outputs);
702 
703  return mesh;
704 }
705 
706 } // namespace blender::nodes
707 
709 
711 
713 {
714  b.add_input<decl::Int>(N_("Vertices"))
715  .default_value(32)
716  .min(3)
717  .max(512)
718  .description(N_("Number of points on the circle at the top and bottom"));
719  b.add_input<decl::Int>(N_("Side Segments"))
720  .default_value(1)
721  .min(1)
722  .max(512)
723  .description(N_("The number of edges running vertically along the side of the cone"));
724  b.add_input<decl::Int>(N_("Fill Segments"))
725  .default_value(1)
726  .min(1)
727  .max(512)
728  .description(N_("Number of concentric rings used to fill the round face"));
729  b.add_input<decl::Float>(N_("Radius Top"))
730  .min(0.0f)
731  .subtype(PROP_DISTANCE)
732  .description(N_("Radius of the top circle of the cone"));
733  b.add_input<decl::Float>(N_("Radius Bottom"))
734  .default_value(1.0f)
735  .min(0.0f)
736  .subtype(PROP_DISTANCE)
737  .description(N_("Radius of the bottom circle of the cone"));
738  b.add_input<decl::Float>(N_("Depth"))
739  .default_value(2.0f)
740  .min(0.0f)
741  .subtype(PROP_DISTANCE)
742  .description(N_("Height of the generated cone"));
743  b.add_output<decl::Geometry>(N_("Mesh"));
744  b.add_output<decl::Bool>(N_("Top")).field_source();
745  b.add_output<decl::Bool>(N_("Bottom")).field_source();
746  b.add_output<decl::Bool>(N_("Side")).field_source();
747 }
748 
750 {
751  NodeGeometryMeshCone *node_storage = MEM_cnew<NodeGeometryMeshCone>(__func__);
752 
754 
755  node->storage = node_storage;
756 }
757 
759 {
760  bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
761  bNodeSocket *rings_socket = vertices_socket->next;
762  bNodeSocket *fill_subdiv_socket = rings_socket->next;
763 
764  const NodeGeometryMeshCone &storage = node_storage(*node);
766  const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE;
767  nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill);
768 }
769 
770 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
771 {
772  uiLayoutSetPropSep(layout, true);
773  uiLayoutSetPropDecorate(layout, false);
774  uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
775 }
776 
778 {
779  const NodeGeometryMeshCone &storage = node_storage(params.node());
781 
782  const int circle_segments = params.extract_input<int>("Vertices");
783  if (circle_segments < 3) {
784  params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
785  params.set_default_remaining_outputs();
786  return;
787  }
788 
789  const int side_segments = params.extract_input<int>("Side Segments");
790  if (side_segments < 1) {
791  params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
792  params.set_default_remaining_outputs();
793  return;
794  }
795 
796  const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE;
797  const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
798  if (fill_segments < 1) {
799  params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
800  params.set_default_remaining_outputs();
801  return;
802  }
803 
804  const float radius_top = params.extract_input<float>("Radius Top");
805  const float radius_bottom = params.extract_input<float>("Radius Bottom");
806  const float depth = params.extract_input<float>("Depth");
807 
808  ConeAttributeOutputs attribute_outputs;
809  if (params.output_is_required("Top")) {
810  attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection");
811  }
812  if (params.output_is_required("Bottom")) {
813  attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection");
814  }
815  if (params.output_is_required("Side")) {
816  attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection");
817  }
818 
819  Mesh *mesh = create_cylinder_or_cone_mesh(radius_top,
820  radius_bottom,
821  depth,
822  circle_segments,
823  side_segments,
824  fill_segments,
825  fill,
826  attribute_outputs);
827 
828  /* Transform the mesh so that the base of the cone is at the origin. */
829  BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
830 
831  if (attribute_outputs.top_id) {
832  params.set_output("Top",
833  AnonymousAttributeFieldInput::Create<bool>(
834  std::move(attribute_outputs.top_id), params.attribute_producer_name()));
835  }
836  if (attribute_outputs.bottom_id) {
837  params.set_output(
838  "Bottom",
839  AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
840  params.attribute_producer_name()));
841  }
842  if (attribute_outputs.side_id) {
843  params.set_output("Side",
844  AnonymousAttributeFieldInput::Create<bool>(
845  std::move(attribute_outputs.side_id), params.attribute_producer_name()));
846  }
847 
848  params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
849 }
850 
851 } // namespace blender::nodes::node_geo_mesh_primitive_cone_cc
852 
854 {
856 
857  static bNodeType ntype;
858 
863  &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage);
867  nodeRegisterType(&ntype);
868 }
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:29
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
General operations, lookup, etc. for materials.
void BKE_id_material_eval_ensure_default_slot(struct ID *id)
Definition: material.c:784
void BKE_mesh_translate(struct Mesh *me, const float offset[3], bool do_keys)
Definition: mesh.cc:1632
struct Mesh * BKE_mesh_new_nomain(int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
Definition: mesh.cc:991
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_MESH_PRIMITIVE_CONE
Definition: BKE_node.h:1396
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 M_PI
Definition: BLI_math_base.h:20
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
#define UNUSED(x)
#define TIP_(msgid)
@ ME_EDGEDRAW
@ ME_EDGERENDER
GeometryNodeMeshCircleFillType
@ GEO_NODE_MESH_CIRCLE_FILL_NGON
@ GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN
@ GEO_NODE_MESH_CIRCLE_FILL_NONE
_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 y
_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 bottom
in reality light always falls off quadratically Particle Info
@ PROP_DISTANCE
Definition: RNA_types.h:149
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)
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type)
OperationNode * node
bNodeTree * ntree
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh)
OwnedAnonymousAttributeID< true > StrongAnonymousAttributeID
static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_update(bNodeTree *ntree, bNode *node)
static void calculate_cone_vertices(const MutableSpan< MVert > &verts, const ConeConfig &config)
Mesh * create_line_mesh(const float3 start, const float3 delta, int count)
static void calculate_cone_faces(const MutableSpan< MLoop > &loops, const MutableSpan< MPoly > &polys, const ConeConfig &config)
static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
static void calculate_cone_edges(const MutableSpan< MEdge > &edges, const ConeConfig &config)
static void calculate_selection_outputs(Mesh *mesh, const ConeConfig &config, ConeAttributeOutputs &attribute_outputs)
Mesh * create_cylinder_or_cone_mesh(float radius_top, float radius_bottom, float depth, int circle_segments, int side_segments, int fill_segments, GeometryNodeMeshCircleFillType fill_type, ConeAttributeOutputs &attribute_outputs)
static Mesh * create_vertex_mesh()
vec_base< float, 3 > float3
vec_base< float, 2 > float2
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_mesh_primitive_cone()
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
#define min(a, b)
Definition: sort.c:35
static GeometrySet create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
unsigned int e
unsigned int v
float co[3]
struct MEdge * medge
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
struct bNodeSocket * next
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
StrongAnonymousAttributeID bottom_id
StrongAnonymousAttributeID side_id
ConeConfig(float radius_top, float radius_bottom, float depth, int circle_segments, int side_segments, int fill_segments, GeometryNodeMeshCircleFillType fill_type)
GeometryNodeMeshCircleFillType fill_type
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480