Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| #include "BKE_curves.hh" | #include "BKE_curves.hh" | ||||
| #include "BKE_curve_to_mesh.hh" | #include "BKE_curve_to_mesh.hh" | ||||
| #include "DNA_mesh_types.h" | |||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "node_geometry_util.hh" | #include "node_geometry_util.hh" | ||||
| namespace blender::nodes::node_geo_curve_to_mesh_cc { | namespace blender::nodes::node_geo_curve_to_mesh_cc { | ||||
| static void node_declare(NodeDeclarationBuilder &b) | static void node_declare(NodeDeclarationBuilder &b) | ||||
| { | { | ||||
| b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); | b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); | ||||
| b.add_input<decl::Geometry>(N_("Profile Curve")) | b.add_input<decl::Geometry>(N_("Profile Curve")) | ||||
| .only_realized_data() | .only_realized_data() | ||||
| .supported_type(GEO_COMPONENT_TYPE_CURVE); | .supported_type(GEO_COMPONENT_TYPE_CURVE); | ||||
| b.add_input<decl::Bool>(N_("Fill Caps")) | b.add_input<decl::Bool>(N_("Fill Caps")) | ||||
| .description( | .description( | ||||
| N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); | N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); | ||||
| b.add_output<decl::Geometry>(N_("Mesh")); | b.add_output<decl::Geometry>(N_("Mesh")); | ||||
| b.add_output<decl::Bool>(N_("Start Cap")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("End Cap")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("First Rail Edge")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("Rail Edges")).field_source(); | |||||
| b.add_output<decl::Bool>(N_("Profile Edges")).field_source(); | |||||
| } | |||||
| static void calculate_selection_outputs(Mesh *mesh, | |||||
| const Curves curves, | |||||
| const Curves *profile_curves, | |||||
| const bool fill_caps, | |||||
| CurveToMeshAttributeOutputs &r_attribute_outputs) | |||||
| { | |||||
| MutableAttributeAccessor attributes = mesh->attributes_for_write(); | |||||
| bke::CurvesGeometry curves_geometry = bke::CurvesGeometry::wrap(curves.geometry); | |||||
| bke::CurvesGeometry profiles_geometry = bke::CurvesGeometry::wrap(profile_curves->geometry); | |||||
| if (curves_geometry.points_num() == 0 || profiles_geometry.points_num() == 0) { | |||||
| return; | |||||
| } | |||||
| if (r_attribute_outputs.first_rail_id) { | |||||
| SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( | |||||
| r_attribute_outputs.first_rail_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection.span.fill(false); | |||||
| int index = 0; | |||||
| for (const int curve_i : IndexRange(curves_geometry.curves_num())) { | |||||
| for (const int profile_i : IndexRange(profiles_geometry.curves_num())) { | |||||
| const int curve_size = curves_geometry.evaluated_offsets()[curve_i + 1] - | |||||
| curves_geometry.evaluated_offsets()[curve_i]; | |||||
| const int profile_size = profiles_geometry.evaluated_offsets()[profile_i + 1] - | |||||
| profiles_geometry.evaluated_offsets()[profile_i]; | |||||
| const int rail_edges = curves_geometry.cyclic()[curve_i] ? curve_size : curve_size - 1; | |||||
| const int profile_edges = profiles_geometry.cyclic()[profile_i] ? profile_size : | |||||
| profile_size - 1; | |||||
| selection.span.slice(index, rail_edges).fill(true); | |||||
| index += rail_edges * profile_edges + curve_size * profile_edges; | |||||
| } | |||||
| } | |||||
| selection.finish(); | |||||
| } | |||||
| if (r_attribute_outputs.rails_id) { | |||||
| SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( | |||||
| r_attribute_outputs.rails_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection.span.fill(false); | |||||
| int index = 0; | |||||
| for (const int curve_i : IndexRange(curves_geometry.curves_num())) { | |||||
| for (const int profile_i : IndexRange(profiles_geometry.curves_num())) { | |||||
| const int curve_size = curves_geometry.evaluated_offsets()[curve_i + 1] - | |||||
| curves_geometry.evaluated_offsets()[curve_i]; | |||||
| const int profile_size = profiles_geometry.evaluated_offsets()[profile_i + 1] - | |||||
| profiles_geometry.evaluated_offsets()[profile_i]; | |||||
| const int rail_edges = curves_geometry.cyclic()[curve_i] ? curve_size : curve_size - 1; | |||||
| const int profile_edges = profiles_geometry.cyclic()[profile_i] ? profile_size : | |||||
| profile_size - 1; | |||||
| selection.span.slice(index, rail_edges * profile_edges).fill(true); | |||||
| index += rail_edges * profile_edges + curve_size * profile_edges; | |||||
| } | |||||
| } | |||||
| selection.finish(); | |||||
| } | |||||
| if (r_attribute_outputs.profiles_id) { | |||||
| SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( | |||||
| r_attribute_outputs.profiles_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection.span.fill(false); | |||||
| int index = 0; | |||||
| for (const int curve_i : IndexRange(curves_geometry.curves_num())) { | |||||
| for (const int profile_i : IndexRange(profiles_geometry.curves_num())) { | |||||
| const int curve_size = curves_geometry.evaluated_offsets()[curve_i + 1] - | |||||
| curves_geometry.evaluated_offsets()[curve_i]; | |||||
| const int profile_size = profiles_geometry.evaluated_offsets()[profile_i + 1] - | |||||
| profiles_geometry.evaluated_offsets()[profile_i]; | |||||
| const int rail_edges = curves_geometry.cyclic()[curve_i] ? curve_size : curve_size - 1; | |||||
| const int profile_edges = profiles_geometry.cyclic()[profile_i] ? profile_size : | |||||
| profile_size - 1; | |||||
| index += rail_edges * profile_edges; | |||||
| selection.span.slice(index, curve_size * profile_edges).fill(true); | |||||
| index += curve_size * profile_edges; | |||||
| } | |||||
| } | |||||
| selection.finish(); | |||||
| } | |||||
| if (r_attribute_outputs.start_cap_id) { | |||||
| SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( | |||||
| r_attribute_outputs.start_cap_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection.span.fill(false); | |||||
| int index = 0; | |||||
| for (const int curve_i : IndexRange(curves_geometry.curves_num())) { | |||||
| for (const int profile_i : IndexRange(profiles_geometry.curves_num())) { | |||||
| const int curve_size = curves_geometry.evaluated_offsets()[curve_i + 1] - | |||||
| curves_geometry.evaluated_offsets()[curve_i]; | |||||
| const int profile_size = profiles_geometry.evaluated_offsets()[profile_i + 1] - | |||||
| profiles_geometry.evaluated_offsets()[profile_i]; | |||||
| const int rail_edges = curves_geometry.cyclic()[curve_i] ? curve_size : curve_size - 1; | |||||
| const int profile_edges = profiles_geometry.cyclic()[profile_i] ? profile_size : | |||||
| profile_size - 1; | |||||
| index += rail_edges * profile_edges; | |||||
| selection.span.slice(index, profile_edges).fill(true); | |||||
| index += curve_size * profile_edges; | |||||
| } | |||||
| } | |||||
| selection.finish(); | |||||
| } | |||||
| if (r_attribute_outputs.end_cap_id) { | |||||
| SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( | |||||
| r_attribute_outputs.end_cap_id.get(), ATTR_DOMAIN_EDGE); | |||||
| selection.span.fill(false); | |||||
| int index = 0; | |||||
| for (const int curve_i : IndexRange(curves_geometry.curves_num())) { | |||||
| for (const int profile_i : IndexRange(profiles_geometry.curves_num())) { | |||||
| const int curve_size = curves_geometry.evaluated_offsets()[curve_i + 1] - | |||||
| curves_geometry.evaluated_offsets()[curve_i]; | |||||
| const int profile_size = profiles_geometry.evaluated_offsets()[profile_i + 1] - | |||||
| profiles_geometry.evaluated_offsets()[profile_i]; | |||||
| const int rail_edges = curves_geometry.cyclic()[curve_i] ? curve_size : curve_size - 1; | |||||
| const int profile_edges = profiles_geometry.cyclic()[profile_i] ? profile_size : | |||||
| profile_size - 1; | |||||
| index += rail_edges * profile_edges; | |||||
| index += (curve_size - 1) * profile_edges; | |||||
| selection.span.slice(index, profile_edges).fill(true); | |||||
| index += curve_size; | |||||
| } | |||||
| } | |||||
| selection.finish(); | |||||
| } | |||||
| } | } | ||||
| static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, | static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, | ||||
| const GeometrySet &profile_set, | const GeometrySet &profile_set, | ||||
| const bool fill_caps) | const bool fill_caps, | ||||
| CurveToMeshAttributeOutputs &r_attribute_outputs) | |||||
| { | { | ||||
| const Curves &curves = *geometry_set.get_curves_for_read(); | const Curves &curves = *geometry_set.get_curves_for_read(); | ||||
| const Curves *profile_curves = profile_set.get_curves_for_read(); | const Curves *profile_curves = profile_set.get_curves_for_read(); | ||||
| GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); | GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); | ||||
| if (profile_curves == nullptr) { | if (profile_curves == nullptr) { | ||||
| Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry)); | Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry)); | ||||
| geometry_set.replace_mesh(mesh); | geometry_set.replace_mesh(mesh); | ||||
| } | } | ||||
| else { | else { | ||||
| Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry), | Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry), | ||||
| bke::CurvesGeometry::wrap(profile_curves->geometry), | bke::CurvesGeometry::wrap(profile_curves->geometry), | ||||
| fill_caps); | fill_caps); | ||||
| calculate_selection_outputs(mesh, curves, profile_curves, fill_caps, r_attribute_outputs); | |||||
| geometry_set.replace_mesh(mesh); | geometry_set.replace_mesh(mesh); | ||||
| } | } | ||||
| } | } | ||||
| static void node_geo_exec(GeoNodeExecParams params) | static void node_geo_exec(GeoNodeExecParams params) | ||||
| { | { | ||||
| GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); | GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); | ||||
| GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); | GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); | ||||
| const bool fill_caps = params.extract_input<bool>("Fill Caps"); | const bool fill_caps = params.extract_input<bool>("Fill Caps"); | ||||
| bool has_curves = false; | bool has_curves = false; | ||||
| CurveToMeshAttributeOutputs attribute_outputs; | |||||
| if (params.output_is_required("Start Cap")) { | |||||
| attribute_outputs.start_cap_id = StrongAnonymousAttributeID("start_cap_selection"); | |||||
| } | |||||
| if (params.output_is_required("End Cap")) { | |||||
| attribute_outputs.end_cap_id = StrongAnonymousAttributeID("end_cap_selection"); | |||||
| } | |||||
| if (params.output_is_required("First Rail Edge")) { | |||||
| attribute_outputs.first_rail_id = StrongAnonymousAttributeID("first_rail_edge_selection"); | |||||
| } | |||||
| if (params.output_is_required("Rail Edges")) { | |||||
| attribute_outputs.rails_id = StrongAnonymousAttributeID("rail_edges"); | |||||
| } | |||||
| if (params.output_is_required("Profile Edges")) { | |||||
| attribute_outputs.profiles_id = StrongAnonymousAttributeID("profile_edges"); | |||||
| } | |||||
| curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { | curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { | ||||
| if (geometry_set.has_curves()) { | if (geometry_set.has_curves()) { | ||||
| has_curves = true; | has_curves = true; | ||||
| geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); | geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps, attribute_outputs); | ||||
| } | } | ||||
| geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); | geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); | ||||
| }); | }); | ||||
| if (attribute_outputs.start_cap_id) { | |||||
| params.set_output( | |||||
| "Start Cap", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.start_cap_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.end_cap_id) { | |||||
| params.set_output( | |||||
| "End Cap", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.end_cap_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.first_rail_id) { | |||||
| params.set_output( | |||||
| "First Rail Edge", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.first_rail_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.rails_id) { | |||||
| params.set_output( | |||||
| "Rail Edges", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.rails_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| if (attribute_outputs.profiles_id) { | |||||
| params.set_output( | |||||
| "Profile Edges", | |||||
| AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.profiles_id), | |||||
| params.attribute_producer_name())); | |||||
| } | |||||
| params.set_output("Mesh", std::move(curve_set)); | params.set_output("Mesh", std::move(curve_set)); | ||||
| } | } | ||||
| } // namespace blender::nodes::node_geo_curve_to_mesh_cc | } // namespace blender::nodes::node_geo_curve_to_mesh_cc | ||||
| void register_node_type_geo_curve_to_mesh() | void register_node_type_geo_curve_to_mesh() | ||||
| { | { | ||||
| namespace file_ns = blender::nodes::node_geo_curve_to_mesh_cc; | namespace file_ns = blender::nodes::node_geo_curve_to_mesh_cc; | ||||
| static bNodeType ntype; | static bNodeType ntype; | ||||
| geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY); | geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY); | ||||
| ntype.declare = file_ns::node_declare; | ntype.declare = file_ns::node_declare; | ||||
| ntype.geometry_node_execute = file_ns::node_geo_exec; | ntype.geometry_node_execute = file_ns::node_geo_exec; | ||||
| nodeRegisterType(&ntype); | nodeRegisterType(&ntype); | ||||
| } | } | ||||