Blender V4.3
node_geo_boolean.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "BKE_instances.hh"
7
8#include "DNA_mesh_types.h"
9#include "DNA_object_types.h"
10
11#include "NOD_rna_define.hh"
12
13#include "UI_interface.hh"
14#include "UI_resources.hh"
15
17#include "GEO_mesh_boolean.hh"
18#include "GEO_randomize.hh"
19
20#include "node_geometry_util.hh"
21
23
25{
26 const bNode *node = b.node_or_null();
27
28 auto &first_geometry = b.add_input<decl::Geometry>("Mesh 1").only_realized_data().supported_type(
30
31 if (node != nullptr) {
32 switch (geometry::boolean::Operation(node->custom1)) {
35 b.add_input<decl::Geometry>("Mesh", "Mesh 2")
36 .supported_type(GeometryComponent::Type::Mesh)
37 .multi_input();
38 break;
40 b.add_input<decl::Geometry>("Mesh 2")
41 .supported_type(GeometryComponent::Type::Mesh)
42 .multi_input();
43 break;
44 }
45 }
46
47 b.add_input<decl::Bool>("Self Intersection");
48 b.add_input<decl::Bool>("Hole Tolerant");
49 b.add_output<decl::Geometry>("Mesh").propagate_all();
50 auto &output_edges = b.add_output<decl::Bool>("Intersecting Edges")
51 .field_on_all()
52 .make_available([](bNode &node) {
54 });
55
56 if (node != nullptr) {
57 const auto operation = geometry::boolean::Operation(node->custom1);
58 const auto solver = geometry::boolean::Solver(node->custom2);
59
60 output_edges.available(solver == geometry::boolean::Solver::MeshArr);
61
62 switch (operation) {
65 first_geometry.available(false);
66 break;
68 break;
69 }
70 }
71}
72
73static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
74{
75 uiItemR(layout, ptr, "operation", UI_ITEM_NONE, "", ICON_NONE);
76 uiItemR(layout, ptr, "solver", UI_ITEM_NONE, "", ICON_NONE);
77}
78
80 std::optional<std::string> intersecting_edges_id;
81};
82
83static void node_init(bNodeTree * /*tree*/, bNode *node)
84{
87}
88
89#ifdef WITH_GMP
90static Array<short> calc_mesh_material_map(const Mesh &mesh, VectorSet<Material *> &all_materials)
91{
92 Array<short> map(mesh.totcol);
93 for (const int i : IndexRange(mesh.totcol)) {
94 Material *material = mesh.mat[i];
95 map[i] = material ? all_materials.index_of_or_add(material) : -1;
96 }
97 return map;
98}
99#endif /* WITH_GMP */
100
102{
103#ifdef WITH_GMP
106 const bool use_self = params.get_input<bool>("Self Intersection");
107 const bool hole_tolerant = params.get_input<bool>("Hole Tolerant");
108
110 Vector<float4x4> transforms;
111 VectorSet<Material *> materials;
112 Vector<Array<short>> material_remaps;
113
114 GeometrySet set_a;
116 set_a = params.extract_input<GeometrySet>("Mesh 1");
117 /* Note that it technically wouldn't be necessary to realize the instances for the first
118 * geometry input, but the boolean code expects the first shape for the difference operation
119 * to be a single mesh. */
120 if (const Mesh *mesh_in_a = set_a.get_mesh()) {
121 meshes.append(mesh_in_a);
122 transforms.append(float4x4::identity());
123 if (mesh_in_a->totcol == 0) {
124 /* Necessary for faces using the default material when there are no material slots. */
125 materials.add(nullptr);
126 }
127 else {
128 materials.add_multiple({mesh_in_a->mat, mesh_in_a->totcol});
129 }
130 material_remaps.append({});
131 }
132 }
133
134 Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2");
135
136 for (const GeometrySet &geometry : geometry_sets) {
137 if (const Mesh *mesh = geometry.get_mesh()) {
138 meshes.append(mesh);
139 transforms.append(float4x4::identity());
140 material_remaps.append(calc_mesh_material_map(*mesh, materials));
141 }
142 if (const bke::Instances *instances = geometry.get_instances()) {
143 const Span<bke::InstanceReference> references = instances->references();
144 const Span<int> handles = instances->reference_handles();
145 const Span<float4x4> instance_transforms = instances->transforms();
146 for (const int i : handles.index_range()) {
147 const bke::InstanceReference &reference = references[handles[i]];
148 switch (reference.type()) {
151 reference.object());
152 if (const Mesh *mesh = object_geometry.get_mesh()) {
153 meshes.append(mesh);
154 transforms.append(instance_transforms[i]);
155 material_remaps.append(calc_mesh_material_map(*mesh, materials));
156 }
157 break;
158 }
160 if (const Mesh *mesh = reference.geometry_set().get_mesh()) {
161 meshes.append(mesh);
162 transforms.append(instance_transforms[i]);
163 material_remaps.append(calc_mesh_material_map(*mesh, materials));
164 }
165 break;
166 }
169 break;
170 }
171 }
172 }
173 }
174
175 AttributeOutputs attribute_outputs;
177 attribute_outputs.intersecting_edges_id = params.get_output_anonymous_attribute_id_if_needed(
178 "Intersecting Edges");
179 }
180
181 Vector<int> intersecting_edges;
183 op_params.boolean_mode = operation;
184 op_params.no_self_intersections = !use_self;
185 op_params.watertight = !hole_tolerant;
186 op_params.no_nested_components = true; /* TODO: make this configurable. */
188 meshes,
189 transforms,
191 material_remaps,
192 op_params,
193 solver,
194 attribute_outputs.intersecting_edges_id ? &intersecting_edges : nullptr);
195 if (!result) {
196 params.set_default_remaining_outputs();
197 return;
198 }
199
200 MEM_SAFE_FREE(result->mat);
201 result->mat = static_cast<Material **>(
202 MEM_malloc_arrayN(materials.size(), sizeof(Material *), __func__));
203 result->totcol = materials.size();
204 MutableSpan(result->mat, result->totcol).copy_from(materials);
205
206 /* Store intersecting edges in attribute. */
207 if (attribute_outputs.intersecting_edges_id) {
208 MutableAttributeAccessor attributes = result->attributes_for_write();
209 SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
210 *attribute_outputs.intersecting_edges_id, AttrDomain::Edge);
211
212 selection.span.fill(false);
213 for (const int i : intersecting_edges) {
214 selection.span[i] = true;
215 }
216 selection.finish();
217 }
219
220 Vector<GeometrySet> all_geometries;
221 all_geometries.append(set_a);
222 all_geometries.extend(geometry_sets);
223
224 const std::array types_to_join = {GeometryComponent::Type::Edit};
225 GeometrySet result_geometry = geometry::join_geometries(
226 all_geometries, {}, std::make_optional(types_to_join));
227 result_geometry.replace_mesh(result);
228 result_geometry.name = set_a.name;
229
230 params.set_output("Mesh", std::move(result_geometry));
231#else
232 params.error_message_add(NodeWarningType::Error,
233 TIP_("Disabled, Blender was compiled without GMP"));
234 params.set_default_remaining_outputs();
235#endif
236}
237
238static void node_rna(StructRNA *srna)
239{
240 static const EnumPropertyItem rna_node_geometry_boolean_method_items[] = {
242 "INTERSECT",
243 0,
244 "Intersect",
245 "Keep the part of the mesh that is common between all operands"},
247 "UNION",
248 0,
249 "Union",
250 "Combine meshes in an additive way"},
252 "DIFFERENCE",
253 0,
254 "Difference",
255 "Combine meshes in a subtractive way"},
256 {0, nullptr, 0, nullptr, nullptr},
257 };
258 static const EnumPropertyItem rna_geometry_boolean_solver_items[] = {
260 "EXACT",
261 0,
262 "Exact",
263 "Exact solver for the best results"},
265 "FLOAT",
266 0,
267 "Float",
268 "Simple solver for the best performance, without support for overlapping geometry"},
269 {0, nullptr, 0, nullptr, nullptr},
270 };
271
273 "operation",
274 "Operation",
275 "",
276 rna_node_geometry_boolean_method_items,
279
281 "solver",
282 "Solver",
283 "",
284 rna_geometry_boolean_solver_items,
287}
288
289static void node_register()
290{
291 static blender::bke::bNodeType ntype;
292
293 geo_node_type_base(&ntype, GEO_NODE_MESH_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY);
294 ntype.declare = node_declare;
296 ntype.initfunc = node_init;
299
300 node_rna(ntype.rna_ext.srna);
301}
303
304} // namespace blender::nodes::node_geo_boolean_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define TIP_(msgid)
Object is a sort of wrapper for general info.
#define MEM_SAFE_FREE(v)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
int64_t index_of_or_add(const Key &key)
void append(const T &value)
void extend(Span< T > array)
void make_available(bNode &node) const
local_group_size(16, 16) .push_constant(Type b
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
GeometrySet object_get_evaluated_geometry_set(const Object &object)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
Mesh * mesh_boolean(Span< const Mesh * > meshes, Span< float4x4 > transforms, const float4x4 &target_transform, Span< Array< short > > material_remaps, BooleanOpParameters op_params, Solver solver, Vector< int > *r_intersecting_edges)
void debug_randomize_mesh_order(Mesh *mesh)
Definition randomize.cc:220
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
static void node_init(bNodeTree *, bNode *node)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
static void node_rna(StructRNA *srna)
static void node_declare(NodeDeclarationBuilder &b)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
signed short int16_t
Definition stdint.h:76
StructRNA * srna
Definition RNA_types.hh:780
const Mesh * get_mesh() const
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126