Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/curves/intern/curves_ops.cc')
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc645
1 files changed, 513 insertions, 132 deletions
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index 5588f7440a8..f4d1c8046f1 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -6,19 +6,26 @@
#include <atomic>
+#include "BLI_devirtualize_parameters.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector_set.hh"
#include "ED_curves.h"
#include "ED_object.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "WM_api.h"
+#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_legacy_convert.h"
#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -37,8 +44,11 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "RNA_prototypes.h"
+#include "GEO_reverse_uv_sampler.hh"
+
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
* `cu`: Local space of the curves object that is being edited.
@@ -49,6 +59,41 @@
namespace blender::ed::curves {
+static bool object_has_editable_curves(const Main &bmain, const Object &object)
+{
+ if (object.type != OB_CURVES) {
+ return false;
+ }
+ if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
+ return false;
+ }
+ if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
+ return false;
+ }
+ return true;
+}
+
+VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
+{
+ VectorSet<Curves *> unique_curves;
+
+ const Main &bmain = *CTX_data_main(&C);
+
+ Object *object = CTX_data_active_object(&C);
+ if (object && object_has_editable_curves(bmain, *object)) {
+ unique_curves.add_new(static_cast<Curves *>(object->data));
+ }
+
+ CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
+ if (object_has_editable_curves(bmain, *object)) {
+ unique_curves.add(static_cast<Curves *>(object->data));
+ }
+ }
+ CTX_DATA_END;
+
+ return unique_curves;
+}
+
using bke::CurvesGeometry;
namespace convert_to_particle_system {
@@ -143,25 +188,20 @@ static void try_convert_single_object(Object &curves_ob,
}
Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
+ BVHTreeFromMesh surface_bvh;
+ BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+
const Span<float3> positions_cu = curves.positions();
- const VArray<int> looptri_indices = curves.surface_triangle_indices();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
BKE_mesh_runtime_looptri_len(&surface_me)};
- /* Find indices of curves that can be transferred to the old hair system. */
- Vector<int> curves_indices_to_transfer;
- for (const int curve_i : curves.curves_range()) {
- const int looptri_i = looptri_indices[curve_i];
- if (looptri_i >= 0 && looptri_i < looptris.size()) {
- curves_indices_to_transfer.append(curve_i);
- }
- else {
- *r_could_not_convert_some_curves = true;
- }
+ if (looptris.is_empty()) {
+ *r_could_not_convert_some_curves = true;
}
- const int hairs_num = curves_indices_to_transfer.size();
- if (hairs_num == 0) {
+ const int hair_num = curves.curves_num();
+ if (hair_num == 0) {
return;
}
@@ -187,13 +227,13 @@ static void try_convert_single_object(Object &curves_ob,
psys_changed_type(&surface_ob, particle_system);
MutableSpan<ParticleData> particles{
- static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
- hairs_num};
+ static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)),
+ hair_num};
/* The old hair system still uses #MFace, so make sure those are available on the mesh. */
BKE_mesh_tessface_calc(&surface_me);
- /* Prepare utility data structure to map hair roots to mfaces. */
+ /* Prepare utility data structure to map hair roots to #MFace's. */
const Span<int> mface_to_poly_map{
static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
surface_me.totface};
@@ -204,22 +244,25 @@ static void try_convert_single_object(Object &curves_ob,
}
/* Prepare transformation matrices. */
- const float4x4 curves_to_world_mat = curves_ob.obmat;
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
+ const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
- for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
- const int curve_i = curves_indices_to_transfer[new_hair_i];
+ for (const int new_hair_i : IndexRange(hair_num)) {
+ const int curve_i = new_hair_i;
const IndexRange points = curves.points_for_curve(curve_i);
- const int looptri_i = looptri_indices[curve_i];
+ const float3 &root_pos_cu = positions_cu[points.first()];
+ const float3 root_pos_su = transforms.curves_to_surface * root_pos_cu;
+
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(
+ surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
+ BLI_assert(nearest.index >= 0);
+
+ const int looptri_i = nearest.index;
const MLoopTri &looptri = looptris[looptri_i];
const int poly_i = looptri.poly;
- const float3 &root_pos_cu = positions_cu[points.first()];
- const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
-
const int mface_i = find_mface_for_root_position(
surface_me, poly_to_mface_map[poly_i], root_pos_su);
const MFace &mface = surface_me.mface[mface_i];
@@ -248,7 +291,7 @@ static void try_convert_single_object(Object &curves_ob,
for (const int key_i : hair_keys.index_range()) {
const float3 &key_pos_cu = positions_cu[points[key_i]];
- const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
+ const float3 key_pos_su = transforms.curves_to_surface * key_pos_cu;
const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
HairKey &key = hair_keys[key_i];
@@ -422,7 +465,6 @@ static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNU
}
Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name);
- ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */
Curves *curves_id = static_cast<Curves *>(ob_new->data);
BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false);
bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval);
@@ -475,133 +517,169 @@ static bool snap_curves_to_surface_poll(bContext *C)
return true;
}
-static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
+static void snap_curves_to_surface_exec_object(Object &curves_ob,
+ const Object &surface_ob,
+ const AttachMode attach_mode,
+ bool *r_invalid_uvs,
+ bool *r_missing_uvs)
{
- const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- std::atomic<bool> found_invalid_looptri_index = false;
+ Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
- CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
- if (curves_ob->type != OB_CURVES) {
- continue;
- }
- Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- if (curves_id.surface == nullptr) {
- continue;
- }
- Object &surface_ob = *curves_id.surface;
- if (surface_ob.type != OB_MESH) {
- continue;
- }
- Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
-
- MutableSpan<float3> positions_cu = curves.positions_for_write();
- MutableSpan<int> surface_triangle_indices = curves.surface_triangle_indices_for_write();
- MutableSpan<float2> surface_triangle_coords = curves.surface_triangle_coords_for_write();
-
- const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
- BKE_mesh_runtime_looptri_len(&surface_mesh)};
-
- const float4x4 curves_to_world_mat = curves_ob->obmat;
- const float4x4 world_to_curves_mat = curves_to_world_mat.inverted();
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
- const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat;
-
- switch (attach_mode) {
- case AttachMode::Nearest: {
- BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
-
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange points = curves.points_for_curve(curve_i);
- const int first_point_i = points.first();
- const float3 old_first_point_pos_cu = positions_cu[first_point_i];
- const float3 old_first_point_pos_su = curves_to_surface_mat * old_first_point_pos_cu;
-
- BVHTreeNearest nearest;
- nearest.index = -1;
- nearest.dist_sq = FLT_MAX;
- BLI_bvhtree_find_nearest(surface_bvh.tree,
- old_first_point_pos_su,
- &nearest,
- surface_bvh.nearest_callback,
- &surface_bvh);
- const int looptri_index = nearest.index;
- if (looptri_index == -1) {
- continue;
- }
-
- const float3 new_first_point_pos_su = nearest.co;
- const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su;
- const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
-
- for (float3 &pos_cu : positions_cu.slice(points)) {
- pos_cu += pos_diff_cu;
- }
-
- surface_triangle_indices[curve_i] = looptri_index;
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id.surface_uv_map != nullptr) {
+ const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh);
+ surface_uv_map = surface_attributes
+ .lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
+ .typed<float2>();
+ }
+
+ MutableSpan<float3> positions_cu = curves.positions_for_write();
+ MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
+
+ const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
+ BKE_mesh_runtime_looptri_len(&surface_mesh)};
+
+ const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
+
+ switch (attach_mode) {
+ case AttachMode::Nearest: {
+ BVHTreeFromMesh surface_bvh;
+ BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ const int first_point_i = points.first();
+ const float3 old_first_point_pos_cu = positions_cu[first_point_i];
+ const float3 old_first_point_pos_su = transforms.curves_to_surface *
+ old_first_point_pos_cu;
+
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(surface_bvh.tree,
+ old_first_point_pos_su,
+ &nearest,
+ surface_bvh.nearest_callback,
+ &surface_bvh);
+ const int looptri_index = nearest.index;
+ if (looptri_index == -1) {
+ continue;
+ }
+ const float3 new_first_point_pos_su = nearest.co;
+ const float3 new_first_point_pos_cu = transforms.surface_to_curves *
+ new_first_point_pos_su;
+ const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
+
+ for (float3 &pos_cu : positions_cu.slice(points)) {
+ pos_cu += pos_diff_cu;
+ }
+
+ if (!surface_uv_map.is_empty()) {
const MLoopTri &looptri = surface_looptris[looptri_index];
- const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
- const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
+ const int corner0 = looptri.tri[0];
+ const int corner1 = looptri.tri[1];
+ const int corner2 = looptri.tri[2];
+ const float2 &uv0 = surface_uv_map[corner0];
+ const float2 &uv1 = surface_uv_map[corner1];
+ const float2 &uv2 = surface_uv_map[corner2];
+ const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
+ const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
+ const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
- surface_triangle_coords[curve_i] = bke::curves::encode_surface_bary_coord(bary_coords);
+ const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
+ surface_uv_coords[curve_i] = uv;
}
- });
+ }
+ });
+ break;
+ }
+ case AttachMode::Deform: {
+ if (surface_uv_map.is_empty()) {
+ *r_missing_uvs = true;
break;
}
- case AttachMode::Deform: {
- threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
- for (const int curve_i : curves_range) {
- const IndexRange points = curves.points_for_curve(curve_i);
- const int first_point_i = points.first();
- const float3 old_first_point_pos_cu = positions_cu[first_point_i];
-
- const int looptri_index = surface_triangle_indices[curve_i];
- if (!surface_looptris.index_range().contains(looptri_index)) {
- found_invalid_looptri_index = true;
- continue;
- }
-
- const MLoopTri &looptri = surface_looptris[looptri_index];
+ using geometry::ReverseUVSampler;
+ ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris};
+
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
+ for (const int curve_i : curves_range) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ const int first_point_i = points.first();
+ const float3 old_first_point_pos_cu = positions_cu[first_point_i];
+
+ const float2 uv = surface_uv_coords[curve_i];
+ ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
+ if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
+ *r_invalid_uvs = true;
+ continue;
+ }
- const float3 bary_coords = bke::curves::decode_surface_bary_coord(
- surface_triangle_coords[curve_i]);
+ const MLoopTri &looptri = *lookup_result.looptri;
+ const float3 &bary_coords = lookup_result.bary_weights;
- const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
- const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
+ const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
+ const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
+ const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
- float3 new_first_point_pos_su;
- interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
- const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su;
+ float3 new_first_point_pos_su;
+ interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
+ const float3 new_first_point_pos_cu = transforms.surface_to_curves *
+ new_first_point_pos_su;
- const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
- for (float3 &pos_cu : positions_cu.slice(points)) {
- pos_cu += pos_diff_cu;
- }
+ const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
+ for (float3 &pos_cu : positions_cu.slice(points)) {
+ pos_cu += pos_diff_cu;
}
- });
- break;
- }
+ }
+ });
+ break;
}
+ }
+
+ DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+}
+
+static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
+{
+ const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
+ bool found_invalid_uvs = false;
+ bool found_missing_uvs = false;
+
+ CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
+ if (curves_ob->type != OB_CURVES) {
+ continue;
+ }
+ Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
+ if (curves_id.surface == nullptr) {
+ continue;
+ }
+ if (curves_id.surface->type != OB_MESH) {
+ continue;
+ }
+ snap_curves_to_surface_exec_object(
+ *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
}
CTX_DATA_END;
- if (found_invalid_looptri_index) {
+ if (found_missing_uvs) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Curves do not have attachment information that can be used for deformation");
+ }
+ if (found_invalid_uvs) {
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
}
- WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
+ /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
return OPERATOR_FINISHED;
}
@@ -645,6 +723,305 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
"How to find the point on the surface to attach to");
}
+bool selection_operator_poll(bContext *C)
+{
+ const Object *object = CTX_data_active_object(C);
+ if (object == nullptr) {
+ return false;
+ }
+ if (object->type != OB_CURVES) {
+ return false;
+ }
+ if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) {
+ return false;
+ }
+ return true;
+}
+
+namespace set_selection_domain {
+
+static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
+{
+ const eAttrDomain domain = eAttrDomain(RNA_enum_get(op->ptr, "domain"));
+
+ for (Curves *curves_id : get_unique_editable_curves(*C)) {
+ if (curves_id->selection_domain == domain && (curves_id->flag & CV_SCULPT_SELECTION_ENABLED)) {
+ continue;
+ }
+
+ const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain);
+ curves_id->selection_domain = domain;
+ curves_id->flag |= CV_SCULPT_SELECTION_ENABLED;
+
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+
+ if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
+ VArray<float> curve_selection = curves.adapt_domain(
+ curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
+ curve_selection.materialize(curves.selection_curve_float_for_write());
+ attributes.remove(".selection_point_float");
+ }
+ else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
+ VArray<float> point_selection = curves.adapt_domain(
+ curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ point_selection.materialize(curves.selection_point_float_for_write());
+ attributes.remove(".selection_curve_float");
+ }
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace set_selection_domain
+
+static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Set Select Mode";
+ ot->idname = __func__;
+ ot->description = "Change the mode used for selection masking in curves sculpt mode";
+
+ ot->exec = set_selection_domain::curves_set_selection_domain_exec;
+ ot->poll = selection_operator_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = prop = RNA_def_enum(
+ ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
+}
+
+namespace disable_selection {
+
+static int curves_disable_selection_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ for (Curves *curves_id : get_unique_editable_curves(*C)) {
+ curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace disable_selection
+
+static void CURVES_OT_disable_selection(wmOperatorType *ot)
+{
+ ot->name = "Disable Selection";
+ ot->idname = __func__;
+ ot->description = "Disable the drawing of influence of selection in sculpt mode";
+
+ ot->exec = disable_selection::curves_disable_selection_exec;
+ ot->poll = selection_operator_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static bool varray_contains_nonzero(const VArray<float> &data)
+{
+ bool contains_nonzero = false;
+ devirtualize_varray(data, [&](const auto array) {
+ for (const int i : data.index_range()) {
+ if (array[i] != 0.0f) {
+ contains_nonzero = true;
+ break;
+ }
+ }
+ });
+ return contains_nonzero;
+}
+
+bool has_anything_selected(const Curves &curves_id)
+{
+ const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ switch (curves_id.selection_domain) {
+ case ATTR_DOMAIN_POINT:
+ return varray_contains_nonzero(curves.selection_point_float());
+ case ATTR_DOMAIN_CURVE:
+ return varray_contains_nonzero(curves.selection_curve_float());
+ }
+ BLI_assert_unreachable();
+ return false;
+}
+
+static bool any_point_selected(const CurvesGeometry &curves)
+{
+ return varray_contains_nonzero(curves.selection_point_float());
+}
+
+static bool any_point_selected(const Span<Curves *> curves_ids)
+{
+ for (const Curves *curves_id : curves_ids) {
+ if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace select_all {
+
+static void invert_selection(MutableSpan<float> selection)
+{
+ threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ selection[i] = 1.0f - selection[i];
+ }
+ });
+}
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+ int action = RNA_enum_get(op->ptr, "action");
+
+ VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
+
+ if (action == SEL_TOGGLE) {
+ action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
+ }
+
+ for (Curves *curves_id : unique_curves) {
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
+ if (action == SEL_SELECT) {
+ /* As an optimization, just remove the selection attributes when everything is selected. */
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+ attributes.remove(".selection_point_float");
+ attributes.remove(".selection_curve_float");
+ }
+ else {
+ MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
+ curves.selection_point_float_for_write() :
+ curves.selection_curve_float_for_write();
+ if (action == SEL_DESELECT) {
+ selection.fill(0.0f);
+ }
+ else if (action == SEL_INVERT) {
+ invert_selection(selection);
+ }
+ }
+
+ /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
+ * attribute for now. */
+ DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace select_all
+
+static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot)
+{
+ ot->name = "(De)select All";
+ ot->idname = __func__;
+ ot->description = "(De)select all control points";
+
+ ot->exec = select_all::select_all_exec;
+ ot->poll = selection_operator_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+namespace surface_set {
+
+static bool surface_set_poll(bContext *C)
+{
+ const Object *object = CTX_data_active_object(C);
+ if (object == nullptr) {
+ return false;
+ }
+ if (object->type != OB_MESH) {
+ return false;
+ }
+ return true;
+}
+
+static int surface_set_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+
+ Object &new_surface_ob = *CTX_data_active_object(C);
+
+ Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
+ const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.ldata,
+ CD_MLOOPUV);
+
+ CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
+ if (selected_ob->type != OB_CURVES) {
+ continue;
+ }
+ Object &curves_ob = *selected_ob;
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+
+ MEM_SAFE_FREE(curves_id.surface_uv_map);
+ if (new_uv_map_name != nullptr) {
+ curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
+ }
+
+ bool missing_uvs;
+ bool invalid_uvs;
+ snap_curves_to_surface::snap_curves_to_surface_exec_object(
+ curves_ob,
+ new_surface_ob,
+ snap_curves_to_surface::AttachMode::Nearest,
+ &invalid_uvs,
+ &missing_uvs);
+
+ /* Add deformation modifier if necessary. */
+ blender::ed::curves::ensure_surface_deformation_node_exists(*C, curves_ob);
+
+ curves_id.surface = &new_surface_ob;
+ ED_object_parent_set(
+ op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr);
+
+ DEG_id_tag_update(&curves_ob.id, ID_RECALC_TRANSFORM);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
+
+ /* Required for deformation. */
+ new_surface_ob.modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION;
+ DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
+ }
+ CTX_DATA_END;
+
+ DEG_relations_tag_update(bmain);
+
+ return OPERATOR_FINISHED;
+}
+
+} // namespace surface_set
+
+static void CURVES_OT_surface_set(wmOperatorType *ot)
+{
+ ot->name = "Set Curves Surface Object";
+ ot->idname = __func__;
+ ot->description =
+ "Use the active object as surface for selected curves objects and set it as the parent";
+
+ ot->exec = surface_set::surface_set_exec;
+ ot->poll = surface_set::surface_set_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
} // namespace blender::ed::curves
void ED_operatortypes_curves()
@@ -653,4 +1030,8 @@ void ED_operatortypes_curves()
WM_operatortype_append(CURVES_OT_convert_to_particle_system);
WM_operatortype_append(CURVES_OT_convert_from_particle_system);
WM_operatortype_append(CURVES_OT_snap_curves_to_surface);
+ WM_operatortype_append(CURVES_OT_set_selection_domain);
+ WM_operatortype_append(SCULPT_CURVES_OT_select_all);
+ WM_operatortype_append(CURVES_OT_disable_selection);
+ WM_operatortype_append(CURVES_OT_surface_set);
}