diff options
Diffstat (limited to 'source/blender/nodes/geometry')
26 files changed, 1171 insertions, 70 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 81092798ca1..79a98c5ebf0 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -62,4 +62,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, Mesh *create_cube_mesh(const float size); +/** + * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. + */ +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_component, + Span<bool> masks, + const bool invert); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc index 21538db5455..71643df1cb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -101,9 +101,12 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f return tmp; } -template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max) +template<> +inline ColorGeometry4f clamp_value(const ColorGeometry4f val, + const ColorGeometry4f min, + const ColorGeometry4f max) { - Color4f tmp; + ColorGeometry4f tmp; tmp.r = std::min(std::max(val.r, min.r), max.r); tmp.g = std::min(std::max(val.g, min.g), max.g); tmp.b = std::min(std::max(val.b, min.b), max.b); @@ -214,8 +217,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam break; } case CD_PROP_COLOR: { - Color4f min = params.get_input<Color4f>("Min_003"); - Color4f max = params.get_input<Color4f>("Max_003"); + ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003"); + ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003"); if (operation == NODE_CLAMP_RANGE) { if (min.r > max.r) { std::swap(min.r, max.r); @@ -230,8 +233,9 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam std::swap(min.a, max.a); } } - MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); - clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); + clamp_attribute<ColorGeometry4f>( + attribute_input->typed<ColorGeometry4f>(), results, min, max); break; } default: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index b13e82e676d..5293dd8c876 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon * currently. */ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - OutputAttribute_Typed<Color4f> attribute_result = - component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain); + OutputAttribute_Typed<ColorGeometry4f> attribute_result = + component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain); if (!attribute_result) { return; } @@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); - MutableSpan<Color4f> results = attribute_result.as_span(); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span(); ColorBand *color_ramp = &node_storage->color_ramp; parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index a2ff1668a06..57ac68b4cd4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray<float3> &input_a, } } -static void do_equal_operation_color4f(const VArray<Color4f> &input_a, - const VArray<Color4f> &input_b, +static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, + const VArray<ColorGeometry4f> &input_b, const float threshold, MutableSpan<bool> span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) < threshold_squared; } } @@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray<float3> &input_a, } } -static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a, - const VArray<Color4f> &input_b, +static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a, + const VArray<ColorGeometry4f> &input_b, const float threshold, MutableSpan<bool> span_result) { const float threshold_squared = pow2f(threshold); const int size = input_a.size(); for (const int i : IndexRange(size)) { - const Color4f a = input_a[i]; - const Color4f b = input_b[i]; + const ColorGeometry4f a = input_a[i]; + const ColorGeometry4f b = input_b[i]; span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; } } @@ -287,8 +287,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f( - attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); + do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), + attribute_b->typed<ColorGeometry4f>(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( @@ -305,8 +307,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f( - attribute_a->typed<Color4f>(), attribute_b->typed<Color4f>(), threshold, result_span); + do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), + attribute_b->typed<ColorGeometry4f>(), + threshold, + result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc index 2fc86269797..599c9e58e52 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -165,9 +165,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>( - input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f)); - MutableSpan<Color4f> results = attribute_result.as_span<Color4f>(); + GVArray_Typed<ColorGeometry4f> attribute_in = + component.attribute_get_for_read<ColorGeometry4f>( + input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 60522fd0f72..389abe3b2aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams break; } case CD_PROP_COLOR: { - const Color4f value = params.get_input<Color4f>("Value_002"); + const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002"); attribute->fill(&value); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index e502a183ef5..a6bd6c0ee32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode, static void do_mix_operation_color4f(const int blend_mode, const VArray<float> &factors, - const VArray<Color4f> &inputs_a, - const VArray<Color4f> &inputs_b, - VMutableArray<Color4f> &results) + const VArray<ColorGeometry4f> &inputs_a, + const VArray<ColorGeometry4f> &inputs_b, + VMutableArray<ColorGeometry4f> &results) { const int size = results.size(); parallel_for(IndexRange(size), 512, [&](IndexRange range) { for (const int i : range) { const float factor = factors[i]; - Color4f a = inputs_a[i]; - const Color4f b = inputs_b[i]; + ColorGeometry4f a = inputs_a[i]; + const ColorGeometry4f b = inputs_b[i]; ramp_blend(blend_mode, a, factor, b); results.set(i, a); } @@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type, else if (result_type == CD_PROP_COLOR) { do_mix_operation_color4f(blend_mode, attribute_factor, - attribute_a.typed<Color4f>(), - attribute_b.typed<Color4f>(), - attribute_result.typed<Color4f>()); + attribute_a.typed<ColorGeometry4f>(), + attribute_b.typed<ColorGeometry4f>(), + attribute_result.typed<ColorGeometry4f>()); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index aa558314b9e..d6b1ad3e9e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -79,8 +79,9 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec const AttributeDomain result_domain = get_result_domain( component, result_attribute_name, mapping_name); - OutputAttribute_Typed<Color4f> attribute_out = - component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain); + OutputAttribute_Typed<ColorGeometry4f> attribute_out = + component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name, + result_domain); if (!attribute_out) { return; } @@ -88,7 +89,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( mapping_name, result_domain, {0, 0, 0}); - MutableSpan<Color4f> colors = attribute_out.as_span(); + MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { for (const int i : range) { TexResult texture_result = {0}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 8877af445f9..b04e04d1cb7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -59,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation) static bool operation_use_input_c(const NodeVectorMathOperation operation) { - return ELEM( - operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD); + return ELEM(operation, + NODE_VECTOR_MATH_WRAP, + NODE_VECTOR_MATH_REFRACT, + NODE_VECTOR_MATH_FACEFORWARD, + NODE_VECTOR_MATH_MULTIPLY_ADD); } static void geo_node_attribute_vector_math_layout(uiLayout *layout, @@ -137,6 +140,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op case NODE_VECTOR_MATH_TANGENT: case NODE_VECTOR_MATH_REFRACT: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: return CD_PROP_FLOAT3; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: @@ -495,6 +499,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: + case NODE_VECTOR_MATH_MULTIPLY_ADD: do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(), attribute_b->typed<float3>(), attribute_c->typed<float3>(), diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc new file mode 100644 index 00000000000..306085e3b75 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_length_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_length_out[] = { + {SOCK_FLOAT, N_("Length")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_curve_length_exec(GeoNodeExecParams params) +{ + GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); + curve_set = bke::geometry_set_realize_instances(curve_set); + if (!curve_set.has_curve()) { + params.set_output("Length", 0.0f); + return; + } + const CurveEval &curve = *curve_set.get_curve_for_read(); + float length = 0.0f; + for (const SplinePtr &spline : curve.splines()) { + length += spline->length(); + } + params.set_output("Length", length); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_length() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 1c42b9341a0..684f7d6c702 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -182,6 +182,8 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, } } + output_curve->attributes.reallocate(output_curve->splines().size()); + return output_curve; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f0effdc71f6..fe1b23bf6d7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -21,6 +21,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_spline.hh" @@ -236,6 +237,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr } Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total); + BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..0e053a6d0e9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -0,0 +1,677 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::bke::CustomDataAttributes; + +/* Code from the mask modifier in MOD_mask.cc. */ +extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map); +extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map, + blender::Span<int> edge_map); +extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span<int> vertex_map, + blender::Span<int> edge_map, + blender::Span<int> masked_poly_indices, + blender::Span<int> new_loop_starts); + +static bNodeSocketTemplate geo_node_delete_geometry_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Selection")}, + {SOCK_BOOLEAN, N_("Invert")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_delete_geometry_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data(spline.positions(), r_spline.positions(), mask); + copy_data(spline.radii(), r_spline.radii(), mask); + copy_data(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast<const BezierSpline &>(spline); + BezierSpline &dst = static_cast<BezierSpline &>(r_spline); + copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); + copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(spline); + NURBSpline &dst = static_cast<NURBSpline &>(r_spline); + copy_data(src.weights(), dst.weights(), mask); + break; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.get_for_read(name); + BLI_assert(src_attribute); + + if (!dst.create(name, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional<GMutableSpan> new_attribute = dst.get_for_write(name); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve, + const StringRef name, + const bool invert) +{ + Span<SplinePtr> input_splines = input_curve.splines(); + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector<int64_t> copied_splines; + + if (input_curve.attributes.get_for_read(name)) { + GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false); + for (const int i : input_splines.index_range()) { + if (selection[i] == invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Reuse index vector for each spline. */ + Vector<int64_t> indices_to_copy; + + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false); + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[i_point] == invert) { + indices_to_copy.append(i_point); + } + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void delete_curve_selection(const CurveComponent &in_component, + CurveComponent &r_component, + const StringRef selection_name, + const bool invert) +{ + std::unique_ptr<CurveEval> r_curve = curve_delete( + *in_component.get_for_read(), selection_name, invert); + if (r_curve) { + r_component.replace(r_curve.release()); + } + else { + r_component.clear(); + } +} + +static void delete_point_cloud_selection(const PointCloudComponent &in_component, + PointCloudComponent &out_component, + const StringRef selection_name, + const bool invert) +{ + const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_POINT, false); + VArray_Span<bool> selection{selection_attribute}; + + const int total = selection.count(invert); + if (total == 0) { + out_component.clear(); + return; + } + out_component.replace(BKE_pointcloud_new_nomain(total)); + + /* Invert the inversion, because this deletes the selected points instead of keeping them. */ + copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); +} + +static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + uint *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + uint num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_edge_map, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge + * are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + uint *r_num_selected_vertices, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + uint num_selected_edges = 0; + uint num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const VArray<bool> &vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const VArray<bool> &edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const VArray<bool> &poly_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + uint num_selected_vertices = 0; + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +using FillMapsFunction = void (*)(const Mesh &mesh, + const VArray<bool> &selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops); + +/** + * Delete the parts of the mesh that are in the selection. The `fill_maps_function` + * depends on the selection type: vertices, edges or faces. + */ +static Mesh *delete_mesh_selection(const Mesh &mesh_in, + const VArray<bool> &selection, + const bool invert, + FillMapsFunction fill_maps_function) +{ + Array<int> vertex_map(mesh_in.totvert); + uint num_selected_vertices; + + Array<int> edge_map(mesh_in.totedge); + uint num_selected_edges; + + Vector<int> selected_poly_indices; + Vector<int> new_loop_starts; + uint num_selected_polys; + uint num_selected_loops; + + /* Fill all the maps based on the selection. We delete everything + * in the selection instead of keeping it, so we need to invert it. */ + fill_maps_function(mesh_in, + selection, + !invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + + Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + BKE_mesh_calc_edges_loose(result); + /* Tag to recalculate normals later. */ + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + + return result; +} + +static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) +{ + std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name); + if (!selection_attribute) { + /* The node will not do anything in this case, but this function must return something. */ + return ATTR_DOMAIN_POINT; + } + + /* Corners can't be deleted separately, so interpolate corner attributes + * to the face domain. Note that this choice is somewhat arbitrary. */ + if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { + return ATTR_DOMAIN_FACE; + } + + return selection_attribute->domain; +} + +static void delete_mesh_selection(MeshComponent &component, + const Mesh &mesh_in, + const StringRef selection_name, + const bool invert) +{ + /* Figure out the best domain to use. */ + const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); + + /* This already checks if the attribute exists, and displays a warning in that case. */ + GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + selection_name, selection_domain, false); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] != invert) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + Mesh *mesh_out; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); + break; + case ATTR_DOMAIN_EDGE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); + break; + case ATTR_DOMAIN_FACE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); + break; + default: + BLI_assert_unreachable(); + break; + } + component.replace_mesh_but_keep_vertex_group_names(mesh_out); +} + +static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + const bool invert = params.extract_input<bool>("Invert"); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (selection_name.empty()) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + GeometrySet out_set(geometry_set); + if (geometry_set.has<PointCloudComponent>()) { + delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(), + out_set.get_component_for_write<PointCloudComponent>(), + selection_name, + invert); + } + if (geometry_set.has<MeshComponent>()) { + delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(), + *geometry_set.get_mesh_for_read(), + selection_name, + invert); + } + if (geometry_set.has<CurveComponent>()) { + delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(), + out_set.get_component_for_write<CurveComponent>(), + selection_name, + invert); + } + + params.set_output("Geometry", std::move(out_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_delete_geometry() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out); + ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index aace71d1d40..f19d533d0b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -214,6 +215,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) } Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); BLI_assert(BKE_mesh_is_valid(mesh)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 2806472286e..4c1521aa6f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -561,6 +562,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius_top, radius_bottom, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 1d31068653e..b34913df843 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -64,6 +65,7 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const float size = params.extract_input<float>("Size"); Mesh *mesh = create_cube_mesh(size); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index f443b4387d3..c7b9fb920f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -75,6 +76,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 5a4bab86421..ac2f5a23a4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -167,6 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y); BLI_assert(BKE_mesh_is_valid(mesh)); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 242cc6ed7df..5f7d8150022 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "bmesh.h" @@ -67,6 +68,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 2eeb87695a8..d93c4e39fda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -165,6 +166,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) const int count = params.extract_input<int>("Count"); mesh = create_line_mesh(start, delta, count); } + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index cc93e71a5dd..7d340679269 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "UI_interface.h" @@ -296,6 +297,7 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); + BKE_id_material_eval_ensure_default_slot(&mesh->id); params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..0fb7910c904 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,316 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::Array; + +static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = { + {SOCK_GEOMETRY, N_("Mesh")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +namespace blender::nodes { + +template<typename T> +static void copy_attribute_to_points(const VArray<T> &source_data, + Span<int> map, + MutableSpan<T> dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span<Vector<int>> point_to_vert_maps) +{ + MutableSpan<SplinePtr> splines = curve.splines(); + Set<std::string> source_attribute_names = mesh_component.attribute_names(); + + /* Copy builtin control point attributes. */ + if (source_attribute_names.contains_as("tilt")) { + const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_names.remove_contained_as("tilt"); + } + if (source_attribute_names.contains_as("radius")) { + const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( + "radius", ATTR_DOMAIN_POINT, 1.0f); + parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points<float>( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_names.remove_contained_as("radius"); + } + + /* Don't copy other builtin control point attributes. */ + source_attribute_names.remove_as("position"); + + /* Copy dynamic control point attributes. */ + for (const StringRef name : source_attribute_names) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(name, data_type); + std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points<T>( + mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr<CurveEval> curve; + Vector<Vector<int>> point_to_vert_maps; +}; + +static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + Vector<Vector<int>> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array<int> neighbor_count(verts.size(), 0); + for (const std::pair<int, int> &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array<int> neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array<int> used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array<int> neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array<int> unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Vector<int> point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params, + const MeshComponent &component) +{ + const Mesh &mesh = *component.get_for_read(); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector<std::pair<int, int>> selected_edges; + for (const int i : IndexRange(mesh.totedge)) { + if (selection[i]) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + } + + return selected_edges; +} + +static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *component.get_for_read(); + Span<MVert> verts = Span{mesh.mvert, mesh.totvert}; + Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component); + if (selected_edges.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); + copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); + + params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index d2b024b208c..fc04d1e275f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -52,10 +52,10 @@ static void copy_data_based_on_mask(Span<T> data, } } -static void copy_attributes_based_on_mask(const GeometryComponent &in_component, - GeometryComponent &result_component, - Span<bool> masks, - const bool invert) +void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, + GeometryComponent &result_component, + Span<bool> masks, + const bool invert) { for (const std::string &name : in_component.attribute_names()) { ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); @@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component create_component_points(out_component, total); - copy_attributes_based_on_mask(in_component, out_component, masks, invert); + copy_point_attributes_based_on_mask(in_component, out_component, masks, invert); } static GeometrySet separate_geometry_set(const GeometrySet &set_in, @@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in, { GeometrySet set_out; for (const GeometryComponent *component : set_in.get_components_for_read()) { + if (component->type() == GEO_COMPONENT_TYPE_CURVE) { + /* Don't support the curve component for now, even though it has a point domain. */ + continue; + } GeometryComponent &out_component = set_out.get_component_for_write(component->type()); separate_points_from_component(*component, out_component, mask_name, invert); } @@ -167,6 +171,6 @@ void register_node_type_geo_point_separate() geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index bb0a20f4561..742fafba9e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -22,24 +22,24 @@ static bNodeSocketTemplate geo_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, - {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000}, - {SOCK_BOOLEAN, N_("A")}, - {SOCK_BOOLEAN, N_("B")}, - {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0}, - {SOCK_STRING, N_("A")}, - {SOCK_STRING, N_("B")}, - {SOCK_GEOMETRY, N_("A")}, - {SOCK_GEOMETRY, N_("B")}, - {SOCK_OBJECT, N_("A")}, - {SOCK_OBJECT, N_("B")}, - {SOCK_COLLECTION, N_("A")}, - {SOCK_COLLECTION, N_("B")}, + {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000}, + {SOCK_BOOLEAN, N_("False")}, + {SOCK_BOOLEAN, N_("True")}, + {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0}, + {SOCK_STRING, N_("False")}, + {SOCK_STRING, N_("True")}, + {SOCK_GEOMETRY, N_("False")}, + {SOCK_GEOMETRY, N_("True")}, + {SOCK_OBJECT, N_("False")}, + {SOCK_OBJECT, N_("True")}, + {SOCK_COLLECTION, N_("False")}, + {SOCK_COLLECTION, N_("True")}, {-1, ""}, }; @@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) { NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); - data->input_type = SOCK_FLOAT; + data->input_type = SOCK_GEOMETRY; node->storage = data; } @@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams ¶ms, const StringRef input_suffix, const StringRef output_identifier) { - const std::string name_a = "A" + input_suffix; - const std::string name_b = "B" + input_suffix; + const std::string name_a = "False" + input_suffix; + const std::string name_b = "True" + input_suffix; if (input) { params.set_input_unused(name_a); if (params.lazy_require_input(name_b)) { @@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params) break; } case SOCK_RGBA: { - output_input<Color4f>(params, input, "_004", "Output_004"); + output_input<ColorGeometry4f>(params, input, "_004", "Output_004"); break; } case SOCK_STRING: { @@ -165,13 +165,13 @@ void register_node_type_geo_switch() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out); node_type_init(&ntype, geo_node_switch_init); node_type_update(&ntype, blender::nodes::geo_node_switch_update); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; - ntype.geometry_node_execute_supports_lazyness = true; + ntype.geometry_node_execute_supports_laziness = true; ntype.draw_buttons = geo_node_switch_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 9714a4f8a80..1ad5dbea492 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -20,6 +20,7 @@ #include "BLI_float4x4.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" @@ -72,7 +73,8 @@ void transform_mesh(Mesh *mesh, else { const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_calc_normals(mesh); + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; } } @@ -158,7 +160,6 @@ static void transform_curve(CurveEval &curve, const float3 rotation, const float3 scale) { - if (use_translate(rotation, scale)) { curve.translate(translation); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index c6049787970..403f4906d07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -23,6 +23,7 @@ #include "node_geometry_util.hh" #include "BKE_lib_id.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_volume.h" @@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in, if (mesh == nullptr) { return; } + BKE_id_material_eval_ensure_default_slot(&mesh->id); MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); dst_component.replace(mesh); } |