Changeset View
Changeset View
Standalone View
Standalone View
source/blender/geometry/intern/convert_mesh_to_curve.cc
- This file was copied from source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc.
| Show All 9 Lines | |||||
| * GNU General Public License for more details. | * GNU General Public License for more details. | ||||
| * | * | ||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| */ | */ | ||||
| #include "BLI_array.hh" | #include "BLI_array.hh" | ||||
| #include "BLI_set.hh" | |||||
| #include "BLI_task.hh" | #include "BLI_task.hh" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "BKE_attribute_access.hh" | |||||
| #include "BKE_attribute_math.hh" | #include "BKE_attribute_math.hh" | ||||
| #include "BKE_geometry_set.hh" | |||||
| #include "BKE_spline.hh" | #include "BKE_spline.hh" | ||||
| #include "node_geometry_util.hh" | #include "GEO_mesh_to_curve.hh" | ||||
| using blender::Array; | namespace blender::geometry { | ||||
| namespace blender::nodes { | |||||
| static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b) | |||||
| { | |||||
| b.add_input<decl::Geometry>("Mesh"); | |||||
| b.add_input<decl::String>("Selection"); | |||||
| b.add_output<decl::Geometry>("Curve"); | |||||
| } | |||||
| template<typename T> | template<typename T> | ||||
| static void copy_attribute_to_points(const VArray<T> &source_data, | static void copy_attribute_to_points(const VArray<T> &source_data, | ||||
| Span<int> map, | Span<int> map, | ||||
| MutableSpan<T> dest_data) | MutableSpan<T> dest_data) | ||||
| { | { | ||||
| for (const int point_index : map.index_range()) { | for (const int point_index : map.index_range()) { | ||||
| const int vert_index = map[point_index]; | const int vert_index = map[point_index]; | ||||
| dest_data[point_index] = source_data[vert_index]; | dest_data[point_index] = source_data[vert_index]; | ||||
| } | } | ||||
| } | } | ||||
| static void copy_attributes_to_points(CurveEval &curve, | static void copy_attributes_to_points(CurveEval &curve, | ||||
| const MeshComponent &mesh_component, | const MeshComponent &mesh_component, | ||||
| Span<Vector<int>> point_to_vert_maps) | Span<Vector<int>> point_to_vert_maps) | ||||
| { | { | ||||
| MutableSpan<SplinePtr> splines = curve.splines(); | MutableSpan<SplinePtr> splines = curve.splines(); | ||||
| Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids(); | Set<bke::AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids(); | ||||
| /* Copy builtin control point attributes. */ | /* Copy builtin control point attributes. */ | ||||
| if (source_attribute_ids.contains("tilt")) { | if (source_attribute_ids.contains("tilt")) { | ||||
| const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( | const fn::GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( | ||||
| "tilt", ATTR_DOMAIN_POINT, 0.0f); | "tilt", ATTR_DOMAIN_POINT, 0.0f); | ||||
| threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { | threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| copy_attribute_to_points<float>( | copy_attribute_to_points<float>( | ||||
| *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); | *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); | ||||
| } | } | ||||
| }); | }); | ||||
| source_attribute_ids.remove_contained("tilt"); | source_attribute_ids.remove_contained("tilt"); | ||||
| } | } | ||||
| if (source_attribute_ids.contains("radius")) { | if (source_attribute_ids.contains("radius")) { | ||||
| const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( | const fn::GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( | ||||
| "radius", ATTR_DOMAIN_POINT, 1.0f); | "radius", ATTR_DOMAIN_POINT, 1.0f); | ||||
| threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { | threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| copy_attribute_to_points<float>( | copy_attribute_to_points<float>( | ||||
| *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); | *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); | ||||
| } | } | ||||
| }); | }); | ||||
| source_attribute_ids.remove_contained("radius"); | source_attribute_ids.remove_contained("radius"); | ||||
| } | } | ||||
| /* Don't copy other builtin control point attributes. */ | /* Don't copy attributes that are built in on meshes but not on curves. */ | ||||
| source_attribute_ids.remove("position"); | for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { | ||||
| if (attribute_id.is_named() && mesh_component.attribute_is_builtin(attribute_id.name())) { | |||||
| source_attribute_ids.remove(attribute_id); | |||||
| } | |||||
| } | |||||
| /* Copy dynamic control point attributes. */ | /* Copy dynamic control point attributes. */ | ||||
| for (const AttributeIDRef &attribute_id : source_attribute_ids) { | for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { | ||||
| const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, | const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read( | ||||
| ATTR_DOMAIN_POINT); | attribute_id, ATTR_DOMAIN_POINT); | ||||
| /* Some attributes might not exist if they were builtin attribute on domains that don't | /* 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. */ | * have any elements, i.e. a face attribute on the output of the line primitive node. */ | ||||
| if (!mesh_attribute) { | if (!mesh_attribute) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); | const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); | ||||
| threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { | threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { | ||||
| for (const int i : range) { | for (const int i : range) { | ||||
| /* Create attribute on the spline points. */ | /* Create attribute on the spline points. */ | ||||
| splines[i]->attributes.create(attribute_id, data_type); | splines[i]->attributes.create(attribute_id, data_type); | ||||
| std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write( | std::optional<fn::GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write( | ||||
| attribute_id); | attribute_id); | ||||
| BLI_assert(spline_attribute); | BLI_assert(spline_attribute); | ||||
| /* Copy attribute based on the map for this spline. */ | /* Copy attribute based on the map for this spline. */ | ||||
| attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { | attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { | ||||
| using T = decltype(dummy); | using T = decltype(dummy); | ||||
| copy_attribute_to_points<T>( | copy_attribute_to_points<T>( | ||||
| mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); | mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); | ||||
| }); | }); | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| curve.assert_valid_point_attributes(); | curve.assert_valid_point_attributes(); | ||||
| } | } | ||||
| struct CurveFromEdgesOutput { | struct CurveFromEdgesOutput { | ||||
| std::unique_ptr<CurveEval> curve; | std::unique_ptr<CurveEval> curve; | ||||
| Vector<Vector<int>> point_to_vert_maps; | Vector<Vector<int>> point_to_vert_maps; | ||||
| }; | }; | ||||
| static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) | static CurveFromEdgesOutput edges_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) | ||||
| { | { | ||||
| std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); | std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); | ||||
| Vector<Vector<int>> point_to_vert_maps; | Vector<Vector<int>> point_to_vert_maps; | ||||
| /* Compute the number of edges connecting to each vertex. */ | /* Compute the number of edges connecting to each vertex. */ | ||||
| Array<int> neighbor_count(verts.size(), 0); | Array<int> neighbor_count(verts.size(), 0); | ||||
| for (const std::pair<int, int> &edge : edges) { | for (const std::pair<int, int> &edge : edges) { | ||||
| neighbor_count[edge.first]++; | neighbor_count[edge.first]++; | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | static CurveFromEdgesOutput edges_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) | ||||
| return {std::move(curve), std::move(point_to_vert_maps)}; | 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). | * 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 | * This helps to make the above algorithm simpler by removing the need to check for selection | ||||
| * in many places. | * in many places. | ||||
| */ | */ | ||||
| static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params, | static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const IndexMask selection) | ||||
| 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; | Vector<std::pair<int, int>> selected_edges; | ||||
| for (const int i : IndexRange(mesh.totedge)) { | for (const int i : selection) { | ||||
| if (selection[i]) { | |||||
| selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); | selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); | ||||
| } | } | ||||
| } | |||||
| return selected_edges; | return selected_edges; | ||||
| } | } | ||||
| static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) | std::unique_ptr<CurveEval> mesh_to_curve_convert(const MeshComponent &mesh_component, | ||||
| const IndexMask selection) | |||||
| { | { | ||||
| GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); | const Mesh &mesh = *mesh_component.get_for_read(); | ||||
| Vector<std::pair<int, int>> selected_edges = get_selected_edges(*mesh_component.get_for_read(), | |||||
| selection); | |||||
| CurveFromEdgesOutput output = edges_to_curve({mesh.mvert, mesh.totvert}, selected_edges); | |||||
| copy_attributes_to_points(*output.curve, mesh_component, output.point_to_vert_maps); | |||||
| geometry_set = bke::geometry_set_realize_instances(geometry_set); | return std::move(output.curve); | ||||
| 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); | std::unique_ptr<CurveEval> mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection) | ||||
| 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; | MeshComponent mesh_component; | ||||
| mesh_component.replace(&const_cast<Mesh &>(mesh), GeometryOwnershipType::ReadOnly); | |||||
| geo_node_type_base( | return mesh_to_curve_convert(mesh_component, selection); | ||||
| &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); | |||||
| ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; | |||||
| ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; | |||||
| nodeRegisterType(&ntype); | |||||
| } | } | ||||
| } // namespace blender::geometry | |||||