Blender  V3.3
node_geo_convex_hull.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 #include "DNA_pointcloud_types.h"
6 
7 #include "BKE_curves.hh"
8 #include "BKE_material.h"
9 #include "BKE_mesh.h"
10 
11 #include "node_geometry_util.hh"
12 
13 #ifdef WITH_BULLET
14 # include "RBI_hull_api.h"
15 #endif
16 
18 
20 {
21  b.add_input<decl::Geometry>(N_("Geometry"));
22  b.add_output<decl::Geometry>(N_("Convex Hull"));
23 }
24 
25 #ifdef WITH_BULLET
26 
27 static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
28 {
29  plConvexHull hull = plConvexHullCompute((float(*)[3])coords.data(), coords.size());
30 
31  const int verts_num = plConvexHullNumVertices(hull);
32  const int faces_num = verts_num <= 2 ? 0 : plConvexHullNumFaces(hull);
33  const int loops_num = verts_num <= 2 ? 0 : plConvexHullNumLoops(hull);
34  /* Half as many edges as loops, because the mesh is manifold. */
35  const int edges_num = verts_num == 2 ? 1 : verts_num < 2 ? 0 : loops_num / 2;
36 
37  /* Create Mesh *result with proper capacity. */
38  Mesh *result;
39  if (mesh) {
41  mesh, verts_num, edges_num, 0, loops_num, faces_num);
42  }
43  else {
44  result = BKE_mesh_new_nomain(verts_num, edges_num, 0, loops_num, faces_num);
46  }
47 
48  /* Copy vertices. */
49  for (const int i : IndexRange(verts_num)) {
50  float co[3];
51  int original_index;
52  plConvexHullGetVertex(hull, i, co, &original_index);
53 
54  if (original_index >= 0 && original_index < coords.size()) {
55 # if 0 /* Disabled because it only works for meshes, not predictable enough. */
56  /* Copy custom data on vertices, like vertex groups etc. */
57  if (mesh && original_index < mesh->totvert) {
58  CustomData_copy_data(&mesh->vdata, &result->vdata, (int)original_index, (int)i, 1);
59  }
60 # endif
61  /* Copy the position of the original point. */
62  copy_v3_v3(result->mvert[i].co, co);
63  }
64  else {
65  BLI_assert_msg(0, "Unexpected new vertex in hull output");
66  }
67  }
68 
69  /* Copy edges and loops. */
70 
71  /* NOTE: ConvexHull from Bullet uses a half-edge data structure
72  * for its mesh. To convert that, each half-edge needs to be converted
73  * to a loop and edges need to be created from that. */
74  Array<MLoop> mloop_src(loops_num);
75  uint edge_index = 0;
76  for (const int i : IndexRange(loops_num)) {
77  int v_from;
78  int v_to;
79  plConvexHullGetLoop(hull, i, &v_from, &v_to);
80 
81  mloop_src[i].v = (uint)v_from;
82  /* Add edges for ascending order loops only. */
83  if (v_from < v_to) {
84  MEdge &edge = result->medge[edge_index];
85  edge.v1 = v_from;
86  edge.v2 = v_to;
88 
89  /* Write edge index into both loops that have it. */
90  int reverse_index = plConvexHullGetReversedLoopIndex(hull, i);
91  mloop_src[i].e = edge_index;
92  mloop_src[reverse_index].e = edge_index;
93  edge_index++;
94  }
95  }
96  if (edges_num == 1) {
97  /* In this case there are no loops. */
98  MEdge &edge = result->medge[0];
99  edge.v1 = 0;
100  edge.v2 = 1;
102  edge_index++;
103  }
104  BLI_assert(edge_index == edges_num);
105 
106  /* Copy faces. */
107  Array<int> loops;
108  int j = 0;
109  MLoop *loop = result->mloop;
110  for (const int i : IndexRange(faces_num)) {
111  const int len = plConvexHullGetFaceSize(hull, i);
112 
113  BLI_assert(len > 2);
114 
115  /* Get face loop indices. */
116  loops.reinitialize(len);
117  plConvexHullGetFaceLoops(hull, i, loops.data());
118 
119  MPoly &face = result->mpoly[i];
120  face.loopstart = j;
121  face.totloop = len;
122  for (const int k : IndexRange(len)) {
123  MLoop &src_loop = mloop_src[loops[k]];
124  loop->v = src_loop.v;
125  loop->e = src_loop.e;
126  loop++;
127  }
128  j += len;
129  }
130 
131  plConvexHullDelete(hull);
132  return result;
133 }
134 
135 static Mesh *compute_hull(const GeometrySet &geometry_set)
136 {
137  int span_count = 0;
138  int count = 0;
139  int total_num = 0;
140 
141  Span<float3> positions_span;
142 
143  if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
144  count++;
145  if (const VArray<float3> positions = component->attributes()->lookup<float3>(
146  "position", ATTR_DOMAIN_POINT)) {
147  if (positions.is_span()) {
148  span_count++;
149  positions_span = positions.get_internal_span();
150  }
151  total_num += positions.size();
152  }
153  }
154 
155  if (const PointCloudComponent *component =
157  count++;
158  if (const VArray<float3> positions = component->attributes()->lookup<float3>(
159  "position", ATTR_DOMAIN_POINT)) {
160  if (positions.is_span()) {
161  span_count++;
162  positions_span = positions.get_internal_span();
163  }
164  total_num += positions.size();
165  }
166  }
167 
168  if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
169  count++;
170  span_count++;
171  const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
172  positions_span = curves.evaluated_positions();
173  total_num += positions_span.size();
174  }
175 
176  if (count == 0) {
177  return nullptr;
178  }
179 
180  /* If there is only one positions virtual array and it is already contiguous, avoid copying
181  * all of the positions and instead pass the span directly to the convex hull function. */
182  if (span_count == 1 && count == 1) {
183  return hull_from_bullet(nullptr, positions_span);
184  }
185 
186  Array<float3> positions(total_num);
187  int offset = 0;
188 
189  if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
190  if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
192  varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
193  offset += varray.size();
194  }
195  }
196 
197  if (const PointCloudComponent *component =
199  if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
201  varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
202  offset += varray.size();
203  }
204  }
205 
206  if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
207  const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
208  Span<float3> array = curves.evaluated_positions();
209  positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
210  offset += array.size();
211  }
212 
213  return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
214 }
215 
216 #endif /* WITH_BULLET */
217 
219 {
220  GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
221 
222 #ifdef WITH_BULLET
223 
224  geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
225  Mesh *mesh = compute_hull(geometry_set);
226  geometry_set.replace_mesh(mesh);
228  });
229 
230  params.set_output("Convex Hull", std::move(geometry_set));
231 #else
232  params.error_message_add(NodeWarningType::Error,
233  TIP_("Disabled, Blender was compiled without Bullet"));
234  params.set_default_remaining_outputs();
235 #endif /* WITH_BULLET */
236 }
237 
238 } // namespace blender::nodes::node_geo_convex_hull_cc
239 
241 {
242  namespace file_ns = blender::nodes::node_geo_convex_hull_cc;
243 
244  static bNodeType ntype;
245 
249  nodeRegisterType(&ntype);
250 }
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
Low-level operations for curves.
void CustomData_copy_data(const struct CustomData *source, struct CustomData *dest, int source_index, int dest_index, int count)
@ GEO_COMPONENT_TYPE_MESH
General operations, lookup, etc. for materials.
void BKE_id_material_eval_ensure_default_slot(struct ID *id)
Definition: material.c:784
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
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
#define NODE_CLASS_GEOMETRY
Definition: BKE_node.h:359
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define GEO_NODE_CONVEX_HULL
Definition: BKE_node.h:1406
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
MINLINE void copy_v3_v3(float r[3], const float a[3])
unsigned int uint
Definition: BLI_sys_types.h:67
#define TIP_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
struct CurvesGeometry CurvesGeometry
@ ME_EDGEDRAW
@ ME_EDGERENDER
@ ME_LOOSEEDGE
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to curves
size_t size() const
constexpr const T * data() const
Definition: BLI_span.hh:203
constexpr int64_t size() const
Definition: BLI_span.hh:240
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
int len
Definition: draw_manager.c:108
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void register_node_type_geo_convex_hull()
MutableSpan< float3 > positions
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
int plConvexHullGetFaceSize(plConvexHull hull, int n)
int plConvexHullNumLoops(plConvexHull hull)
void plConvexHullGetVertex(plConvexHull hull, int n, float coords[3], int *original_index)
void plConvexHullGetLoop(plConvexHull hull, int n, int *v_from, int *v_to)
int plConvexHullNumVertices(plConvexHull hull)
int plConvexHullGetReversedLoopIndex(plConvexHull hull, int n)
plConvexHull plConvexHullCompute(float(*coords)[3], int count)
void plConvexHullDelete(plConvexHull hull)
void plConvexHullGetFaceLoops(plConvexHull hull, int n, int *loops)
int plConvexHullNumFaces(plConvexHull hull)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only_during_modify(const blender::Span< GeometryComponentType > component_types)
const GeometryComponent * get_component_for_read(GeometryComponentType component_type) const
const Mesh * get_mesh_for_read() const
const Curves * get_curves_for_read() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
unsigned int e
unsigned int v
CustomData vdata
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)